Animate Custom UIView Property in Swift

后端 未结 2 1520
一向
一向 2021-01-03 07:01

all!

I have created a circular progress view using CoreGraphics that looks and updates like so:

50% 75%

The class is a UIView

相关标签:
2条回答
  • 2021-01-03 07:40

    Try this out :)

    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()
        }
    }
    
    0 讨论(0)
  • 2021-01-03 07:46

    Whilst AntonTheDev provides a great answer, his solution does not allow you to animate the CircularProgressView in an animation block, so you cant do neat things like:

    UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut, 
    animations: {
        circularProgress.progress = 0.76
    }, completion: nil)
    

    There's a similar question here with a up to date Swift 3 answer based on ideas from the accepted answer and this post. This is what the final solution looks like.

    Swift 3 Solution

    0 讨论(0)
提交回复
热议问题