为 CAAnimation 目的将 Swift 结构转换/包装为 NSValue?

Posted

技术标签:

【中文标题】为 CAAnimation 目的将 Swift 结构转换/包装为 NSValue?【英文标题】:Convert/Wrap Swift struct as NSValue for CAAnimation purposes? 【发布时间】:2015-10-01 17:29:46 【问题描述】:

我有一个自定义的CALayer 子类,它可以绘制圆弧。它看起来像:

class ArcLayer: CALayer 
    var strokeColor:UIColor = UIColor.blackColor()  didSet  self.setNeedsDisplay() 
    var strokeWidth:CGFloat = 1.0  didSet  self.setNeedsDisplay() 
    var strokeCap:CGLineCap = .Butt  didSet  self.setNeedsDisplay() 
    var startRadians:CGFloat = 0.0  didSet  self.setNeedsDisplay() 
    var sweepRadians:CGFloat = Tau   didSet  self.setNeedsDisplay() 

    // other init's omitted for brevity 

    override init(layer: AnyObject) 
        super.init(layer: layer)
        if let layer = layer as? ArcLayer 
            self.strokeColor = layer.strokeColor
            self.strokeWidth = layer.strokeWidth
            self.strokeCap = layer.strokeCap
            self.startRadians = layer.startRadians
            self.sweepRadians = layer.sweepRadians
        
    

    override func drawInContext(ctx: CGContext) 
        ...
    

    override class func needsDisplayForKey(key: String) -> Bool 
        return key == "startRadians" || key == "sweepRadians" || super.needsDisplayForKey(key)
    

我正在使用自定义子类,因为虽然CAShapeLayerpath 是可动画的,但它不会以您期望的弧旋转的方式进行动画。以上工作。开始/扫描的动画会产生所需的效果。

我想改变它。我有一个 Angle 结构(参见 this answer),我宁愿使用它而不是原始弧度 CGFloat 值。将这两个变量的类型更改为 Angle 可以正常编译。并且原始显示也有效(在从Angle vars 进行适当的转换/提取之后)。问题是现在能够为这些变量设置动画。和以前一样,我可能会写一些代码,例如:

let sweep = CABasicAnimation(keyPath: "sweepRadians")
sweep.fromValue = 0.0 // raw radians value
sweep.toValue = Tau // raw radians value for one rotation
sweep.duration = 10.seconds
sweep.repeatCount = 0
self.blueRing.addAnimation(sweep, forKey: "sweepRadians")

我不能只将这些更改为 Angle 值:

sweep.fromValue = Angle(raw: 0, unit: .Rotations)
sweep.toValue = Angle(raw: 0, unit: .Rotations)

这会导致

cannot assign value of type 'Angle' to type of 'AnyObject?'

我希望解决方案是我需要以某种方式将我的Angle 值转换/包装为 NSValue 对象?这真的正确吗?可以做到吗?怎么样?

我真的很喜欢 Swift,但它与 Objc 相交的极端情况可能真的很令人困惑。

【问题讨论】:

【参考方案1】:

Angle 是一个结构体,不符合AnyObject。为了符合AnyObject,需要改成一个类,或者像Box这样包装成一个类:

final class Box<T> 
    let value: T
    init(_ value: T)  self.value = value 

也就是说,这不太可能奏效。 CABasicAnimation对interpolate specific things有专门的了解:

整数和双精度数 CGRect、CGPoint、CGSize 和 CGAffineTransform 结构 CATransform3D 数据结构 CGColor 和 CGI​​mage 参考

我不相信有任何方法可以添加到此列表中。我不相信您可以使用 CABasicAnimation 来插入任意类型。你需要从一个数字动画到一个数字,所以这基本上就是你上面的代码。

当然,您可以使 sweepRadians 成为仅转发到 sweep 的计算属性,并且您可以从 Degrees(0).rawRadians 动画到 Degrees(360).rawRadians。这样您就可以获得大部分您想要的实际收益;只是不是没有在某个时候手动转换为数字。

现在,如果这是 Mac,您可以继承 NSAnimation 并构建自己的动画师来执行此操作。请参阅CABasicAnimation and custom types 进行讨论。但是ios上没有NSAnimation,我不相信你可以用同样的方式继承CAAnimationCAAnimation 上没有“进度”委托方法。

【讨论】:

【参考方案2】:

我认为你应该尝试使用init(pointer pointer: UnsafePointer&lt;Void&gt;)of NSValue。您可以使用func withUnsafePointer&lt;T, Result&gt;(inout arg: T, @noescape _ body: UnsafePointer&lt;T&gt; throws -&gt; Result) rethrows -&gt; Result 获取指向您的Angle 实例的指针(参见http://swiftdoc.org/v2.0/func/withUnsafePointer/ 以供参考)。 附:将 NSValue 包装的东西传递给 Core Animation 的想法通常是一种可行的技术。不过,目前还不清楚 CA 是否会解开 Angle。祝你好运。

【讨论】:

以上是关于为 CAAnimation 目的将 Swift 结构转换/包装为 NSValue?的主要内容,如果未能解决你的问题,请参考以下文章

CAAnimation - 显示/隐藏持续时间为 0

CAAnimation 在 CALayer 上没有按预期工作

CAAnimation 正在覆盖以前的 CAAnimation 动画

CAAnimation 在一个点上进行变换旋转

是否可以控制 CAAnimation 的时间线?

应用 CAAnimation 时,AQGridView 单元格被交换