在Swift中animation自定义UIView属性
所有!
我创build了一个使用CoreGraphics的循环进度视图,看起来和更新像这样:
50% 75%
这个类是一个UIView类,它有一个名为“progress”的variables,它决定了多less圆被填充。
它运作良好,但我想能够animation变化的进度variables,使酒吧animation顺利。
我已经从无数的例子中看到,我需要有一个CALayer类和我所做的View类,但是它根本没有animation。
两个问题:
- 我可以保留在Core Graphics中绘制的graphics,还是需要以某种方式在CALayer中重绘它?
-
我目前(尝试)的解决scheme崩溃在: 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
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提供了一个很好的答案,但是他的解决scheme不允许您在animation块中为CircularProgressView设置animation,所以你不能做一些整洁的事情:
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut, animations: { circularProgress.progress = 0.76 }, completion: nil)
这里有一个类似的问题,以最新的Swift 3的答案为基础,从接受的答案和这篇文章的想法。 这是最终的解决scheme。
Swift 3解决scheme