Search code examples
objective-ccocoa-touchios5ios4ios6

Circular Progress Bars in IOS


I want to create a circular progress bar like the following:

Circular progress bar

How can I do that using Objective-C and Cocoa?

How I started doing it was creating a UIView and editing the drawRect, but I am bit lost. Any help would be greatly appreciated.

Thanks!


Solution

  • The basic concept is to use the UIBezierPath class to your advantage. You are able to draw arcs, which achieve the effect you're after. I've only had half an hour or so to have a crack at this, but my attempt is below.

    Very rudimentary, it simply uses a stroke on the path, but here we go. You can alter/modify this to your exact needs, but the logic to do the arc countdown will be very similar.

    In the view class:

    @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 the view controller:

    @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;
        }
    }