为什么不早点调用自定义CALayer的draw方法呢?

Why isn't the draw method of the custom CALayer called earlier?

问题

我使用计时器在选择的 UIView 上触发随机动画。所有动画都按预期工作,但在定时器选择器退出后调用 CALayer 的绘制方法。

详细说明

为了清楚和简化,让我对执行的操作进行图解。

到目前为止我创建的所有动画都按预期工作:它们是现有 subviews/sublayers 上的 CABasicAnimations 的组合或添加到所选视图层次结构的新动画。它们被编码为 UIView 扩展,因此可以在任何视图上调用它们,而不管视图所在的视图或视图控制器如何。好吧,除了一个之外,所有工作都有效。

我确实创建了一个自定义 CALayer class,它包含在 CALayer 上绘制图案。在这个习惯的扩展 class 中,有一种方法可以使这些模式动画化(请参阅后面的代码)。所以总而言之,当我到达 step/method animate selected view 和 运行 这个特定的动画时,这是 应该发生的 :

问题:如果加上其他所有的动画,所有的绘制都是在animate selected viewstep/method退出之前执行的,特别是case,自定义CALayer class draw方法在performAnimation方法退出后被调用,进而导致动画崩溃

我应该补充一点,我已经在一个单独的和简化的操场上尝试了自定义 CALayer class 动画并且效果很好(我将自定义层添加到 UIViewController 的 UIView viewDidLoad 方法,然后我在 UIViewController 的 viewDidAppear 方法中调用图层动画。)

代码

(注意这个方法在UIView扩展中,所以这里的self代表调用了动画的UIView)

目前尝试次数

我试图通过在代码的不同位置插入 setNeedsDisplay / setNeedsLayout 来强制绘制图层,但它不起作用:调试自定义 CALayer class 的 draw方法在performAnimation方法退出后不断到达,而在animatePattern方法中修改图层的框架时应该调用它。我一定错过了一些非常明显的东西,但我目前正在 运行 转圈子,我希望能有新的目光关注它。

提前感谢您花时间考虑这个问题! 最好的,

通常情况下,当您花时间写下您的问题时,新的想法就会开始涌现。事实上,我记得 self.setNeedsDisplay() 通知系统需要(重新)绘制图层但它不会立即(重新)绘制它:它将在下次刷新时完成。似乎刷新发生在该特定动画的循环结束之后。为了克服这个问题,我首先在设置 patternLayer 边界后立即添加了对 display 方法的调用,并且它起作用了,但是鉴于@Matt 评论,我通过调用 displayIfNeeded 更改了解决方案方法代替。它也有效。

func animatePattern(for duration: TimeInterval = 1) {

let patternLayer        = PatternLayer(effectType: .dark)
patternLayer.frame      = self.bounds
self.layer.addSublayer(patternLayer)

// asks the system to draw the layer now
patternLayer.displayIfNeeded()

patternLayer.play(for: duration)

}

再一次,如果有人想出另一个更优雅的solution/explanation,请不要拒绝分享!

您重写 draw(_ context),因为 UIView 可以是 CALayer 的委托。

 UIView: {

    var patternLayer :  PatternLayer

     func animatePattern(for duration: TimeInterval = 1) {

      patternLayer      = PatternLayer(effectType: .dark)
       patternLayer.frame      = self.bounds
       self.layer.addSublayer(patternLayer)
     }

      func draw(_ layer: CALayer, in ctx: CGContext){
        layer.draw(ctx)
         if  NECESSARY { patternLayer.play(for: duration)}

        }

      }