Circular control bars in iOS

I want to create a circular progress bar as shown below:

Circular progress bar

How to do this using Objective-C and Cocoa?

How I started doing this is creating a UIView and editing drawRect, but I got a little lost. Any help would be greatly appreciated.

Thank!

+50
objective-c cocoa-touch ios4 ios5 ios6
Nov 26
source share
6 answers

The basic concept is to use the UIBezierPath class to your advantage. You can draw arcs that achieve the effect you are using. I only had half an hour or so to have a crack, but my attempt is lower.

Very rudimentary, it just takes a stroke on the way, but here we go. You can change / change this for your specific needs, but the logic for the arc countdown will be very similar.

In a class like:

 @interface TestView () { CGFloat startAngle; CGFloat endAngle; } @end @implementation TestView - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code self.backgroundColor = [UIColor whiteColor]; // Determine our start and stop angles for the arc (in radians) startAngle = M_PI * 1.5; endAngle = startAngle + (M_PI * 2); } return self; } - (void)drawRect:(CGRect)rect { // Display our percentage as a string NSString* textContent = [NSString stringWithFormat:@"%d", self.percent]; UIBezierPath* bezierPath = [UIBezierPath bezierPath]; // Create our arc, with the correct angles [bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2) radius:130 startAngle:startAngle endAngle:(endAngle - startAngle) * (_percent / 100.0) + startAngle clockwise:YES]; // Set the display for the path, and stroke it bezierPath.lineWidth = 20; [[UIColor redColor] setStroke]; [bezierPath stroke]; // Text Drawing CGRect textRect = CGRectMake((rect.size.width / 2.0) - 71/2.0, (rect.size.height / 2.0) - 45/2.0, 71, 45); [[UIColor blackColor] setFill]; [textContent drawInRect: textRect withFont: [UIFont fontWithName: @"Helvetica-Bold" size: 42.5] lineBreakMode: NSLineBreakByWordWrapping alignment: NSTextAlignmentCenter]; } 

For a controller of the form:

 @interface ViewController () { TestView* m_testView; NSTimer* m_timer; } @end - (void)viewDidLoad { // Init our view [super viewDidLoad]; m_testView = [[TestView alloc] initWithFrame:self.view.bounds]; m_testView.percent = 100; [self.view addSubview:m_testView]; } - (void)viewDidAppear:(BOOL)animated { // Kick off a timer to count it down m_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(decrementSpin) userInfo:nil repeats:YES]; } - (void)decrementSpin { // If we can decrement our percentage, do so, and redraw the view if (m_testView.percent > 0) { m_testView.percent = m_testView.percent - 1; [m_testView setNeedsDisplay]; } else { [m_timer invalidate]; m_timer = nil; } } 
+66
Nov 27 '12 at 20:51
source share

I implemented a simple library for iOS that does just that. It is based on the UILabel class, so you can display whatever you want in your progress bar, but you can also leave it blank.

After initialization, you have only one line of code to set the progress:

[_myProgressLabel setProgress:(50/100))];

The library is called KAProgressLabel

+21
Jun 12 '13 at 22:37
source share

My example with magic numbers (for better understanding):

  CAShapeLayer *circle = [CAShapeLayer layer]; circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(29, 29) radius:27 startAngle:-M_PI_2 endAngle:2 * M_PI - M_PI_2 clockwise:YES].CGPath; circle.fillColor = [UIColor clearColor].CGColor; circle.strokeColor = [UIColor greenColor].CGColor; circle.lineWidth = 4; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; animation.duration = 10; animation.removedOnCompletion = NO; animation.fromValue = @(0); animation.toValue = @(1); animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; [circle addAnimation:animation forKey:@"drawCircleAnimation"]; [imageCircle.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; [imageCircle.layer addSublayer:circle]; 
+19
Jul 22 '14 at 16:44
source share

For Swift, use this,

 let circle = UIView(frame: CGRectMake(0,0, 100, 100)) circle.layoutIfNeeded() let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2) let circleRadius : CGFloat = circle.bounds.width / 2 * 0.83 var circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true ) let progressCircle = CAShapeLayer() progressCircle.path = circlePath.CGPath progressCircle.strokeColor = UIColor.greenColor().CGColor progressCircle.fillColor = UIColor.clearColor().CGColor progressCircle.lineWidth = 1.5 progressCircle.strokeStart = 0 progressCircle.strokeEnd = 0.22 circle.layer.addSublayer(progressCircle) self.view.addSubview(circle) 

Link: see here .

+11
Jan 28 '15 at 11:53
source share

You can check my lib MBCircularProgressBar

+10
Jul 23 '15 at 14:34
source share

Swift 3 use this,

CAShapeLayer with animation: continue with Zaid Pathan ans.

  let circle = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100)) circle.layoutIfNeeded() var progressCircle = CAShapeLayer() let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2) let circleRadius : CGFloat = circle.bounds.width / 2 * 0.83 let circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true ) progressCircle = CAShapeLayer () progressCircle.path = circlePath.cgPath progressCircle.strokeColor = UIColor.green.cgColor progressCircle.fillColor = UIColor.clear.cgColor progressCircle.lineWidth = 2.5 progressCircle.strokeStart = 0 progressCircle.strokeEnd = 1.0 circle.layer.addSublayer(progressCircle) let animation = CABasicAnimation(keyPath: "strokeEnd") animation.fromValue = 0 animation.toValue = 1.0 animation.duration = 5.0 animation.fillMode = kCAFillModeForwards animation.isRemovedOnCompletion = false progressCircle.add(animation, forKey: "ani") self.view.addSubview(circle) 
+4
Jan 12 '17 at 6:51
source share



All Articles