我发现Swift数字尤其笨拙,正如在现实生活中经常发生的那样,我必须就CGRect和CGPoint与Cocoa Touch进行沟通(例如,因为我们正在谈论某事frame
或bounds
)。
从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时会发生什么。尽管有文档,但没有带有值但没有标签的初始值设定项。这无法编译,因为我们有双打:
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试图通过将Float转换为NSNumber来“过桥”,当桥的另一侧需要CGFloat时,这当然是错误的。我还没有弄清楚导致错误出现在另一个项目中的两个项目之间的区别是什么(可能是其他人有的)。
注意:我可能已经发现了这个问题:它似乎依赖于Build Active Architecture Only构建设置,这反过来表明它是一个64位问题。这是有道理的,因为Float不会与64位设备上的CGFloat匹配。这意味着阻抗不匹配问题甚至比我想象的还要糟糕。
我正在寻找关于这个主题的实用的智慧话语。我想有人可能已经设计了一些CGRect和CGPoint扩展,这将使生活变得更加容易。 (或者可能有人写了一大堆额外的算术运算符函数重载,这样将CGFloat与Int或Double结合起来“就行了” - 如果可能的话。)
答案 0 :(得分:24)
正如您所发现的那样,明确地将scale
键入CGFloat
确实是处理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日Apple Developer Forum就此问题发表了这样的话:
这里发生的是CGFloat是Float的一个类型 或双倍取决于您是否为32位或64位构建。 这正是Objective-C的工作方式,但在Swift中存在问题 因为Swift不允许隐式转换。
我们知道 这个问题并认为是严肃的:我们正在评估几个问题 现在有不同的解决方案,并将在稍后的测试中推出一个。 正如您所注意到的,您可以通过强制转换为Double来应对此问题。 这是不优雅但有效的: - )
在Xcode 6 Beta 5中更新:
CGFloat可以从任何Integer类型构造(包括 大小整数类型),反之亦然。 (17670817)
答案 1 :(得分:15)
我写了一个处理运算符重载的库,允许Int,CGFloat和Double之间的交互。
https://github.com/seivan/ScalarArithmetic
从Beta 5开始,这里列出了目前你无法用vanilla Swift做的事情。 https://github.com/seivan/ScalarArithmetic#sample
我建议运行带有和不带ScalarArithmetic的测试套件,看看是怎么回事。
答案 2 :(得分:6)
我为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初始化程序,你得到了缺少的参数标签错误,因为你没有关闭标签,你需要CGRect(x: d, y: d, width: d, height: d)
你不能留下标签,除非只有一个参数。