添加从一个视图流向另一个视图的微光动画

Add shimmer animation flowing from one view to another

我正在尝试创建从一个视图流向另一个视图的微光动画。以下是我到目前为止的代码:

override func viewDidLoad() {
    super.viewDidLoad()
    view1.backgroundColor = .purple
    view2.backgroundColor = .purple
    view1.startAnimating()
    //let seconds = 2.0
    //DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
        self.view2.startAnimating()
    //}
}

class ShimmerView: UIView {

let gradientColorOne : CGColor = UIColor.purple.cgColor
let gradientColorTwo : CGColor = UIColor.yellow.cgColor

func addGradientLayer() -> CAGradientLayer {
    
    let gradientLayer = CAGradientLayer()
    
    gradientLayer.frame = self.bounds
    gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
    gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
    gradientLayer.colors = [gradientColorOne, gradientColorTwo, gradientColorOne]
    gradientLayer.locations = [0.0, 0.5, 1.0]
    self.layer.addSublayer(gradientLayer)
    
    return gradientLayer
}

func addAnimation() -> CABasicAnimation {
   
    let animation = CABasicAnimation(keyPath: "locations")
    animation.fromValue = [-1.0, -0.5, 0.0]
    animation.toValue = [1.0, 1.5, 2.0]
    animation.repeatCount = .infinity
    animation.duration = 2
    return animation
}

func startAnimating() {
    
    let gradientLayer = addGradientLayer()
    let animation = addAnimation()
   
    gradientLayer.add(animation, forKey: animation.keyPath)
}
}

我尝试添加延迟,但我想这不会奏效,因为下次动画应该以进一步延迟开始。

这就是它现在的行为方式。动画并行发生:

但是,我想要的是它从 view1 开始动画,然后在 view2 中结束,然后继续这样做。关于如何实现这一目标的任何想法/建议?谢谢。

您可以尝试从调用站点控制动画顺序,如下所示。

  1. 像这样在 ShimmerView 中定义一个名为 animationDuration 的常量。
public static let animationDuration: Double = 2.0
  1. 像这样更新 addAnimation() 实现。
// Remove this line, We need this to be performed only once at a time.
/*
animation.repeatCount = .infinity
*/

// Update this line to use the constant defined earlier
animation.duration = ShimmerView.animationDuration
  1. 从您的调用站点,您现在可以执行此操作。
class ViewController: UIViewController {
    
    let view1 = ShimmerView()
    let view2 = ShimmerView()

    func startAnimating() {
        view1.startAnimating()

        let interval = ShimmerView.animationDuration
        DispatchQueue.main.asyncAfter(deadline: .now() + interval) { [weak self] in
            self?.view2.startAnimating()

            DispatchQueue.main.asyncAfter(deadline: .now() + interval) { [weak self] in
                self?.startAnimating()
            }
        }
    }
}

更新

This kind of almost works, but when view1 finishes the animation and view2 starts it, the gradient on view1 sits in the middle and vice versa. So, it doesn't give a clear flowing animation from top to bottom that goes on.

问题是您的代码没有在动画完成后从您的视图中移除 gradientLayer 实例。您可以使用 CATransaction.setCompletionBlock 来完成,如下所示。

import Foundation
import UIKit

class ShimmerView: UIView {
    
    static let animationDuration: Double = 2.0
    let gradientColorOne: CGColor = UIColor.purple.cgColor
    let gradientColorTwo: CGColor = UIColor.yellow.cgColor
    
    lazy var gradientLayer: CAGradientLayer = {
        let gradient = CAGradientLayer()
        gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
        gradient.endPoint = CGPoint(x: 0.0, y: 1.0)
        gradient.colors = [gradientColorOne, gradientColorTwo, gradientColorOne]
        gradient.locations = [0.0, 0.5, 1.0]
        return gradient
    }()
    
    func addGradientLayer() {
        gradientLayer.frame = self.bounds
        self.layer.addSublayer(gradientLayer)
    }
    
    func removeGradientLayer() {
        gradientLayer.removeFromSuperlayer()
    }

    func addAnimation() -> CABasicAnimation {
        let animation = CABasicAnimation(keyPath: "locations")
        animation.fromValue = [-1.0, -0.5, 0.0]
        animation.toValue = [1.0, 1.5, 2.0]
        animation.duration = ShimmerView.animationDuration
        return animation
    }
    
    func startAnimating() {
        self.addGradientLayer()
        let animation = addAnimation()
        
        CATransaction.begin()
        // Setting this block before adding the animation is crucial
        CATransaction.setCompletionBlock({ [weak self] in
            self?.removeGradientLayer()
        })
        gradientLayer.add(animation, forKey: animation.keyPath)
        CATransaction.commit()
    }
    
}