Creating a circular progress bar around an image in swift - ios

I am trying to create a circular progress bar around an image as shown in the screenshot below. So far I have managed to create a round image with a green border using the code below:
self.image.layer.cornerRadius = self.image.frame.size.width / 2
self.image.clipsToBounds = true
self.image.layer.borderWidth = 6.0
self.image.layer.borderColor = UIColor.greenColor.CGColor
My question is, how do I create a circular progress bar from the border than I have set? Or do I need to remove this border and take a different approach?

I understood your problem and I suggest you to use some library to create such a loader you can find many of the swift libraries. On of them is FPActivityIndicator, it is the same circular loader which you are searching for, you only have to adjust the radius and position of the loader to move it around the image
if you need further help you can send me message and if it helps please accept the answer. Thanks

I have customized the answer of WDUK in stack overflow post according to your need,
It is something like,
TestView.h
#import <UIKit/UIKit.h>
#interface TestView : UIView
#property (nonatomic) double percent;
#end
TestView.m
#import "TestView.h"
#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
{
CGFloat constant = rect.size.width/ 5;
UIImage *img = [UIImage imageNamed:#"lion.jpg"]; // lion.jpg is image name
UIImageView *imgView = [[UIImageView alloc]initWithFrame:CGRectMake(rect.origin.x + constant/2, rect.origin.y + constant/2, rect.size.width-constant, rect.size.height - constant)];
imgView.image = img;
imgView.layer.masksToBounds = YES;
imgView.layer.cornerRadius = imgView.frame.size.width / 2;
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
// Create our arc, with the correct angles
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:constant*2
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];
[self addSubview:imgView];
}
#end
ViewController.m
#import "ViewController.h"
#import "TestView.h"
#interface ViewController (){
TestView* m_testView;
NSTimer* m_timer;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Init our view
CGRect frame = CGRectMake(50, 50, 200, 200);
m_testView = [[TestView alloc] initWithFrame:frame];
m_testView.percent = 0;
[self.view addSubview:m_testView];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidAppear:(BOOL)animated
{
// Kick off a timer to count it down
m_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(increamentSpin) userInfo:nil repeats:YES];
}
- (void)increamentSpin
{
// increament our percentage, do so, and redraw the view
if (m_testView.percent < 100) {
m_testView.percent = m_testView.percent + 1;
[m_testView setNeedsDisplay];
}
else {
[m_timer invalidate];
m_timer = nil;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
If you are decreasing frame size from viewcontroller then you should reduce bezierPath.lineWidth accordingly to show respectively thin progress around imageview.
And this is working perfact, i have tested it!!!

Related

how Draw a percentage of a circle objective c

i can need layout like same as image
but i can not draw like this so any idea about this,
The easiest approach would be to have a view with two subviews, one for the green "percent filled" level, and one for the label for the text. Then you can update the frame for the "percent filled" based upon, obviously, what percent filled you want it. And then apply a circular mask to the whole thing.
For example:
// CircleLevelView.h
//
// Created by Robert Ryan on 10/28/17.
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface CircleLevelView : UIView
/// Percent filled
///
/// Value between 0.0 and 1.0.
#property (nonatomic) IBInspectable CGFloat percent;
/// Text to show up in center of view
///
/// Value between 0.0 and 1.0.
#property (nonatomic, strong) IBInspectable NSString *text;
#end
And
// CircleLevelView.m
//
// Created by Robert Ryan on 10/28/17.
#import "CircleLevelView.h"
#interface CircleLevelView()
#property (nonatomic, weak) CAShapeLayer *circleMask;
#property (nonatomic, weak) UILabel *label;
#property (nonatomic, weak) UIView *fillView;
#end
#implementation CircleLevelView
#synthesize percent = _percent;
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
[self configure];
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
[self configure];
return self;
}
- (instancetype)init {
return [self initWithFrame:CGRectZero];
}
- (void)configure {
self.clipsToBounds = true;
UILabel *fillView = [[UILabel alloc] init];
fillView.translatesAutoresizingMaskIntoConstraints = false;
fillView.backgroundColor = [UIColor colorWithRed:169.0 / 255.0
green:208.0 / 255.0
blue:66.0 / 255.0
alpha:1.0];
[self addSubview:fillView];
self.fillView = fillView;
UILabel *label = [[UILabel alloc] init];
label.translatesAutoresizingMaskIntoConstraints = false;
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor blackColor];
label.textAlignment = NSTextAlignmentCenter;
[self addSubview:label];
self.label = label;
[NSLayoutConstraint activateConstraints:#[
[label.topAnchor constraintEqualToAnchor:self.topAnchor],
[label.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
[label.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
[label.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
[fillView.topAnchor constraintEqualToAnchor:self.topAnchor],
[fillView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
[fillView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
[fillView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor]
]];
CAShapeLayer *circleMask = [CAShapeLayer layer];
circleMask.fillColor = [UIColor whiteColor].CGColor;
circleMask.strokeColor = [UIColor blackColor].CGColor;
circleMask.lineWidth = 0;
self.layer.mask = circleMask;
self.circleMask = circleMask;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGPoint center = CGPointMake(self.bounds.origin.x + self.bounds.size.width / 2.0, self.bounds.origin.y + self.bounds.size.height / 2.0);
CGFloat radius = MIN(self.bounds.size.width, self.bounds.size.height) / 2.0;
self.circleMask.path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI * 2.0 clockwise:true].CGPath;
[self updatePercentFill];
}
- (void)updatePercentFill {
CGFloat circleHeight = MIN(self.bounds.size.width, self.bounds.size.height);
CGFloat percentHeight = circleHeight * self.percent;
self.fillView.frame = CGRectMake(0,
(self.bounds.size.height - circleHeight) / 2 + (circleHeight - percentHeight),
self.bounds.size.width,
circleHeight);
}
// MARK: - Custom Accessor Methods
- (CGFloat)percent {
return _percent;
}
- (void)setPercent:(CGFloat)percent {
_percent = percent;
[self updatePercentFill];
}
- (NSString *)text {
return self.label.text;
}
- (void)setText:(NSString *)text {
self.label.text = text;
}
#end
That yields:
If you know the chord position you can fill to the chord by setting the clip region and then filling the circle.
To work out the position of the chord to give an area of x% you'll need to do some geometry/trigonometry. Every chord which does not pass through the centre forms and isosceles triangle with the centre, and the two sides of that triangle which are radii delimit a segment of the circle. So the area on one side of a chord which does not pass through the centre is the difference of the area of a triangle and a segment, and you can work out how the chord divides the area or where the chord needs to be to divide the area in a particular ratio.
If that all sounds like gobbledegook try looking up chord geometry and you'll undoubtedly find books/sites with helpful diagrams and formulas.
HTH

should this CustomView be a category or a subclass?

I want to extend an existing custom UIViewclass so it can be used in several places within an app. The view has a method that arranges multiple UIButtonsin a circle. The number of UIButtons will vary depending on where the method is called. So too will the size of the UIButton and the radius of the circle. It would also be useful (but not essential) to be able to call the method several times within the same UIView.
Which is the better way ? to make this a category or a sub-class? It would appear I could use either based on this discussion of pros and cons. But my question is more specific.
I’ve made categories for UIColor and UIFontbut so far I have not been able to make this method work as a category just by following Apple's documentation (I tried it both as a class method and as an instance method). Before I try to make it work as a subclass, can someone who has done it before, either way, please recommend the better approach based on my example below.
Here is the method as it was in the CustomView
circleOfButtons
- (void)circleOfButtons {
screenCentre.x = CGRectGetWidth (self.bounds) / 2;
screenCentre.y = CGRectGetHeight (self.bounds) / 2;
for (int i = 1; i <= buttonCount; i++) {
radians = 2 * M_PI * i / buttonCount;
CGFloat arcStartPoint = - M_PI / 2; // first point clockwise after 12 o'clock
buttonCentre.x = screenCentre.x + radius * cos(radians + arcStartPoint);
buttonCentre.y = screenCentre.y + radius * sin(radians + arcStartPoint);
CGPoint target = CGPointMake(buttonCentre.x, buttonCentre.y);
CGFloat x = screenCentre.x - buttonSize / 2;
CGFloat y = screenCentre.y - buttonSize / 2;
CGFloat wide = buttonSize;
CGFloat high = buttonSize;
UIButton *circleButton = [[UIButton alloc] initWithFrame:CGRectMake(x, y, wide, high)];
[circleButton setTag:i];
circleButton.clipsToBounds = YES;
circleButton.layer.masksToBounds = NO;
circleButton.layer.borderWidth = 0.25f;
circleButton.layer.cornerRadius = buttonSize/2;
circleButton.layer.borderColor = [UIColor blackColor].CGColor;
circleButton.backgroundColor = UIColor.whiteColor;
[circleButton setTitle:[NSString stringWithFormat:#"%i", i] forState:UIControlStateNormal];
[self addSubview:circleButton];
// animation 1
[UIView animateWithDuration:0.5 animations:^{
circleButton.transform = CGAffineTransformMakeScale(1.0, 1.0);
circleButton.center = screenCentre;
}
completion:^(BOOL finished){}];
// animation 2
[UIView animateWithDuration:1.0f animations:^{
circleButton.transform = CGAffineTransformIdentity;
circleButton.center = target;
}
completion:^(BOOL finished){}];
}
and here is the CustomView
CustomView.m
#import <UIKit/UIKit.h>
#import "CustomView.h"
#implementation CustomView : UIView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:[UIScreen mainScreen].bounds];
if (self) {
self.translatesAutoresizingMaskIntoConstraints = true;
self.backgroundColor = [UIColor lightGrayColor];
buttonCount = 5; //16;
buttonSize = 80; //41;
radius = 68; //105;
[self circleOfButtons];
}
return self;
}
CustomView.h
#import <UIKit/UIKit.h>
#interface CustomView : UIView {
CGPoint screenCentre, buttonCentre;
float radius, radians, buttonSize;
int buttonCount;
}
ViewController.m
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGRect rect = [UIScreen mainScreen].bounds;
float statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
CGRect screenFrame = CGRectMake(0, statusBarHeight, rect.size.width, rect.size.height - statusBarHeight);
self.view = [[UIView alloc] initWithFrame: screenFrame];
CustomView *cv = [[CustomView alloc]initWithFrame:screenFrame];
[self.view addSubview:cv];
}

How should I programatically change content in a linked activity on button click?

This question may seem simple but please hear me out. I've made a huge mistake my project contains 300+ activities that are in a single storyboard and I've just recently figured out that Xcode is terrible at handling that. So what I would like to do is to have my different content which is now currently displayed in separate activities written into the code so that I only have to have the minimum amount of activities in my storyboard. This may be hard to follow I apologize.
In my head here is how it should work. I'l give an example.
In my app I have a section called MAPS, from the homepage you click on the maps button and it brings you to a menu which has buttons with the maps names on them. As you would expect the name of the map on the button brings you the the activity with said map. The problem is that each map is in a separate activity that is linked too inside the storyboard.
What I would like to do is programatically call each separate activity from within one activity in my storyboard effectively replacing 20+ activities with 1.
Here is what my map view controllers look like (they're actually .png images with a scrollview)
MapRoute1.h
#import "ViewController.h"
#interface MapRoute1 : ViewController <UIScrollViewDelegate>
#property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
#property (nonatomic, strong) UIImageView *imageView;
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
#end
MapRoute1.m
#import "MapRoute1.h"
#interface MapRoute1 ()
#end
#implementation MapRoute1
#synthesize imageView = _imageView;
#synthesize scrollView = _scrollView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;
if (contentsFrame.size.width < boundsSize.width) {
contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
contentsFrame.origin.x = 0.0f;
}
if (contentsFrame.size.height < boundsSize.height) {
contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
contentsFrame.origin.y = 0.0f;
}
self.imageView.frame = contentsFrame;
}
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
// 1
CGPoint pointInView = [recognizer locationInView:self.imageView];
// 2
CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
// 3
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
// 4
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
}
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
// Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
}
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
// Return the view that you want to zoom
return self.imageView;
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so you need to re-center the contents
[self centerScrollViewContents];
}
- (void)viewDidLoad {
[super viewDidLoad];
// 1
UIImage *image = [UIImage imageNamed:#"map_route1.png"];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.scrollView addSubview:self.imageView];
// 2
self.scrollView.contentSize = image.size;
// 3
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];
UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewTwoFingerTapped:)];
twoFingerTapRecognizer.numberOfTapsRequired = 1;
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// 4
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
CGFloat minScale = MIN(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
// 5
self.scrollView.maximumZoomScale = 1.5f;
self.scrollView.zoomScale = minScale;
// 6
[self centerScrollViewContents];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
So as you can tell the only part that changes in each separate map is this line of code:
UIImage *image = [UIImage imageNamed:#"map_route1.png"];
What I would like to do is have some sort of list of all my images and when the button is clicked for that map(image) it will replace this line of code with the image name or Map that the button should link too. The overall code and everything should only be one activity in my storyboard that is linked to be each button in the MAPS menu.
Currently all activities are linked by a simple "control click-drag" in the storyboard and are contained inside of a navigation controller which provides back navigation.
I would like to keep the back navigation with the navigation controller but simply replace the image inside of the scrollview.
Thanks in advance!
-Derek

setNeedDisplay still does not work [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Previously I asked a question in stackoverflow and that is about setNeedsDisplay cannot work.
I am not a lazy guy and I have tried everything but it still cannot work.
Maybe I cannot find where is the problem.
Can anyone help me to find out the problem?
//viewcontroller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize frequency;
- (void)viewDidLoad
{
hi = [[cro alloc]init];
[self.view addSubview:hi];
CGFloat fu = frequency.value;
[hi changefreq:fu];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)change:(id)sender
{
CGFloat fu = frequency.value;
[hi changefreq:fu];
}
#end
//cro.m
#import "cro.h"
#implementation cro
#synthesize fffff;
CGFloat freee;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineJoinRound);
NSLog(#"hi i am here %f",fffff);
const CGFloat amplitude = 200/2;
for(CGFloat x = 0; x < 600; x += 0.5)
{
CGFloat y = amplitude * cosf(2 * M_PI * (x / 600) * freee) + 200;
if(x == 0)
CGContextMoveToPoint(context, x, y);
else
CGContextAddLineToPoint(context, x, y);
}
CGContextSetStrokeColorWithColor(context, [[UIColor greenColor] CGColor]);
self.clearsContextBeforeDrawing = NO;
CGContextStrokePath(context);
}
-(void)changefreq:(CGFloat)fre
{
NSLog(#"fre= %f",fre);
freee = fre;
[self setNeedsDisplay];
}
#end
here is the project
http://www.sendspace.com/file/lzt4b0
You initialize your view by calling [[cro alloc] init]. Since you're not calling initWithFrame: this will result in a view with zero width and height. Calling setNeedsDisplay on such a view has no effect because there is nothing to display.
Change your first line in viewDidLoad to something like
hi = [[cro alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
(adjust the size as needed)
Alternatively, you might want to use the cro instance that you already have in your storyboard instead of instantiating a new one. The storyboard instance is the one you're seeing, the hi instance is actually invisible with your current code. (btw, if you expect other people to read your code, you might want to start using sensible variable and class names.)
The following changes in ViewController.m ViewdidLoad method will do the fix
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
hi = [[cro alloc]initWithFrame:CGRectMake(201, 58, 414, 571)];
[self.view addSubview:hi];
CGFloat fu = frequency.value;
[hi changefreq:fu];
}
If you do the following it works for me:
Use property instead of ivar
Now you access your cro instance through self.cro
Connect in your storyboard the view controller's outlet cro (there is already a property named like this in your uploaded project) with the subview
Remove in -[ViewController viewDidLoad:] the lines where you create a new cro instance and add it as a subview to self
Use in your cro-class the ivar fffff in -[cro changefreq:] and -[cro drawRect:]
(It's all copied from your uploaded project only with my changes)
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet cro *cro;
#property (strong, nonatomic) IBOutlet UISlider *frequency;
-(IBAction)change:(id)sender;
#end
#implementation ViewController
#synthesize frequency;
- (void)viewDidLoad
{
CGFloat fu = frequency.value;
[self.cro changefreq:fu];
//NSLog(#"%f",hi.fffff);
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)change:(id)sender
{
CGFloat fu = frequency.value;
[self.cro changefreq:fu];
//[hi setNeedsDisplay];
}
#end
#interface cro : UIView
#property (nonatomic) CGFloat fffff;
-(void)changefreq:(CGFloat)fre;
#end
#implementation cro
#synthesize fffff;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
fffff = 15;
// Initialization code
}
return self;
}
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1);
CGContextSetLineJoin(context, kCGLineJoinRound);
NSLog(#"hi i am here %f",fffff);
const CGFloat amplitude = 200/2;
for(CGFloat x = 0; x < 600; x += 0.5)
{
CGFloat y = amplitude * cosf(2 * M_PI * (x / 600) * fffff) + 200;
if(x == 0)
CGContextMoveToPoint(context, x, y);
else
CGContextAddLineToPoint(context, x, y);
}
CGContextSetStrokeColorWithColor(context, [[UIColor greenColor] CGColor]);
self.clearsContextBeforeDrawing = NO;
CGContextStrokePath(context);
}
-(void)changefreq:(CGFloat)fre
{
NSLog(#"fre= %f",fre);
fffff = fre;
[self setNeedsDisplay];
}
#end

UIButton with timer

I'd like to create a UIButton with a timer as shown in the attached picture. How do I go about it?
Will adding MBProgressHUD to the UIButton help?
(source: efytimes.com)
I can show you how to draw the circle that represents the timer, I'm sure you can take it from there. Here's the code:
TimerButton.h
#import <UIKit/UIKit.h>
#interface TimerButton : UIView
{
float currentAngle;
float currentTime;
float timerLimit;
NSTimer *timer;
}
#property float currentAngle;
-(void)stopTimer;
-(void)startTimerWithTimeLimit:(int)tl;
#end
TimerButton.m
#import "TimerButton.h"
#implementation TimerButton
#define DEGREES_TO_RADIANS(degrees) ((3.14159265359 * degrees)/ 180)
#define TIMER_STEP .01
#synthesize currentAngle;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.backgroundColor = [UIColor clearColor];
currentAngle = 0;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
UIBezierPath* aPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50)
radius:45
startAngle:DEGREES_TO_RADIANS(0)
endAngle:DEGREES_TO_RADIANS(currentAngle)
clockwise:YES];
[[UIColor redColor] setStroke];
aPath.lineWidth = 5;
[aPath stroke];
}
-(void)startTimerWithTimeLimit:(int)tl
{
timerLimit = tl;
timer = [NSTimer scheduledTimerWithTimeInterval:TIMER_STEP target:self selector:#selector(updateTimerButton:) userInfo:nil repeats:YES];
}
-(void)stopTimer
{
[timer invalidate];
}
-(void)updateTimerButton:(NSTimer *)timer
{
currentTime += TIMER_STEP;
currentAngle = (currentTime/timerLimit) * 360;
if(currentAngle >= 360) [self stopTimer];
[self setNeedsDisplay];
}
#end
Give that a try and let me know if you need further explanation.

Resources