Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
461 views
in Technique[技术] by (71.8m points)

ios - 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 (e.g., because we're talking about something's frame or bounds).

CGFloat vs. Double

Consider the following innocent-looking code from a UIViewController subclass:

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).

The example is artificially brief, so there's an artificially simple solution, which is to cast scale to a Float from the get-go. 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.

Verbose Initializer

Another irritation is what happens when the time comes to create a new 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:

Missing argument labels 'x:y:width:height:' in call

So we end up falling back on CGRectMake, which is no improvement on Objective-C. And sometimes CGRectMake and CGSizeMake are no improvement. 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'

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. 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. Which makes sense, since Float would not be a match for CGFloat on a 64-bit device. 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. (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.)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Explicitly typing scale to CGFloat, as you have discovered, is indeed the way handle the typing issue in 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:

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. This is exactly how Objective-C works, but is problematic in Swift because Swift doesn't allow implicit conversions.

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. This is inelegant but effective :-)

Update In Xcode 6 Beta 5:

A CGFloat can be constructed from any Integer type (including the sized integer types) and vice-versa. (17670817)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...