Swift数字和CGFloat(CGPoint、CGRect等)



当我不得不与Cocoa Touch就CGRect和CGPoint进行交流时(例如,因为我们谈论的是某个事物的framebounds(,我发现Swift数字特别笨拙,这在现实生活中经常发生。

CGFloat与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强制转换为Float,我们也不会编译:

调用中缺少参数标签"x:y:width:height:">

因此,我们最终回到了CGRectMake,这对Objective-C没有任何改进。有时CGRectMake和CGSizeMake并没有改善。考虑一下我的一个应用程序中的实际代码:

let kSEP : Float = 2.0
let intercellSpacing = CGSizeMake(kSEP, kSEP);

在我的一个项目中,这是有效的。在另一个例子中,它神秘地失败了——完全相同的代码!——出现此错误:

"NSNumber"不是"CGFloat"的子类型

有时,Swift似乎试图通过向NSNumber铸造Float来"过桥",当然,当桥的另一边期望CGFloat时,这样做是错误的。我还没有弄清楚这两个项目之间的区别是什么,导致错误出现在一个项目中,而不是另一个项目(也许其他人出现了(。

注意:我可能已经解决了这个问题:它似乎取决于"仅构建活动体系结构"的构建设置,这反过来表明它是一个64位问题。这是有道理的,因为Float在64位设备上与CGFloat不匹配。这意味着阻抗失配问题比我想象的还要严重。

结论

我正在寻找关于这个话题的实用的智慧之词。我想可能有人设计了一些CGRect和CGPoint扩展,这会让生活变得更轻松。(或者可能有人写了一大堆额外的算术运算符函数重载,比如将CGFloat与Int或Double组合"刚好有效"——如果可能的话。(

正如您所发现的,显式键入scaleCGFloat确实是处理swift中键入问题的方法。供他人参考:

let scale: CGFloat = 2.0
let r = self.view.bounds
var r2 = CGRect()
r2.size.width = r.width * scale

不知道如何回答你的第二个问题,你可能想用不同的标题单独发布。

更新:

Swift创始人兼首席开发者Chris Lattner在2014年7月4日的苹果开发者论坛上就这个问题发表了这样的言论:

这里发生的事情是CGFloat是任一Float的类型别名或Double,具体取决于您是为32位还是64位构建。这正是Objective-C的工作方式,但在Swift中存在问题因为Swift不允许隐式转换。

我们知道这个问题很严重:我们正在评估几个现在有不同的解决方案,并将在稍后的测试版中推出。正如您所注意到的,您今天可以通过选择Double来应对这种情况。这是不雅但有效的:-(

在Xcode 6 Beta 5中更新:

CGFloat可以由任何Integer类型构造(包括大小为整数类型(,反之亦然。(17670817(

我写了一个库,用于处理运算符重载,以允许Int、CGFloat和Double之间的交互。

https://github.com/seivan/ScalarArithmetic

从Beta 5开始,这里列出了一些你目前不能用香草Swift做的事情。https://github.com/seivan/ScalarArithmetic#sample

我建议运行带有和不带有ScalarAthmetic的测试套件,看看发生了什么。

我为Double和Int创建了一个扩展,为它们添加了一个计算的CGFloatValue属性。

extension Double {
    var CGFloatValue: CGFloat {
        get {
            return CGFloat(self)
        }
    }
}
extension Int {
    var CGFloatValue: CGFloat {
        get {
            return CGFloat(self)
        }
    }
}

您可以使用let someCGFloat = someDoubleOrInt.CGFloatValue 访问它

此外,对于CGRect Initializer,您会得到缺少参数标签的错误,因为您遗漏了标签,您需要CGRect(x: d, y: d, width: d, height: d)。除非只有一个参数,否则您不能遗漏标签。

最新更新