我正在尝试为 UIButton 创建微光效果,因此我将其子类化并尝试启动动画,但没有任何反应。我可以看到按钮上的渐变,但它没有动画。我做错了什么?
这就是它的样子:
这是我的代码:
class ShimmerButton: UIButton {
private var gradientColorOne : CGColor = UIColor(white: 0.85, alpha: 0.0).cgColor
private var gradientColorTwo : CGColor = UIColor(white: 0.95, alpha: 0.5).cgColor
func addGradientLayer() -> CAGradientLayer {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = self.bounds
gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
gradientLayer.colors = [self.gradientColorOne, self.gradientColorTwo, self.gradientColorOne]
gradientLayer.locations = [0.0, 0.5, 1.0]
let top = self.layer.sublayers?.last
self.layer.insertSublayer(gradientLayer, above: top)
return gradientLayer
}
func addAnimation(duration: Double, repeatCount: Float) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [-1.0, -0.5, 0.0]
animation.toValue = [1.0, 1.5, 2.0]
animation.repeatCount = repeatCount
animation.duration = duration
return animation
}
func startAnimating(duration: Double, repeatCount: Float) {
let gradientLayer = self.addGradientLayer()
let animation = self.addAnimation(duration: duration, repeatCount: repeatCount)
gradientLayer.add(animation, forKey: "anim")
}
}
开始动画:
self.button.startAnimating(duration: 0.9, repeatCount: .infinity)
代码示例取自此处。
对 Position 属性进行动画处理效果很好。
我编写了一个创建简单渐变动画的游乐场。看起来像这样:
我还合并了 OP ShimmerButton(进行了一些更改,以便您可以启动/停止微光动画。微光动画如下所示:
这是该游乐场的代码:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class ShimmerButton: UIButton {
public var animating = false
private var gradientLayer: CAGradientLayer?
private var gradientColorOne : CGColor = UIColor(white: 0.85, alpha: 0.0).cgColor
private var gradientColorTwo : CGColor = UIColor(white: 0.95, alpha: 0.5).cgColor
private func addGradientLayer() -> CAGradientLayer? {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = self.bounds
gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
gradientLayer.colors = [self.gradientColorOne, self.gradientColorTwo, self.gradientColorOne]
gradientLayer.locations = [-1.0, -0.5, 0.0]
let top = self.layer.sublayers?.last
self.layer.insertSublayer(gradientLayer, above: top)
return gradientLayer
}
public func addAnimation(duration: Double, repeatCount: Float) -> CABasicAnimation {
let animation = CABasicAnimation(keyPath: "locations")
animation.fromValue = [-1.0, -0.5, 0.0]
animation.toValue = [1.0, 1.5, 2.0]
animation.repeatCount = repeatCount
animation.duration = duration
animation.delegate = self
return animation
}
public func stopAnimating() {
guard let gradientLayer = gradientLayer else { return }
gradientLayer.removeFromSuperlayer()
gradientLayer.removeAllAnimations()
self.gradientLayer = nil
animating = false
}
public func startAnimating(duration: Double, repeatCount: Float) {
if !animating {
animating = true
gradientLayer = self.addGradientLayer()
let animation = self.addAnimation(duration: duration, repeatCount: repeatCount)
gradientLayer?.add(animation, forKey: "anim")
}
}
}
extension ShimmerButton: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished: Bool) {
animating = false
if finished {
gradientLayer?.removeFromSuperlayer()
gradientLayer = nil;
}
}
}
class GradientView: UIView {
var animationIsLeft: Bool = false
var animating = false
static override var layerClass: AnyClass {
return CAGradientLayer.self
}
public func animateGradient() {
animating = true
animateGradientStep()
}
public func animateGradientStep() {
guard animating,
let layer = layer as? CAGradientLayer else { return }
let animation = CABasicAnimation(keyPath:"locations")
animation.duration = 0.5
animationIsLeft = !animationIsLeft
animation.fromValue = animationIsLeft ? [0, 0.1, 1] : [0, 0.9, 1.0]
let toValue = !animationIsLeft ? [0, 0.1, 1] : [0, 0.9, 1.0]
animation.toValue = toValue
animation.delegate = self
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) {
layer.locations = toValue.map { NSNumber(value: $0) }
}
layer.add(animation, forKey: nil)
}
func doInitSetup() {
guard let layer = layer as? CAGradientLayer else { return }
layer.startPoint = CGPoint(x: 0.0, y: 1.0)
layer.endPoint = CGPoint(x: 1.0, y: 1.0)
layer.colors = [UIColor.blue.cgColor, UIColor.red.cgColor, UIColor.blue.cgColor]
layer.locations = [0, 0.1, 1]
}
override init(frame: CGRect) {
super.init(frame: frame)
doInitSetup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
doInitSetup()
}
}
extension GradientView: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
animateGradientStep()
}
}
class MyViewController : UIViewController {
var gradientView: GradientView?
var button: ShimmerButton?
override func loadView() {
let view = UIView()
view.backgroundColor = .white
view.layer.borderWidth = 1
self.view = view
gradientView = GradientView()
if let gradientView = gradientView {
gradientView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(gradientView)
gradientView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
gradientView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 100).isActive = true
gradientView.heightAnchor.constraint(equalToConstant: 200).isActive = true
gradientView.widthAnchor.constraint(equalToConstant: 200).isActive = true
gradientView.layer.borderWidth = 1
}
button = ShimmerButton()
guard let button = button else { return }
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
button.setTitle("Button", for: .normal)
button.backgroundColor = UIColor.lightGray
button.layer.borderWidth = 1
button.layer.cornerRadius = 10
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20).isActive = true
button.widthAnchor.constraint(equalToConstant: 120).isActive = true
button.heightAnchor.constraint(equalToConstant: 30).isActive = true
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}
@IBAction func buttonTapped(_ sender: UIButton) {
if gradientView?.animating == true {
gradientView?.animating = false
} else {
gradientView?.animateGradient()
}
guard let button = button else { return }
if button.animating == true {
button.stopAnimating()
} else {
button.startAnimating(duration: 1.0, repeatCount: Float.greatestFiniteMagnitude)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}
PlaygroundPage.current.liveView = MyViewController()
class Roll: UIView {
private lazy var bg: CAGradientLayer = {
let l = RepairedCAGradientLayer()
l.startPoint = CGPoint(x: 0.5, y: 0)
l.endPoint = CGPoint(x: 0.5, y: 1)
l.colors = [
UIColor.white,
UIColor.black,
UIColor.white,
].compactMap{$0.cgColor}
l.locations = [-1, -0.5, 0]
layer.insertSublayer(l, at: 0)
let anim = CABasicAnimation(keyPath: "locations")
anim.fromValue = [-1, -0.5, 0]
anim.toValue = [1, 1.5, 2]
anim.repeatCount = .infinity
anim.duration = 1
l.add(anim, forKey: nil)
return l
}()
override func layoutSubviews() {
super.layoutSubviews()
bg.frame = bounds
}
}
(注意“from的结尾和to的开头”之间的差距必须是1.0)
看起来(目前)UIKit“知道”你想要在每一端无限扩展渐变,这很方便。在情况并非如此的系统中,您只需使用此技巧...
l.colors = [
UIColor.white,
UIColor.white,
UIColor.black,
UIColor.white,
UIColor.white
]
和
anim.fromValue = [-1, -1, -0.5, 0, 2]
anim.toValue = [-1, 1, 1.5, 2, 2]
为了具体起见,我通常会这样做,但无论如何。(注)我不知道如何制作GIF动画,抱歉
(注)这里不能上传视频真是荒谬,现在是21世纪了。
(注)我意识到现在有很多网站可以转换为动画 gif,这很公平