O código abaixo mostra o problema:
import UIKit
import SnapKit
class ViewController: UIViewController {
let animatedBorderView = AnimatedGradientBorderView()
override func viewDidLoad() {
super.viewDidLoad()
animatedBorderView.layer.cornerRadius = 20
animatedBorderView.clipsToBounds = true
view.addSubview(animatedBorderView)
animatedBorderView.snp.makeConstraints { make in
make.center.equalToSuperview()
make.size.equalTo(400)
}
}
}
class AnimatedGradientBorderView: UIView {
private let gradientLayer = CAGradientLayer()
private let shapeLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
setupLayers()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupLayers()
}
private func setupLayers() {
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
gradientLayer.colors = [
UIColor.systemRed.cgColor,
UIColor.systemYellow.cgColor,
UIColor.systemGreen.cgColor,
]
gradientLayer.locations = [0, 0.3, 0.6, 1]
layer.addSublayer(gradientLayer)
shapeLayer.lineWidth = 5
shapeLayer.fillColor = nil
shapeLayer.strokeColor = UIColor.black.cgColor
gradientLayer.mask = shapeLayer
startAnimating()
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
let path = UIBezierPath(roundedRect: bounds.insetBy(dx: shapeLayer.lineWidth / 2, dy: shapeLayer.lineWidth / 2), cornerRadius: layer.cornerRadius)
shapeLayer.path = path.cgPath
}
private func startAnimating() {
let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [0, 0.2, 0.4, 0.6]
animation.toValue = [0.4, 0.6, 0.8, 1]
animation.duration = 2.0
animation.repeatCount = .infinity
animation.autoreverses = false
gradientLayer.add(animation, forKey: "animateLocations")
}
}
Isso produz a animação abaixo:
Observe que defini autoreverses
como falso porque não quero que ele reverta ao repetir para infinity
. No entanto, como consequência disso, o problema é que, após o duration
, ele reinicia no original location
. Isso causa um salto repentino na borda do gradiente móvel.
Como posso fazer com que ele continue se movendo para sempre na mesma direção sem precisar reiniciar?
Para obter um efeito de borda rotativa, mudei para um gradiente cônico (.conic), que irradia cores naturalmente em torno de um ponto central. Em vez de deslocar as cores manualmente, toda a gradientLayer é rotacionada no eixo z. (Usando a animação de borda de gradiente estilo Snake no iOS como referência)
Experimente isto: