UIView
提供了很多动画属性,比如frame
, center
, backgroundColor
等,都可以产生动画效果。但有时候如果我们也想自定义一个动画属性,那应该怎么实现呢?
话不多说,直接用demo说话!
1、示例Demo
-
ProgressLayer
类
final class ProgressLayer: CALayer {
@NSManaged var progress: CGFloat
override init() {
super.init()
}
override init(layer: Any) {
super.init(layer: layer)
if let presentationLayer = layer as? ProgressLayer {
progress = presentationLayer.progress
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override class func needsDisplay(forKey key: String) -> Bool {
if key == "progress" { return true }
return super.needsDisplay(forKey: key)
}
override func action(forKey event: String) -> CAAction? {
if event == "progress" {
if let animation = super.action(forKey: "backgroundColor") as? CABasicAnimation {
animation.keyPath = event
if let pLayer = presentation() {
animation.fromValue = pLayer.progress
}
animation.isRemovedOnCompletion = true
animation.fillMode = .forwards
animation.toValue = nil
return animation
}
setNeedsDisplay()
return nil
}
return super.action(forKey: event)
}
override func draw(in ctx: CGContext) {
super.draw(in: ctx)
var frame = self.bounds
frame.size.width *= progress
ctx.setFillColor(UIColor.red.cgColor)
ctx.fill(frame)
}
}
ProgressView
final class ProgressView: UIView {
override class var layerClass: Swift.AnyClass { return ProgressLayer.self }
var progressLayer: ProgressLayer { return layer as! ProgressLayer }
@objc dynamic var progress: CGFloat {
get { return progressLayer.progress }
set { progressLayer.progress = min(1, max(0, newValue)) }
}
}
@IBAction func resetPressed(_ sender: Any) {
progressView.progress = 0
slider.setValue(0, animated: false)
}
@IBAction func loadPressed(_ sender: Any) {
UIView.animate(withDuration: 2) {
self.progressView.progress = 1
}
}
@IBAction func sliderValueChanged(_ sender: Any) {
progressView.progress = CGFloat(slider.value)
}
CustomAnimatableProperty.gif