swiftformulacashapelayer

Creating a progress indicator with a rounded rectangle


I am attempting to create a rounded-rectangle progress indicator in my app. I have previously implemented a circular indicator, but not like this shape. I would like it to look something like this (start point is at the top):

enter image description here

But I get this with 0 as the .strokeStart property of the layer:

enter image description here

My current code place in viewDidLoad():

let queueShapeLayer = CAShapeLayer()
let queuePath = UIBezierPath(roundedRect: addToQueue.frame, cornerRadius: addToQueue.layer.cornerRadius)
queueShapeLayer.path = queuePath.cgPath
queueShapeLayer.lineWidth = 5
queueShapeLayer.strokeColor = UIColor.white.cgColor
queueShapeLayer.fillColor = UIColor.clear.cgColor
queueShapeLayer.strokeStart = 0
queueShapeLayer.strokeEnd = 0.5
view.layer.addSublayer(queueShapeLayer)

addToQueue is the button which says 'Upvote'.

Unlike creating a circular progress indicator, I cannot set the start and end angle in the initialisation of a Bezier path.

How do I make the progress start from the top middle as seen in the first image?

Edit - added a picture without corner radius on:

enter image description here

It seems that the corner radius is creating the issue.


If you have any questions, please ask!


Solution

  • I found a solution so the loading indicator works for round corners:

    enter image description here

    let queueShapeLayer = CAShapeLayer()
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        // Queue timer
        let radius = addToQueue.layer.cornerRadius
        let diameter = radius * 2
        let totalLength = (addToQueue.frame.width - diameter) * 2 + (CGFloat.pi * diameter)
    
        let queuePath = UIBezierPath(roundedRect: addToQueue.frame, cornerRadius: radius)
        queueShapeLayer.path = queuePath.cgPath
        queueShapeLayer.lineWidth = 5
        queueShapeLayer.strokeColor = UIColor.white.cgColor
        queueShapeLayer.fillColor = UIColor.clear.cgColor
        queueShapeLayer.strokeStart = 0.25 - CGFloat.pi * diameter / 3 / totalLength // Change the '0.25' to 0.5, 0.75 etc. wherever you want the bar to start
        queueShapeLayer.strokeEnd = queueShapeLayer.strokeStart + 0.5 // Change this to the value you want it to go to (in this case 0.5 or 50% loaded)
        view.layer.addSublayer(queueShapeLayer)
    }
    

    After I had did this though, I was having problems that I couldn't animate the whole way round. To get around this, I created a second animation (setting strokeStart to 0) and then I placed completion blocks so I could trigger the animations at the correct time.

    Tip:

    Add animation.fillMode = CAMediaTimingFillMode.forwards & animation.isRemovedOnCompletion = false when using a CABasicAnimation for the animation to wait until you remove it.

    I hope this formula helps anyone in the future!

    If you need help, you can always message me and I am willing to help. :)