简体   繁体   English

Swift数字和CGFloat(CGPoint,CGRect等)

[英]Swift numerics and CGFloat (CGPoint, CGRect, etc.)

I'm finding Swift numerics particularly clumsy when, as so often happens in real life, I have to communicate with Cocoa Touch with regard to CGRect and CGPoint (eg, because we're talking about something's frame or bounds ). 我发现Swift数字尤其笨拙,正如在现实生活中经常发生的那样,我必须就CGRect和CGPoint与Cocoa Touch进行沟通(例如,因为我们谈论的是framebounds )。

CGFloat vs. Double CGFloat vs. Double

Consider the following innocent-looking code from a UIViewController subclass: 从UIViewController子类中考虑以下看似无辜的代码:

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

This code fails to compile, with the usual mysterious error on the last line: 此代码无法编译,最后一行通常有神秘错误:

Could not find an overload for '*' that accepts the supplied arguments 找不到接受提供的参数的'*'的重载

This error, as I'm sure you know by now, indicates some kind of impedance mismatch between types. 正如我现在所知道的那样,这个错误表明类型之间存在某种阻抗不匹配。 r.size.width arrives as a CGFloat, which will interchange automatically with a Swift Float but cannot interoperate with a Swift Double variable (which, by default, is what scale is). r.size.width作为CGFloat到达,它将与Swift Float自动交换,但不能与Swift Double变量互操作(默认情况下,它是scale )。

The example is artificially brief, so there's an artificially simple solution, which is to cast scale to a Float from the get-go. 这个例子是人为简短的,所以有一个人为简单的解决方案,即从一开始就将scale到Float。 But when many variables drawn from all over the place are involved in the calculation of a proposed CGRect's elements, there's a lot of casting to do. 但是当从所有地方抽取的许多变量都参与了CGRect元素的计算时,需要进行大量的投射。

Verbose Initializer 详细初始化器

Another irritation is what happens when the time comes to create a new CGRect. 另一个恼人的是当创建新的CGRect时会发生什么。 Despite the documentation, there's no initializer with values but without labels. 尽管有文档,但没有带有值但没有标签的初始值设定项。 This fails to compile because we've got Doubles: 这无法编译,因为我们有双打:

let d = 2.0
var r3 = CGRect(d, d, d, d)

But even if we cast d to a Float, we don't compile: 但即使我们将d转换为Float,我们也不会编译:

Missing argument labels 'x:y:width:height:' in call 在调用中缺少参数标签'x:y:width:height:'

So we end up falling back on CGRectMake , which is no improvement on Objective-C. 所以我们最终会回到CGRectMake ,这对Objective-C没有任何改进。 And sometimes CGRectMake and CGSizeMake are no improvement. 有时CGRectMake和CGSizeMake没有任何改进。 Consider this actual code from one of my apps: 请考虑我的某个应用中的实际代码:

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

In one of my projects, that works. 在我的一个项目中,这是有效的。 In another, it mysteriously fails — the exact same code! 在另一个,它神秘失败 - 完全相同的代码! — with this error: - 出现此错误:

'NSNumber' is not a subtype of 'CGFloat' 'NSNumber'不是'CGFloat'的子类型

It's as if, sometimes, Swift tries to "cross the bridge" by casting a Float to an NSNumber, which of course is the wrong thing to do when what's on the other side of the bridge expects a CGFloat. 有时,Swift试图通过将Float转换为NSNumber来“过桥”,当桥的另一侧需要CGFloat时,这当然是错误的。 I have not yet figured out what the difference is between the two projects that causes the error to appear in one but not the other (perhaps someone else has). 我还没有弄清楚导致错误出现在一个而不是另一个(可能是其他人有)的两个项目之间的区别。

NOTE: I may have figured out that problem: it seems to depend on the Build Active Architecture Only build setting, which in turn suggests that it's a 64-bit issue. 注意:我可能已经发现了这个问题:它似乎依赖于Build Active Architecture Only构建设置,这反过来表明它是一个64位问题。 Which makes sense, since Float would not be a match for CGFloat on a 64-bit device. 这是有道理的,因为Float不会与64位设备上的CGFloat匹配。 That means that the impedance mismatch problem is even worse than I thought. 这意味着阻抗不匹配问题甚至比我想象的还要糟糕。

Conclusion 结论

I'm looking for practical words of wisdom on this topic. 我正在寻找关于这个主题的实用智慧词。 I'm thinking someone may have devised some CGRect and CGPoint extension that will make life a lot easier. 我想有人可能已经设计了一些CGRect和CGPoint扩展,这将使生活变得更加容易。 (Or possibly someone has written a boatload of additional arithmetic operator function overloads, such that combining CGFloat with Int or Double "just works" — if that's possible.) (或者可能有人写了一大堆额外的算术运算符函数重载,这样将CGFloat与Int或Double结合起来“只是工作” - 如果可能的话。)

Explicitly typing scale to CGFloat , as you have discovered, is indeed the way handle the typing issue in swift. 正如您所发现的那样,明确scaleCGFloat键入scale确实是处理swift中的键入问题的方法。 For reference for others: 供他人参考:

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

Not sure how to answer your second question, you may want to post it separately with a different title. 不确定如何回答您的第二个问题,您可能希望使用不同的标题单独发布。

Update: 更新:

Swift creator and lead developer Chris Lattner had this to say on this issue on the Apple Developer Forum on July 4th, 2014: Swift创建者和首席开发人员Chris Lattner在2014年7月4日的Apple开发者论坛上就此问题发表了这样的话:

What is happening here is that CGFloat is a typealias for either Float or Double depending on whether you're building for 32 or 64-bits. 这里发生的是CGFloat是Float或Double的类型,具体取决于你是构建32位还是64位。 This is exactly how Objective-C works, but is problematic in Swift because Swift doesn't allow implicit conversions. 这正是Objective-C的工作方式,但在Swift中存在问题,因为Swift不允许隐式转换。

We're aware of this problem and consider it to be serious: we are evaluating several different solutions right now and will roll one out in a later beta. 我们已经意识到这个问题并认为它是严肃的:我们现在正在评估几种不同的解决方案,并将在稍后的测试版中推出一个。 As you notice, you can cope with this today by casting to Double. 正如您所注意到的,您可以通过强制转换为Double来应对此问题。 This is inelegant but effective :-) 这是不优雅但有效的:-)

Update In Xcode 6 Beta 5: 在Xcode 6 Beta 5中更新:

A CGFloat can be constructed from any Integer type (including the sized integer types) and vice-versa. CGFloat可以从任何Integer类型(包括大小的整数类型)构造,反之亦然。 (17670817) (17670817)

I wrote a library that handles operator overloading to allow interaction between Int, CGFloat and Double. 我写了一个处理运算符重载的库,允许Int,CGFloat和Double之间的交互。

https://github.com/seivan/ScalarArithmetic https://github.com/seivan/ScalarArithmetic

As of Beta 5, here's a list of things that you currently can't do with vanilla Swift. 从Beta 5开始,这里列出了目前你无法用vanilla Swift做的事情。 https://github.com/seivan/ScalarArithmetic#sample https://github.com/seivan/ScalarArithmetic#sample

I suggest running the test suite with and without ScalarArithmetic just to see what's going on. 我建议运行带有和不带ScalarArithmetic的测试套件,看看是怎么回事。

I created an extension for Double and Int that adds a computed CGFloatValue property to them. 我为Double和Int创建了一个扩展,它为它们添加了一个计算的CGFloatValue属性。

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

You would access it by using let someCGFloat = someDoubleOrInt.CGFloatValue 您可以使用let someCGFloat = someDoubleOrInt.CGFloatValue来访问它

Also, as for your CGRect Initializer, you get the missing argument labels error because you have left off the labels, you need CGRect(x: d, y: d, width: d, height: d) you can't leave the labels out unless there is only one argument. 另外,对于你的CGRect初始化程序,你得到了缺少的参数标签错误,因为你没有关闭标签,你需要CGRect(x: d, y: d, width: d, height: d)你不能留下标签除非只有一个论点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM