如何使用 CATransaction 为 CALayer 的出现设置动画?

Posted

技术标签:

【中文标题】如何使用 CATransaction 为 CALayer 的出现设置动画?【英文标题】:How to animate the appearing of a CALayer using CATransaction? 【发布时间】:2021-10-04 07:49:40 【问题描述】:

我想为CALAyer 制作动画,让它看起来像是凭空出现的。我想我可以先将图层的变换设置为“缩放到 x:0,y:0”。然后做一个动画,将transform设置为“scale to x: 1, y: 1”,也就是identity。我认为这会使图层看起来从无到有扩大。但是,当我尝试实现这一点时,图层立即出现,没有动画。

MCVE:

class MyView: UIView 
    override init(frame: CGRect) 
        super.init(frame: frame)
        commonInit()
    
    required init?(coder: NSCoder) 
        super.init(coder: coder)
        commonInit()
    
    
    func commonInit() 
        backgroundColor = .clear
        clipsToBounds = false
        setupLayers()
    
    
    var squareLayer: CAShapeLayer!
    
    private func setupLayers() 
        layer.sublayers?.forEach  $0.removeFromSuperlayer() 
        squareLayer = CAShapeLayer()
        layer.addSublayer(squareLayer)
    
    
    override func layoutSubviews() 
        super.layoutSubviews()
        drawLayers()
    
    
    func drawLayers() 
        squareLayer.fillColor = UIColor.red.cgColor
        squareLayer.path = UIBezierPath(rect: bounds).cgPath
        squareLayer.frame = bounds
    

然后在我的 VC 中:

@IBAction func click() 
    myView = MyView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
    view.addSubview(myView)
    myView.drawLayers()
    myView.squareLayer.setAffineTransform(.init(scaleX: 0, y: 0))
    CATransaction.begin()
    CATransaction.setAnimationDuration(1)
    self.myView.squareLayer.setAffineTransform(.init(scaleX: 1, y: 1))
    CATransaction.commit()

我认为这是因为第一个 setAffineTransform 调用也是动画的(我不知道为什么会产生影响,但我怀疑这可能会产生影响),所以我尝试将第二个调用移动到完成块:

@IBAction func click() 
    myView = MyView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
    view.addSubview(myView)
    myView.drawLayers()
    CATransaction.begin()
    CATransaction.setCompletionBlock 
        CATransaction.begin()
        CATransaction.setAnimationDuration(1)
        self.myView.squareLayer.setAffineTransform(.init(scaleX: 1, y: 1))
        CATransaction.commit()
    
    myView.squareLayer.setAffineTransform(.init(scaleX: 0, y: 0))
    CATransaction.commit()

但是,输出是相同的。另一方面,如果我在第一次调用setAffineTransform 后稍等片刻,它就会起作用:

@IBAction func click() 
    myView = MyView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
    view.addSubview(myView)
    myView.drawLayers()
    myView.squareLayer.setAffineTransform(.init(scaleX: 0, y: 0))
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) 
        CATransaction.begin()
        CATransaction.setAnimationDuration(1)
        self.myView.squareLayer.setAffineTransform(.init(scaleX: 1, y: 1))
        CATransaction.commit()
    

我不认为这是我应该如何解决这个问题。据推测,在那段时间里,一些生命周期方法被调用(我不知道是哪个),而我应该做的是在该生命周期方法之后进行第二次setAffineTransform 调用......(?)

我知道我可以使用CABasicAnimation 来代替,但我不喜欢我需要使用委托来检测动画的结束。这让很多事情对我来说非常麻烦。由于延迟 0.1 秒有效,CATransactions 不可能做到这一点,对吧?

如何使用动画使CALayer 出现?

【问题讨论】:

【参考方案1】:

另一方面,如果我等一下

没错。图层必须是渲染树的一部分才能进行动画处理,因此您不能添加图层并将其作为同一事务的一部分进行动画处理。因此,您的异步调用在附加动画之前等待当前事务提交和下一个事务开始,实际上是一个完全正确的解决方案。

另一种可能是添加层后调用CATransaction.flush();这有效地提早提交了当前事务。

【讨论】:

以上是关于如何使用 CATransaction 为 CALayer 的出现设置动画?的主要内容,如果未能解决你的问题,请参考以下文章

CATransaction 动画问题

如何让 CATransaction 无限重复? - 斯威夫特

iPhone - UINavigationController - 使用 CATransaction 自定义动画

CALayer.opacity 在 CATransaction 内没有动画

CATransaction 完成块成功或失败

CATransaction 和 CAAnimation 有啥区别?