Swift: 如何一步步画圆?

Swift: How to draw a circle step by step?

我建一个工程,最初是画一个圆弧,这个圆弧是圆的1/8。
然后我在 viewController 上放了一个按钮,每当我点击这个按钮时,它会在上面无缝绘制另一个 1/8 的圆。
但是我遇到了一个问题:当我点击按钮时,它几乎快速绘制了弧线(0.25s),而不是我之前设置的持续时间(1s)。
如何实现无论我什么时候点击按钮,它消耗的时间都与我之前设置的持续时间相同?

import UIKit

let π = CGFloat(M_PI)

class ViewController: UIViewController {

var i: CGFloat = 1
var maxStep: CGFloat = 8

var circleLayer: CAShapeLayer?

override func viewDidLoad() {
    super.viewDidLoad()
    let startAngle = CGFloat(0)
    let endAngle = 2*π
    let ovalRect = CGRectMake(100, 100, 100, 100)

    let ovalPath = UIBezierPath(arcCenter: CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect)), radius: CGRectGetWidth(ovalRect), startAngle: startAngle, endAngle: endAngle, clockwise: true)

    let circleLayer = CAShapeLayer()
    circleLayer.path = ovalPath.CGPath
    circleLayer.strokeColor = UIColor.blueColor().CGColor
    circleLayer.fillColor = UIColor.clearColor().CGColor
    circleLayer.lineWidth = 10.0
    circleLayer.lineCap = kCALineCapRound
    self.circleLayer = circleLayer

    self.view.layer.addSublayer(self.circleLayer)

    let anim = CABasicAnimation(keyPath: "strokeEnd")
    // here i set the duration
    anim.duration = 1.0
    anim.fromValue = 0.0
    anim.toValue = self.i/self.maxStep

    self.circleLayer!.strokeStart = 0.0
    self.circleLayer!.strokeEnd = self.i/self.maxStep

    self.circleLayer!.addAnimation(anim, forKey: "arc animation")
    self.i++
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// when this action be triggered, it almost draws the arc rapidly, why?
@IBAction func buttonAnimate(sender: UIButton) {
    if self.i<=self.maxStep {
        self.circleLayer!.strokeStart = 0.0
        self.circleLayer!.strokeEnd = self.i/self.maxStep
        self.i++
    }
}
}

获取您的 animation 并重置属性。请参阅下面的代码:

@IBAction func buttonAnimate(sender: UIButton) {
    if self.i<=self.maxStep {
        self.circleLayer!.strokeStart = 0.0
        self.circleLayer!.strokeEnd = self.i/self.maxStep

        self.circleLayer?.animationKeys()
        let anim = self.circleLayer?.animationForKey("arc animation") as? CABasicAnimation

        if anim == nil {
            let anim = CABasicAnimation(keyPath: "strokeEnd")
            // here i set the duration
            anim.duration = 1
            anim.fromValue = (self.i - 1)/self.maxStep
            anim.toValue = self.i/self.maxStep
            self.circleLayer!.addAnimation(anim, forKey: "arc animation")
        }
        self.i++
    }
}

希望对您有所帮助!

这是正确的解决方案:

import UIKit

import UIKit

let π = CGFloat(M_PI)

class ViewController: UIViewController {

    var i: CGFloat = 1
    var maxStep: CGFloat = 8

    var prevValue : CGFloat = 0.0

    var circleLayer: CAShapeLayer?

    override func viewDidLoad() {
        super.viewDidLoad()
        let startAngle = CGFloat(0)
        let endAngle = 2*π
        let ovalRect = CGRectMake(100, 100, 100, 100)

        let ovalPath = UIBezierPath(arcCenter: CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect)), radius: CGRectGetWidth(ovalRect), startAngle: startAngle, endAngle: endAngle, clockwise: true)

        let circleLayer = CAShapeLayer()
        circleLayer.path = ovalPath.CGPath
        circleLayer.strokeColor = UIColor.blueColor().CGColor
        circleLayer.fillColor = nil //UIColor.clearColor().CGColor
        circleLayer.lineWidth = 10.0
        circleLayer.lineCap = kCALineCapRound
        self.circleLayer = circleLayer

        self.view.layer.addSublayer(self.circleLayer!)



        self.circleLayer!.strokeStart = 0.0
//Initial stroke-
        setStrokeEndForLayer(self.circleLayer!, from: 0.0,  to: self.i / self.maxStep, animated: true)

        self.i++


    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    @IBAction func buttonAnimate(sender: UIButton) {
        if self.i <= self.maxStep {

            setStrokeEndForLayer(self.circleLayer!, from: (self.i - 1 ) / self.maxStep,  to: self.i / self.maxStep, animated: true)
            self.i++
        }
    }

    func setStrokeEndForLayer(layer: CALayer,  var from:CGFloat, to: CGFloat, animated: Bool)
    {

        self.circleLayer!.strokeEnd = to

        if animated
        {

            //Check if there is any existing animation is in progress, if so override, the from value
            if let circlePresentationLayer = self.circleLayer!.presentationLayer()
            {
                from = circlePresentationLayer.strokeEnd
            }

            //Remove any on going animation
            if (self.circleLayer?.animationForKey("arc animation") as? CABasicAnimation != nil)
            {
                //Remove the current animation
                self.circleLayer!.removeAnimationForKey("arc animation")
            }


            let anim = CABasicAnimation(keyPath: "strokeEnd")
            // here i set the duration
            anim.duration = 1
            anim.fromValue = from
            anim.toValue = to


            self.circleLayer!.addAnimation(anim, forKey: "arc animation")
        }
    }

}