当我需要与Cocoa Touch通信时,尤其是涉及到CGRect和CGPoint时(例如,因为我们正在谈论某个物体的frame或bounds),我发现Swift数值计算特别笨拙。
CGFloat vs. Double
考虑以下来自UIViewController子类的看似无害的代码:
let scale = 2.0
let r = self.view.bounds
var r2 = CGRect()
r2.size.width = r.size.width * scale
这段代码无法编译,最后一行出现了通常的神秘错误:
找不到接受提供的参数的“*”的重载项
正如你现在已经知道的那样,这个错误表明类型之间存在某种阻抗不匹配。 r.size.width
是 CGFloat 类型,它可以自动地与 Swift Float 交换,但无法与 Swift Double 变量互操作(默认情况下,这就是 scale
的类型)。
这个示例人为地简化了问题,因此有一个人为简单的解决方案,即从一开始将 scale
转换为 Float 类型。但当涉及计算建议的 CGRect 的元素时,需要从各个位置提取许多变量,就需要进行大量的强制转换。
详细初始化器
另一个问题是创建新的 CGRect 时会发生什么。尽管有文档,但没有值但没有标签的初始化器。由于我们使用的是 Doubles,这无法编译:
let d = 2.0
var r3 = CGRect(d, d, d, d)
但是,即使我们将d
转换为浮点数,编译器仍然无法编译:
在调用中缺少参数标签“x:y:width:height:”
因此,我们最终退回到使用CGRectMake
,这并没有改善Objective-C的情况。有时,CGRectMake
和CGSizeMake
也无法改善情况。考虑一下我某个应用程序中的实际代码:
let kSEP : Float = 2.0
let intercellSpacing = CGSizeMake(kSEP, kSEP);
在我的项目中,有一个是正常的。但在另一个项目中,完全相同的代码却神秘地失败了,并显示以下错误:
'NSNumber'不是'CGFloat'的子类型。
好像有时候Swift会尝试通过将Float转换为NSNumber来"跨越桥梁",而当桥梁另一侧期望的是CGFloat时,这显然是错误的。我还没有弄清楚两个项目之间造成错误出现和消失的区别(也许其他人已经知道了)。
注意:我可能已经找到了问题所在:它似乎取决于Build Active Architecture Only构建设置,这反过来又表明这是一个64位的问题。这很有道理,因为在64位设备上,Float不会匹配CGFloat。这意味着阻抗不匹配问题比我想象的要严重得多。
结论
我正在寻找有关此主题的实用智慧。我认为有人可能已经设计出了一些CGRect和CGPoint扩展,这将使生活变得更加轻松。(或者可能有人编写了大量额外的算术运算符函数重载,以便将CGFloat与Int或Double结合使用,如果可能的话,就可以"自动"进行转换。)