ios Swift 数字和 CGFloat(CGPoint、CGRect 等)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24108827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:40:42  来源:igfitidea点击:

Swift numerics and CGFloat (CGPoint, CGRect, etc.)

iosswift

提问by matt

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

我发现 Swift 数字特别笨拙,正如在现实生活中经常发生的那样,我必须就 CGRect 和 CGPoint 与 Cocoa Touch 进​​行通信(例如,因为我们正在谈论某事的framebounds)。

CGFloat vs. Double

CGFloat 与 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.widtharrives as a CGFloat, which will interchange automatically with a Swift Float but cannot interoperate with a Swift Double variable (which, by default, is what scaleis).

这个错误,我相信你现在已经知道了,表明类型之间存在某种阻抗不匹配。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 scaleto 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.

该示例人为地简短,因此有一个人为地简单的解决方案,即scale从一开始就强制转换为 Float。但是,当从各地抽取的许多变量都涉及到提议的 CGRect 元素的计算时,需要进行大量的转换。

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:

另一个令人恼火的是当需要创建一个新的 CGRect 时会发生什么。尽管有文档,但没有带值但不带标签的初始化程序。这无法编译,因为我们有双打:

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

But even if we cast dto 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. And sometimes CGRectMake and CGSizeMake are no improvement. Consider this actual code from one of my apps:

所以我们最终回到了CGRectMake,这对 Objective-C 没有任何改进。有时 CGRectMake 和 CGSizeMake 也没有改进。考虑来自我的一个应用程序的实际代码:

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

就好像,有时,Swift 试图通过将 Float 转换为 NSNumber 来“过桥”,当桥的另一侧期望 CGFloat 时,这当然是错误的做法。我还没有弄清楚两个项目之间的区别是什么导致错误出现在一个而不是另一个(也许其他人有)。

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.

注意:我可能已经发现了这个问题:它似乎取决于 Build Active Architecture Only 构建设置,这反过来表明这是一个 64 位问题。这是有道理的,因为 Float 与 64 位设备上的 CGFloat 不匹配。这意味着阻抗失配问题比我想象的还要糟糕。

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

我正在寻找有关此主题的实用智慧之词。我想有人可能已经设计了一些 CGRect 和 CGPoint 扩展,这将使生活更轻松。(或者可能有人写了一大堆额外的算术运算符函数重载,这样将 CGFloat 与 Int 或 Double 结合起来“就行了”——如果可能的话。)

采纳答案by Seivan

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. https://github.com/seivan/ScalarArithmetic#sample

从 Beta 5 开始,这里列出了您目前无法使用 vanilla Swift 执行的操作。 https://github.com/seivan/ScalarArithmetic#sample

I suggest running the test suite with and without ScalarArithmetic just to see what's going on.

我建议使用和不使用 ScalarArithmetic 运行测试套件,看看发生了什么。

回答by BergQuester

Explicitly typing scaleto CGFloat, as you have discovered, is indeed the way handle the typing issue in swift. For reference for others:

正如您所发现的,显式输入scaletoCGFloat确实是快速处理输入问题的方法。供他人参考:

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

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

我们意识到这个问题并认为它很严重:我们现在正在评估几种不同的解决方案,并将在以后的测试版中推出一个。正如您所注意到的,今天您可以通过强制转换为 Double 来解决这个问题。这是不优雅但有效的:-)

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

CGFloat 可以从任何整数类型(包括大小的整数类型)构造,反之亦然。(17670817)

回答by Andrew97p

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)除非只有一个参数,否则您不能将标签留下。