【问题标题】:Subclassing CALayer + Implicit Animation w/ Swift3使用 Swift3 子类化 CALayer + 隐式动画
【发布时间】:2016-11-24 23:36:09
【问题描述】:

我正在尝试通过覆盖 CALayer 来实现“隐式动画”。我已经阅读了文档,发现它相当复杂。此外,我找不到任何像样的代码示例,也找不到很好地解释它或使用任何 Swift 的互联网帖子。

我已经通过子类BandBaseCG 实现,它试图为属性param1 提供隐式动画,并覆盖needsDisplay(forKey:),它在我的属性中调用一次,我返回true。

但这就是问题所在。我的理解是我必须返回一个符合CAAction 协议的对象,比如CABasicAnimation,这就是我所做的。但是,action(forKey:)defaultAction(forKey:) 不会被 param1 键调用。

所以我知道在某些时候我可以在draw(in ctx:) 中提供Core-Graphics 绘图代码来实现实际的隐式动画以响应一些layer.param1 = 2。但我无法做到这一点,因为 API 根本没有按我的预期工作。

这是我当前的实现:

import Foundation
import QuartzCore

class BandBaseCG: CALayer {

   @NSManaged var param1: Float

   override init(layer: Any) {

      super.init(layer: layer)

   }

   override init() {
      super.init()

   }

   required init?(coder aDecoder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
   }


   override func action(forKey event: String) -> CAAction? {

      print(event)

      if event == "param1" {

         let ba = CABasicAnimation(keyPath: event)
         ba.duration = 2
         ba.fromValue = (presentation()! as BandBaseCG).param1
         ba.toValue = 2

         return ba
      }

      return super.action(forKey: event)
   }


   override func draw(in ctx: CGContext) {
      print("draw")
   }


   override class func defaultAction(forKey event: String) -> CAAction?     {

      if event == "param1" {

         let ba = CABasicAnimation(keyPath: event)
         ba.duration = 2
         ba.fromValue = 0
         ba.toValue = 2

         return ba
      }

      return super.defaultAction(forKey: event)
   }

   override class func needsDisplay(forKey key: String) -> Bool {

      print(key)

      if key == "param1" {
         return true
      }

      return super.needsDisplay(forKey: key)
   }

   override func display() {
      print("param value: \(param1)")
   }

}

【问题讨论】:

    标签: swift core-animation calayer


    【解决方案1】:

    这是一个完整的工作示例(来自我的书中):

    class MyView : UIView { // exists purely to host MyLayer
        override class var layerClass : AnyClass {
            return MyLayer.self
        }
        override func draw(_ rect: CGRect) {} // so that layer will draw itself
    }
    class MyLayer : CALayer {
        @NSManaged var thickness : CGFloat
        override class func needsDisplay(forKey key: String) -> Bool {
            if key == #keyPath(thickness) {
                return true
            }
            return super.needsDisplay(forKey:key)
        }
        override func draw(in con: CGContext) {
            let r = self.bounds.insetBy(dx:20, dy:20)
            con.setFillColor(UIColor.red.cgColor)
            con.fill(r)
            con.setLineWidth(self.thickness)
            con.stroke(r)
        }
        override func action(forKey key: String) -> CAAction? {
            if key == #keyPath(thickness) {
                let ba = CABasicAnimation(keyPath: key)
                ba.fromValue = self.presentation()!.value(forKey:key)
                return ba
            }
            return super.action(forKey:key)
        }
    }
    

    你会发现,如果你在你的app里放了一个MyView,你可以说

    let lay = v.layer as! MyLayer
    lay.thickness = 10 // or whatever
    

    并且它将动画边框厚度的变化。

    【讨论】:

    • “此外,我找不到任何像样的代码示例,也找不到很好地解释它或使用任何 Swift 的互联网帖子。”好的,我认为这是一个“体面”的代码示例。至于解释,你显然没有读过我的书,这不是你的错。但它确实存在,我认为它很好地解释了该代码的所有部分(当然只是我的看法)。
    • 我认为这不公平。我花了几个小时在互联网上搜索,发现关于这个问题的信息很少。至于你的书,我很高兴知道它,将来一定会查阅它。感谢您的回复。
    • 放松。我这不是你的错!无害无犯。我已经给了你代码,你会发现它可以工作(后面有一个可下载的完整项目)。我们在这里是同一个团队。
    • 对不起,我没听懂“语气”。你的权利,这是一个很好的例子,我很感激。具体来说,似乎#keypath(我在阅读的任何教程中都没有提到,并且是一个有点晦涩的语言功能)是我需要的关键功能差异,是吗?
    • @C0D3 你必须想想 Cocoa / Objective-C 在幕后做了什么。 Swift 只是一种与之交谈的方式。所以,Swift @NSManaged 是 Objective-C dynamic,这意味着 setter/getter 将在运行时合成,这就是我们在这里需要的,就像在 Core Data 中一样。请参阅apeth.com/iOSBook/ch17.html#_making_a_property_animatable,了解在此处“翻译”为 Swift 的底层 Objective-C 机制。 (Swift dynamic 完全是另外一回事。)
    猜你喜欢
    • 2014-11-07
    • 2015-12-10
    • 1970-01-01
    • 2011-08-15
    • 2016-08-10
    • 2011-01-15
    • 2011-03-16
    • 2016-05-16
    • 2014-04-19
    相关资源
    最近更新 更多