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.
Related
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!!!
Is that possible to give each pagination dots with different colors? Suppose if i have 4 dots means is that possible to give 4 different color for each dots in ios?
http://muthesblog.blogspot.in/2011/11/custompagecontrol.html
by using the above code in the blog you will get different color dots as shown below image with yellow rectangle.
For your convenience adding the code
implemetation of the custompagecontrol
PageControl *<pageControl> = [[[PageControl alloc] initWithFrame:f] autorelease];
<pageControl>.numberOfPages = 5;
<pageControl>.currentPage = 1;
<pageControl>.delegate = self;
[self.view addSubview:<pageControl>];
/// .h file
#import <UIKit/UIKit.h>
#protocol PageControlDelegate;
#interface PageControl : UIView {
#private
NSInteger _currentPage;
NSInteger _numberOfPages;
UIColor *dotColorCurrentPage;
UIColor *dotColorOtherPage;
NSObject<PageControlDelegate> *delegate;
}
// Set these to control the PageControl.
#property (nonatomic) NSInteger currentPage;
#property (nonatomic) NSInteger numberOfPages;
// Customize these as well as the backgroundColor property.
#property (nonatomic, retain) UIColor *dotColorCurrentPage;
#property (nonatomic, retain) UIColor *dotColorOtherPage;
// Optional delegate for callbacks when user taps a page dot.
#property (nonatomic, assign) NSObject<PageControlDelegate> *delegate;
#end
#protocol PageControlDelegate<NSObject>
#optional
- (void)pageControlPageDidChange:(PageControl *)pageControl;
#end
//.m file
#import "PageControl.h"
// Tweak these or make them dynamic.
#define kDotDiameter 10.0
#define kDotSpacer 7.0
#implementation PageControl
#synthesize dotColorCurrentPage;
#synthesize dotColorOtherPage;
#synthesize delegate;
- (NSInteger)currentPage{
return _currentPage;
}
- (void)setCurrentPage:(NSInteger)page{
_currentPage = MIN(MAX(0, page), _numberOfPages-1);
[self setNeedsDisplay];
}
- (NSInteger)numberOfPages{
return _numberOfPages;
}
- (void)setNumberOfPages:(NSInteger)pages{
_numberOfPages = MAX(0, pages);
_currentPage = MIN(MAX(0, _currentPage), _numberOfPages-1);
[self setNeedsDisplay];
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame]))
{
// Default colors.
self.backgroundColor = [UIColor clearColor];
self.dotColorCurrentPage = [UIColor greenColor];
self.dotColorOtherPage = [UIColor whiteColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetAllowsAntialiasing(context, true);
CGRect currentBounds = self.bounds;
CGFloat dotsWidth = self.numberOfPages*kDotDiameter + MAX(0, self.numberOfPages-1)*kDotSpacer;
CGFloat x = CGRectGetMidX(currentBounds)-dotsWidth/2;
CGFloat y = CGRectGetMidY(currentBounds)-kDotDiameter/2;
for (int i=0; i<_numberOfPages; i++)
{
CGRect circleRect = CGRectMake(x, y, kDotDiameter, kDotDiameter);
if (i == _currentPage)
{
CGContextSetFillColorWithColor(context, self.dotColorCurrentPage.CGColor);
}
else
{
if(i==0) {
CGContextSetFillColorWithColor(context, [UIColor magentaColor].CGColor);
} else if(i==1) {
CGContextSetFillColorWithColor(context, [UIColor orangeColor].CGColor);
} else if (i==2) {
CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
} else if (i==3) {
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
} else if (i==4) {
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
}
}
CGContextFillEllipseInRect(context, circleRect);
x += kDotDiameter + kDotSpacer;
}
}
- (void)dealloc {
[dotColorCurrentPage release];
[dotColorOtherPage release];
[super dealloc];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.delegate) return;
CGPoint touchPoint = [[[event touchesForView:self] anyObject] locationInView:self];
CGFloat dotSpanX = self.numberOfPages*(kDotDiameter + kDotSpacer);
CGFloat dotSpanY = kDotDiameter + kDotSpacer;
CGRect currentBounds = self.bounds;
CGFloat x = touchPoint.x + dotSpanX/2 - CGRectGetMidX(currentBounds);
CGFloat y = touchPoint.y + dotSpanY/2 - CGRectGetMidY(currentBounds);
if ((x<0) || (x>dotSpanX) || (y<0) || (y>dotSpanY)) return;
self.currentPage = floor(x/(kDotDiameter+kDotSpacer));
if ([self.delegate respondsToSelector:#selector(pageControlPageDidChange:)])
{
[self.delegate pageControlPageDidChange:self];
}
}
#end
There is no native way to change the colour of each page indicator due to iOS restrictions.
You can however use the following code to change the colour of the currently selected indicator and that of the rest which might be a good compromise.
pageControl.pageIndicatorTintColor = [UIColor purpleColor];
pageControl.currentPageIndicatorTintColor = [UIColor magentaColor];
If this functionality is essential for your app I would advise creating a custom page indicator that serves your purpose or maybe find a library online that allows you to to do this.
I hope that answers your questions :)
Check this below code
pageControl.pageIndicatorTintColor = [UIColor purpleColor];
pageControl.currentPageIndicatorTintColor = [UIColor magentaColor];
or Please refer this link
http://stackoverflow.com/questions/2942636/how-can-i-change-the-color-of-pagination-dots-of-uipagecontrol
I want to make a customized UIPopover with white arrow and white background rectangle (no round corner). However, when the popover window displayed, the arrow is missing. What should I do to fixed this error. Thanks a lot!
CustomizedPopoverController.h:
#import <UIKit/UIKit.h>
#interface CustomizedPopoverController: UIPopoverBackgroundView
#property (nonatomic, readwrite) CGFloat arrowOffset;
#property (nonatomic, readwrite) UIPopoverArrowDirection arrowDirection;
+ (CGFloat)arrowHeight;
+ (CGFloat)arrowBase;
+ (UIEdgeInsets)contentViewInsets;
#end
CustomizedPopoverController.m:
#import "CustomizedPopoverController.h"
#implementation CustomizedPopoverController
#synthesize arrowDirection = _arrowDirection;
#synthesize arrowOffset = _arrowOffset;
#define ArrowBase 30.0f
#define ArrowHeight 15.0f
#define BorderInset 10.0f
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self setBackgroundColor:[UIColor whiteColor]];
}
return self;
}
- (CGFloat) arrowOffset {
return _arrowOffset;
}
- (void) setArrowOffset:(CGFloat)arrowOffset {
_arrowOffset = arrowOffset;
}
- (UIPopoverArrowDirection)arrowDirection {
return _arrowDirection;
}
- (void)setArrowDirection:(UIPopoverArrowDirection)arrowDirection {
_arrowDirection = arrowDirection;
}
+(UIEdgeInsets)contentViewInsets{
return UIEdgeInsetsMake(BorderInset, BorderInset, BorderInset, BorderInset);
}
+(CGFloat)arrowHeight{
return ArrowHeight;
}
+(CGFloat)arrowBase{
return ArrowBase;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat _height = self.frame.size.height;
CGFloat _width = self.frame.size.width;
CGFloat _left = 0.0;
CGFloat _top = 0.0;
CGFloat _coordinate = 0.0;
CGAffineTransform _rotation = CGAffineTransformIdentity;
switch (self.arrowDirection) {
case UIPopoverArrowDirectionUp:
_top += ArrowHeight;
_height -= ArrowHeight;
_coordinate = ((self.frame.size.width / 2) + self.arrowOffset) - (ArrowBase/2);
break;
case UIPopoverArrowDirectionDown:
_height -= ArrowHeight;
_coordinate = ((self.frame.size.width / 2) + self.arrowOffset) - (ArrowBase/2);
_rotation = CGAffineTransformMakeRotation( M_PI );
break;
case UIPopoverArrowDirectionLeft:
_left += ArrowBase;
_width -= ArrowBase;
_coordinate = ((self.frame.size.height / 2) + self.arrowOffset) - (ArrowHeight/2);
_rotation = CGAffineTransformMakeRotation( -M_PI_2 );
break;
case UIPopoverArrowDirectionRight:
_width -= ArrowBase;
_coordinate = ((self.frame.size.height / 2) + self.arrowOffset)- (ArrowHeight/2);
_rotation = CGAffineTransformMakeRotation( M_PI_2 );
break;
}
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
Can Anyone help me? Give me some ideas to achieve this:)
make an UIView class declare in .h file
CGFloat startAngle;
CGFloat endAngle;
#property(assign) int percent;
Replace initWithFrame and drawRect method in .m class
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
// Determine our start and stop angles for the arc (in radians)
startAngle = M_PI * 1;
endAngle = startAngle + (M_PI * 2); }
return self;
}
- (void)drawRect:(CGRect)rect
{
NSString* textContent = [NSString stringWithFormat:#"%d", 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];
}
In your Controller .h import CornerRadious and declare
NSTimer *m_timer;
CornerRadious *cr;
In .m class in ViewDidLoadMethod
cr = [[CornerRadious alloc] initWithFrame:self.view.bounds];
cr.percent = 50;
[self.view addSubview:cr];
aslo make add in these method
- (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 (cr.percent > 0) {
cr.percent = cr.percent - 1;
[cr setNeedsDisplay];
}
else {
[m_timer invalidate];
m_timer = nil;
}
}
Hope it work
I would like to animate a straight line curving into a bezier curve (from "_" to "n"), is there a library somewhere that can help me to do it ?
I know how to draw a Bezier curve with UIBezierPath, I could redraw rapidly and progressively do the transformation, but if something already does that it would be cool :-)
I might do something with a CADisplayLink. For example, you could do this in your view controller using a CAShapeLayer, e.g.:
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface ViewController ()
#property (nonatomic) CFTimeInterval firstTimestamp;
#property (nonatomic, strong) CAShapeLayer *shapeLayer;
#property (nonatomic, strong) CADisplayLink *displayLink;
#property (nonatomic) NSUInteger loopCount;
#end
static CGFloat const kSeconds = 5.0;
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self addShapeLayer];
[self startDisplayLink];
}
- (void)addShapeLayer
{
self.shapeLayer = [CAShapeLayer layer];
self.shapeLayer.path = [[self pathAtInterval:0.0] CGPath];
self.shapeLayer.fillColor = [[UIColor clearColor] CGColor];
self.shapeLayer.lineWidth = 3.0;
self.shapeLayer.strokeColor = [[UIColor redColor] CGColor];
[self.view.layer addSublayer:self.shapeLayer];
}
- (void)startDisplayLink
{
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
self.loopCount++;
NSTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
self.shapeLayer.path = [[self pathAtInterval:elapsed] CGPath];
if (elapsed >= kSeconds)
{
[self stopDisplayLink];
self.shapeLayer.path = [[self pathAtInterval:0] CGPath];
self.statusLabel.text = [NSString stringWithFormat:#"loopCount = %.1f frames/sec", self.loopCount / kSeconds];
}
}
- (UIBezierPath *)pathAtInterval:(NSTimeInterval) interval
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, self.view.bounds.size.height / 2.0)];
CGFloat fractionOfSecond = interval - floor(interval);
CGFloat yOffset = self.view.bounds.size.height * sin(fractionOfSecond * M_PI * 2.0);
[path addCurveToPoint:CGPointMake(self.view.bounds.size.width, self.view.bounds.size.height / 2.0)
controlPoint1:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 - yOffset)
controlPoint2:CGPointMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height / 2.0 + yOffset)];
return path;
}
#end
Alternatively, if you wanted to do it by subclassing UIView, you could do it like so:
#import "View.h"
#import <QuartzCore/QuartzCore.h>
#interface View ()
#property (nonatomic, strong) CADisplayLink *displayLink;
#property (nonatomic) CFTimeInterval firstTimestamp;
#property (nonatomic) CFTimeInterval displayLinkTimestamp;
#property (nonatomic) NSUInteger loopCount;
#end
static CGFloat const kSeconds = 5.25;
#implementation View
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self startDisplayLink];
}
return self;
}
- (void)startDisplayLink
{
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)stopDisplayLink
{
[self.displayLink invalidate];
self.displayLink = nil;
}
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
self.displayLinkTimestamp = displayLink.timestamp;
self.loopCount++;
[self setNeedsDisplayInRect:self.bounds];
NSTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
if (elapsed >= kSeconds)
{
[self stopDisplayLink];
self.displayLinkTimestamp = self.firstTimestamp + kSeconds;
[self setNeedsDisplayInRect:self.bounds];
self.statusLabel.text = [NSString stringWithFormat:#"loopCount = %.1f frames/sec", self.loopCount / kSeconds];
}
}
- (UIBezierPath *)pathAtInterval:(NSTimeInterval) interval
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(0, self.bounds.size.height / 2.0)];
CGFloat fractionOfSecond = interval - floor(interval);
CGFloat yOffset = self.bounds.size.height * sin(fractionOfSecond * M_PI * 2.0);
[path addCurveToPoint:CGPointMake(self.bounds.size.width, self.bounds.size.height / 2.0)
controlPoint1:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0 - yOffset)
controlPoint2:CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0 + yOffset)];
return path;
}
- (void)drawRect:(CGRect)rect
{
NSTimeInterval elapsed = (self.displayLinkTimestamp - self.firstTimestamp);
UIBezierPath *path = [self pathAtInterval:elapsed];
[[UIColor redColor] setStroke];
path.lineWidth = 3.0;
[path stroke];
}
#end
I test both the subclassed UIView as well as the view controller, and they both resulted in roughly 60 frames per second.