1

I would like to create a circular progress with slices where each slice is an arc.

I based my code on this answer: Draw segments from a circle or donut

But I don't know how to copy it and rotate it 10 times. And I would like to color it following a progress variable (in percent).

EDIT: I would like something like this

enter image description here

Any help please

Regards

Community
  • 1
  • 1
Jojo
  • 449
  • 1
  • 7
  • 16
  • Have you looked at this tutorial? http://www.raywenderlich.com/94302/implement-circular-image-loader-animation-cashapelayer – Acoop Aug 04 '15 at 12:07
  • yes. But it's not really like this that I want to do. I want something like this: http://blog.scottlogic.com/archive/2011/02/SegmentedProgress.png – Jojo Aug 04 '15 at 12:19
  • check out this: http://stackoverflow.com/questions/30554632/masking-circle-segments-in-swift/30556058#30556058 – Dániel Nagy Aug 04 '15 at 12:56

1 Answers1

12

You could use a circular path and set the strokeStart and StrokeEnd. Something like this:

let circlePath = UIBezierPath(ovalInRect: CGRect(x: 200, y: 200, width: 150, height: 150))
var segments: [CAShapeLayer] = []
let segmentAngle: CGFloat = (360 * 0.125) / 360

for var i = 0; i < 8; i++ {
    let circleLayer = CAShapeLayer()
    circleLayer.path = circlePath.CGPath

    // start angle is number of segments * the segment angle
    circleLayer.strokeStart = segmentAngle * CGFloat(i)

    // end angle is the start plus one segment, minus a little to make a gap
    // you'll have to play with this value to get it to look right at the size you need
    let gapSize: CGFloat = 0.008
    circleLayer.strokeEnd = circleLayer.strokeStart + segmentAngle - gapSize

    circleLayer.lineWidth = 10
    circleLayer.strokeColor = UIColor(red:0,  green:0.004,  blue:0.549, alpha:1).CGColor
    circleLayer.fillColor = UIColor.clearColor().CGColor

    // add the segment to the segments array and to the view
    segments.insert(circleLayer, atIndex: i)
    view.layer.addSublayer(segments[i])
}
Chris Slowik
  • 2,859
  • 1
  • 14
  • 27
  • Great !! Thanks !! Just one more thing how can you color the segments from a progress rate (0% to 100%) ? From grey to blue color. Thank you for your help !! – Jojo Aug 04 '15 at 19:24
  • i'd just use the number of steps to modify a base color. Should be clean to do within the loop since you're already stepping through each segment. – Chris Slowik Aug 04 '15 at 20:55
  • Great!! It works. Just have to rotate the layer beacause the first segment begins at -M_PI instead of -M_PI /2 – Jojo Aug 05 '15 at 08:34
  • One more question about the rotation which did not work: I create a new CAShapeLayer outside the for-loop and I make this transformation: var transform:CATransform3D = CATransform3DIdentity testlayer.anchorPoint = CGPoint(x: 0.5, y: 0.5) transform = CATransform3DRotate(transform,degree2radian(CGFloat(-90)), 0, 0,1) testlayer.transform = transform view.layer.addSublayer(testlayer) But results is not good. Why ? – Jojo Aug 05 '15 at 16:38
  • I forget to say: var testlayer = CAShapeLayer() testlayer.frame = CGRect(x: self.view.frame.midX, y: 30, width: 200, height: 200) – Jojo Aug 05 '15 at 16:45
  • 1
    I know this is years old, but just want to point out that you have "let segmentAngle: CGFloat = (360 * 0.125) / 360" which doesn't really make sense. The 360 cancels out, so the segmentAngle variable ends up being whichever decimal you set (in this case 0.125). – johnny Jul 16 '19 at 23:35
  • Also, for some reason all of the segments created have no frame or bounds when they're created like this, which is problematic for a lot of use-cases – johnny Jul 17 '19 at 20:08
  • i am also trying to create similar to this but i dont want any space between segments rather a line to separate or separator. how can i achieve this – Muhammad Ahmed Jan 23 '21 at 15:36