为什么自定义CALayer的draw方法没有更早调用?

时间:2019-02-11 11:52:22

标签: ios swift animation timer draw

问题

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

详细说明

为了清楚和简化起见,让我示意执行的动作。

Cycle

到目前为止,我创建的所有动画均按预期工作:它们是现有子视图/子层上CABAsicAnimations的组合,或者是添加到选定视图层次结构中的新动画。它们被编码为UIView扩展,因此可以在任何视图上调用它们,而不必考虑视图或视图所在的视图控制器。嗯,除了一个视图之外,所有其他功能都可以。

我确实已经创建了一个自定义CALayer类,该类包含CALayer上的图形图案。在此自定义类的扩展中,提供了一种对这些模式进行动画处理的方法(请参见下文中的代码)。因此,总而言之,当我到达步骤/方法animate selected view并运行此特定动画时,这是应发生的情况

  • 名为animatePattern的方法称为
  • 此方法将自定义CALayer类添加到选定的视图,然后调用该层的动画扩展

问题:如果使用所有其他动画,则所有图形均在退出animate selected view步骤/方法之前执行,在这种情况下,将自定义CALayer类绘制在performAnimation方法退出后被称为 ,这反过来会导致动画崩溃。

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

代码

  • animate selected view步骤/方法调用的方法:

    func animatePattern(for duration: TimeInterval = 1) {
    
    let patternLayer        = PatternLayer(effectType: .dark)
    patternLayer.frame      = self.bounds
    self.layer.addSublayer(patternLayer)
    patternLayer.play(for: duration)
    
    }
    

(请注意,此方法在UIView扩展中,因此self在此表示已在其上调用动画的UIView)

  • 简化的自定义CALayer类:

    override var bounds: CGRect {
        didSet {
            self.setNeedsDisplay()
        }
    
    
    // Initializers
    override init() {
        super.init()
        self.setNeedsDisplay()
    }
    
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    init(effectType: EffectType){
        super.init()
        // sets various properties
        self.setNeedsDisplay()
    }
    
    
    // Drawing
    override func draw(in ctx: CGContext) {
        super.draw(in: ctx)
        guard self.frame        != CGRect.zero else { return }
    
        self.masksToBounds      = true
    
        // do the drawings
    }
    
  • 自定义CALayer类的动画扩展:

     func play(for duration: Double, removeAfterCompletion: RemoveAfterCompletion = .no) {
    
        guard self.bounds != CGRect.zero else {
            print("Cannot animate nil layer")
            return }
    
        CATransaction.begin()
    
        CATransaction.setCompletionBlock {
    
            if removeAfterCompletion.removeStatus {
                if case RemoveAfterCompletion.yes = removeAfterCompletion { self.fadeOutDuration = 0.0 }
                self.fadeToDisappear(duration: &self.fadeOutDuration)
            }
        }
    
    
        // perform animations
    
        CATransaction.commit()
    }
    

到目前为止的尝试

我试图通过在代码中的各个位置插入setNeedsDisplay / setNeedsLayout来强制绘制图层,但是它不起作用:不断调试自定义CALayer类的draw方法在performAnimation方法退出之后,而在animatePattern方法中修改图层的框架时应调用它。我必须想念一些很明显的东西,但是我目前正在圈子里奔跑,不胜感激。

在此先感谢您抽出宝贵的时间来考虑此问题! 最好,

2 个答案:

答案 0 :(得分:0)

您可以覆盖draw(_上下文),因为UIView可以是CALayer的委托。

List

答案 1 :(得分:-1)

通常情况下,当您花时间写下您的问题时,就会出现新的想法。实际上,我记得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)

}

同样,如果有人提出另一个更优雅的解决方案/解释,请不要共享!