0

CALAyerどこからともなく現れるようにアニメーション化したいと思います。最初にレイヤーの変換を「x:0、y:0にスケーリング」に設定できると思いました。次に、アニメーションを実行して、変換を「x: 1、y: 1 にスケール」に設定します。これがアイデンティティです。これにより、レイヤーがゼロから拡大されたように見えると思いました。しかし、これを実装しようとすると、アニメーションなしでレイヤーがすぐに表示されました。

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(なぜそれが影響するのか正確にはわかりませんでしたが、影響があるのではないかと疑っていました)。そのため、2 番目の呼び出しを完了ブロックに移動しようとしました。

@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そのライフサイクル メソッドの後に 2 回目の呼び出しを行う必要があります... (?)

代わりにこれを使用できることはわかっていますCABasicAnimationが、アニメーションの終わりを検出するためにデリゲートを使用する必要がある方法が好きではありません。これは私にとって多くのことを非常に面倒にします。0.1秒遅らせるのが効いたので、これはCATransactionsで無理じゃないですよね?

CALayerアニメーションを使用して表示するにはどうすればよいですか?

4

1 に答える 1

1

逆にちょっと待ってたら

そのとおりです。アニメーション化するには、レイヤが Render Tree の一部である必要があるため、レイヤを追加して同じトランザクションの一部としてアニメーション化することはできません。したがって、非同期呼び出しは、現在のトランザクションがコミットされて次のトランザクションが開始されるのを待ってからアニメーションをアタッチします。実際、これは完全に正しい解決策です。

CATransaction.flush()別の可能性は、レイヤーを追加した後に呼び出すことです。これにより、現在のトランザクションが効果的に早期にコミットされます。

于 2021-10-04T08:04:02.973 に答える