[英]Animate Custom UIView Property in Swift
所有!
我使用CoreGraphics
创建了一个循环进度视图,其外观和更新如下:
该类是一个UIView
类,它有一个名为'progress'的变量,用于确定填充了多少圆。
它运行良好,但我希望能够对progress变量进行动画处理,以使条形图平滑动画。
我从无数的例子中读到,我需要有一个CALayer
类和View类,我已经制作了它,但它根本没有动画。
两个问题:
CoreGraphics
绘制的图形,还是需要以某种方式在CALayer
重绘它? 我当前(尝试过的)解决方案在底部崩溃: anim.fromValue = pres.progress
。 这是怎么回事?
class CircleProgressView: UIView { @IBInspectable var backFillColor: UIColor = UIColor.blueColor() @IBInspectable var fillColor: UIColor = UIColor.greenColor() @IBInspectable var strokeColor: UIColor = UIColor.greenColor() dynamic var progress: CGFloat = 0.00 { didSet { self.layer.setValue(progress, forKey: "progress") } } var distToDestination: CGFloat = 10.0 @IBInspectable var arcWidth: CGFloat = 20 @IBInspectable var outlineWidth: CGFloat = 5 override class func layerClass() -> AnyClass { return CircleProgressLayer.self } override func drawRect(rect: CGRect) { var fillColor = self.fillColor if distToDestination < 3.0 { fillColor = UIColor.greenColor() } else { fillColor = self.fillColor } //Drawing the inside of the container //Drawing in the container let center = CGPoint(x:bounds.width/2, y: bounds.height/2) let radius: CGFloat = max(bounds.width, bounds.height) - 10 let startAngle: CGFloat = 3 * π / 2 let endAngle: CGFloat = 3 * π / 2 + 2 * π let path = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: startAngle, endAngle: endAngle, clockwise: true) path.lineWidth = arcWidth backFillColor.setStroke() path.stroke() let fill = UIColor.blueColor().colorWithAlphaComponent(0.15) fill.setFill() path.fill() //Drawing the fill path. Same process let fillAngleLength = (π) * progress let fillStartAngle = 3 * π / 2 - fillAngleLength let fillEndAngle = 3 * π / 2 + fillAngleLength let fillPath_fill = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: fillStartAngle, endAngle: fillEndAngle, clockwise: true) fillPath_fill.lineWidth = arcWidth fillColor.setStroke() fillPath_fill.stroke() //Drawing container outline on top let outlinePath_outer = UIBezierPath(arcCenter: center, radius: radius / 2 - outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true) let outlinePath_inner = UIBezierPath(arcCenter: center, radius: radius / 2 - arcWidth + outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true) outlinePath_outer.lineWidth = outlineWidth outlinePath_inner.lineWidth = outlineWidth strokeColor.setStroke() outlinePath_outer.stroke() outlinePath_inner.stroke() } } class CircleProgressLayer: CALayer { @NSManaged var progress: CGFloat override class func needsDisplayForKey(key: String) -> Bool { if key == "progress" { return true } return super.needsDisplayForKey(key) } override func actionForKey(key: String) -> CAAction? { if (key == "progress") { if let pres = self.presentationLayer() { let anim: CABasicAnimation = CABasicAnimation.init(keyPath: key) anim.fromValue = pres.progress anim.duration = 0.2 return anim } return super.actionForKey(key) } else { return super.actionForKey(key) } } }
谢谢您的帮助!
试试这个:)
class ViewController: UIViewController {
let progressView = CircleProgressView(frame:CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200))
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
button.frame = CGRectMake(0, 300, 200, 100)
button.backgroundColor = UIColor.yellowColor()
button.addTarget(self, action: #selector(ViewController.tap), forControlEvents: UIControlEvents.TouchUpInside)
view.addSubview(button)
view.addSubview(progressView)
progressView.progress = 1.0
}
func tap() {
if progressView.progress == 0.5 {
progressView.progress = 1.0
} else {
progressView.progress = 0.5
}
}
}
class CircleProgressView: UIView {
dynamic var progress: CGFloat = 0.00 {
didSet {
let animation = CABasicAnimation()
animation.keyPath = "progress"
animation.fromValue = circleLayer().progress
animation.toValue = progress
animation.duration = Double(0.5)
self.layer.addAnimation(animation, forKey: "progress")
circleLayer().progress = progress
}
}
func circleLayer() -> CircleProgressLayer {
return self.layer as! CircleProgressLayer
}
override class func layerClass() -> AnyClass {
return CircleProgressLayer.self
}
}
class CircleProgressLayer: CALayer {
@NSManaged var progress: CGFloat
override class func needsDisplayForKey(key: String) -> Bool {
if key == "progress" {
return true
}
return super.needsDisplayForKey(key)
}
var backFillColor: UIColor = UIColor.blueColor()
var fillColor: UIColor = UIColor.greenColor()
var strokeColor: UIColor = UIColor.greenColor()
var distToDestination: CGFloat = 10.0
var arcWidth: CGFloat = 20
var outlineWidth: CGFloat = 5
override func drawInContext(ctx: CGContext) {
super.drawInContext(ctx)
UIGraphicsPushContext(ctx)
//Drawing in the container
let center = CGPoint(x:bounds.width/2, y: bounds.height/2)
let radius: CGFloat = max(bounds.width, bounds.height) - 10
let startAngle: CGFloat = 3 * CGFloat(M_PI) / 2
let endAngle: CGFloat = 3 * CGFloat(M_PI) / 2 + 2 * CGFloat(M_PI)
let path = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
path.lineWidth = arcWidth
backFillColor.setStroke()
path.stroke()
let fill = UIColor.blueColor().colorWithAlphaComponent(0.15)
fill.setFill()
path.fill()
//Drawing the fill path. Same process
let fillAngleLength = (CGFloat(M_PI)) * progress
let fillStartAngle = 3 * CGFloat(M_PI) / 2 - fillAngleLength
let fillEndAngle = 3 * CGFloat(M_PI) / 2 + fillAngleLength
let fillPath_fill = UIBezierPath(arcCenter: center, radius: radius/2 - arcWidth/2, startAngle: fillStartAngle, endAngle: fillEndAngle, clockwise: true)
fillPath_fill.lineWidth = arcWidth
fillColor.setStroke()
fillPath_fill.stroke()
//Drawing container outline on top
let outlinePath_outer = UIBezierPath(arcCenter: center, radius: radius / 2 - outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
let outlinePath_inner = UIBezierPath(arcCenter: center, radius: radius / 2 - arcWidth + outlineWidth / 2, startAngle: startAngle, endAngle: endAngle, clockwise: true)
outlinePath_outer.lineWidth = outlineWidth
outlinePath_inner.lineWidth = outlineWidth
strokeColor.setStroke()
outlinePath_outer.stroke()
outlinePath_inner.stroke()
UIGraphicsPopContext()
}
}
虽然AntonTheDev提供了很好的答案,但他的解决方案不允许您在动画块中设置CircularProgressView的动画,所以你不能做如下的整洁事情:
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut,
animations: {
circularProgress.progress = 0.76
}, completion: nil)
这里有一个类似的问题,基于接受的答案和这篇文章的想法,最新的Swift 3答案。 这就是最终解决方案的样子。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.