Swift需要奇怪的铸造

时间:2014-06-08 18:42:31

标签: swift

我正在观看WWDC2014上的一些视频并试图编写我喜欢的代码,但我注意到的一个奇怪的事情是Swift一直对我生气并希望我投射到不同的数字类型。这很容易,但在WWDC的视频中他们不需要这样做。以下是“界面生成器的新功能”中的示例:

-M_PI / 2一直给我错误:“无法找到接受提供的参数的'/'重载'

有没有人有这个问题的解决方案,这不仅仅涉及投射,因为有明显的另一种方法吗?对于类似问题,我还有很多例子。

if !ringLayer {
            ringLayer = CAShapeLayer()

            let innerRect = CGRectInset(bounds, lineWidth / 2.0, lineWidth / 2.0)
            let innerPath = UIBezierPath(ovalInRect: innerRect)

            ringLayer.path = innerPath.CGPath
            ringLayer.fillColor = nil
            ringLayer.lineWidth = lineWidth
            ringLayer.strokeColor = UIColor.blueColor().CGColor
            ringLayer.anchorPoint = CGPointMake(0.5, 0.5)
            ringLayer.transform = CATransform3DRotate
                 (ringLayer.transform, -M_PI/2, 0, 0, 1)

            layer.addSublayer(ringLayer)
        }
        ringLayer.frame = layer.bounds

3 个答案:

答案 0 :(得分:6)

编辑:NB:CGFloat在beta 4中发生了变化,特别是为了更容易处理这个32/64位差异。阅读发行说明,现在不要将下面作为福音:它是为beta 2编写的。

来自this answer的线索我已经解决了:它取决于所选的项目架构。如果我将Project架构保留为默认值(armv7,arm64),那么我会得到与此代码相同的错误:

 // Error with arm7 target:
 ringLayer.transform = CATransform3DRotate(ringLayer.transform, -M_PI/2, 0, 0, 1)

...并且需要施放到Float(好吧,下面的CGFloat,我敢肯定)让它工作:

 // Works with explicit cast on arm7 target
 ringLayer.transform = CATransform3DRotate(ringLayer.transform, Float(-M_PI/2), 0, 0, 1)

但是,如果我仅将目标体系结构更改为arm64,则代码​​的工作方式与视频中的Apple示例相同:

 // Works fine with arm64 target:
 ringLayer.transform = CATransform3DRotate(ringLayer.transform, -M_PI/2, 0, 0, 1)

所以回答你的问题,我相信这是因为CGFloat被定义为64位架构的双倍,所以可以使用M_PI(也是一个双倍)来源的值作为CGFloat参数。但是,当arm7是目标时,CGFloat是一个浮点数,而不是一个双精度数,因此当将M_PI(仍为双精度)表达式直接作为CGFloat参数传递时,您将失去精度。

请注意,Xcode默认只为Debug版本的“主动”架构构建 - 我发现可以通过在菜单栏的标准下拉菜单中切换iPhone 4S和iPhone 5S方案来切换此错误。 Xcode,因为它们有不同的架构。我想在演示视频中,选择了64位架构目标,但在您的项目中,您选择了32位架构?

鉴于CGFloat在64位体系结构上是双精度的,处理这个特定问题的最简单方法是始终转换为CGFloat。

但是,当您需要在不同的体系结构上执行不同的操作时,作为处理此类问题的演示,Swift确实支持conditional compilation

    #if arch(x86_64) || arch(arm64)
        ringLayer.transform = CATransform3DRotate (ringLayer.transform, -M_PI / 2, 0, 0, 1)
    #else
        ringLayer.transform = CATransform3DRotate (ringLayer.transform, CGFloat(-M_PI / 2), 0, 0, 1)
    #endif

然而,这只是一个例子。你真的不希望在任何地方做这种事情,所以我一定会坚持使用CGFloat(<whatever POSIX double value you need>)来获得32位或64位的值取决于目标架构。

Apple在以后的编译器版本中为处理不同的浮点数添加了更多帮助 - 例如,在早期的测试版中,你甚至无法轻易地获取单精度浮点数的floor(),而现在(当前Xcode 6.1) )对于float和double都有floor()ceil()等的覆盖,所以你不需要摆弄条件编译。

答案 1 :(得分:0)

目前在Objective C数字类型和Swift类型之间的自动转换似乎存在问题。为此,我能够通过将lineWidth标记为Float类型来使其工作。我不知道他们为什么在视频中没有这个问题,我认为这是他们使用的不同版本。我缺少一个Objective C互操作设置,或者它只是一个测试版问题。

为了验证我使用的一些基本问题(甚至在Playground中发生):

var x:NSNumber = 1
var y:Integer = 2
var z:Int = 3
x += 5 //error
y += 6 //error
z = z + y //error

答案 2 :(得分:0)

对于Swift 1.2,您必须将第二个参数投射到CGFloat

此代码有效:

ringLayer.transform = CATransform3DRotate(ringLayer.transform, CGFloat(-M_PI/2), 0, 0, 1)