iOS自定义控件之倒计时器

本文将介绍一个拥有圆环形状的倒计时器,涉及到的主要内容有路径绘制、动画、多线程和一些时间单位的相互转换,虽然这么多的内容看起来会很复杂,但跟着小编一步一步来实现,你就会发现原来可以这么简单。所以这里不再对这个控件作过多的陈述了,如果这样做的话是会在开头就出现一段不小的篇幅,我想这样你们是不会耐心看下去的(好吧,我承认是我太懒,没想到小学就学会的造句在今天看来是如此的艰难),不瞎扯了,直接上图吧。

----------------------------------------------------------------------------------------还是分割线------------------------------------------------------------------------------

完成效果图


----------------------------------------------------------------------------------------还是分割线------------------------------------------------------------------------------

1. 创建我们的GYCCircularTimer类

  • 在Xcode里新建一个类,取名GYCCircularTimer,继承UIView
  • 打开GYCCircularTimer.h文件,先从添加属性成员开始,想想一个倒计时器有哪些属性?看图!得有个用于显示它名字的Label吧、还得有个显示剩余时间的Label,圆环这样形状的用半径就可以表示出它的大小了吧、前面的环形圈和后面的环形圈,想想都头大......上代码吧:
[objc]  view plain  copy
  1. @property (assign, nonatomic) CGFloat radius;  
  2.   
  3. @property (strongnonatomicCAShapeLayer *foregroundLayer;  
  4. @property (assign, nonatomic) CGFloat foregroundPathWidth;  
  5. @property (strongnonatomicUIColor *foregroundPathColor;  
  6.   
  7. @property (strongnonatomicCAShapeLayer *backgroundLayer;  
  8. @property (assign, nonatomic) CGFloat backgroundPathWidth;  
  9. @property (strongnonatomicUIColor *backgroundPathColor;  
  10.   
  11. @property (strongnonatomicUIView *contentView;  
  12. @property (strongnonatomicUILabel *titleLabel;  
  13. @property (strongnonatomicUILabel *timeLabel;  
  14.   
  15. @property (assign, nonatomicid <CircularTimerDelegate> delegata;  

我没说让读者就照这样写,怎么声明一个类的属性成员我想还是挺有讲究的吧,所以你大可按你自己的方式来定义类成员,但你够懒的话就直接Copy吧,这里另外定义了一个delegate,后面会用到,这里先添加以下代码到文件顶部位置:
[objc]  view plain  copy
  1. @protocol CircularTimerDelegate <NSObject>  
  2.   
  3. - (void)finishedCountDown;  
  4.   
  5. @end  

那么接下来就是公共接口的方法了(说明都写在注释里了,对就是给你看绿色字体来着):
[objc]  view plain  copy
  1. /** 
  2.  *  初始化CircularTimer 
  3.  * 
  4.  *  @param center      圆心位置 
  5.  *  @param r           半径 
  6.  *  @param width       线宽 
  7.  *  @param color       颜色 
  8.  *  @param isClockWise 是否顺时针 
  9.  *  @param t           标题 
  10.  *  @param h           小时 
  11.  *  @param m           分钟 
  12.  *  @param s           秒 
  13.  * 
  14.  *  @return CircularTimer实例 
  15.  */  
  16. - (instancetype)initWithCenter:(CGPoint)center  
  17.                         Radius:(CGFloat)r  
  18.                      PathWidth:(CGFloat )width  
  19.                      PathColor:(UIColor *)color  
  20.                      Clockwise:(BOOL)isClockWise  
  21.                          Title:(NSString *)t  
  22.                           Hour:(NSUInteger)h  
  23.                         Minute:(NSUInteger)m  
  24.                         Second:(NSUInteger)s;  
  25. /** 
  26.  *  开始记时 
  27.  */  
  28. - (void)startCount;  
  29.   
  30. /** 
  31.  *  暂停 
  32.  */  
  33. - (void)pauseCount;  
  34.   
  35. /** 
  36.  *  继续 
  37.  */  
  38. - (void)resumeCount;  
  39.   
  40. /** 
  41.  *  停止 
  42.  */  
  43. - (void)stopCount;  
  44.   
  45. /** 
  46.  *  设置前景进度条 
  47.  * 
  48.  *  @param width 宽度 
  49.  *  @param color 颜色 
  50.  */  
  51. - (void)setForegroundProcessWidth:(CGFloat)width Color:(UIColor *)color;  
  52.   
  53. /** 
  54.  *  设置背景进度条 
  55.  * 
  56.  *  @param width 宽度 
  57.  *  @param color 颜色 
  58.  */  
  59. - (void)setBackgroundProcessWidth:(CGFloat)width Color:(UIColor *)color;  
  60.   
  61. /** 
  62.  *  设置标题 
  63.  * 
  64.  *  @param size  字体大小 
  65.  *  @param color 字体颜色 
  66.  */  
  67. - (void)setTitleFontSize:(CGFloat)size Color:(UIColor *)color;  
  68.   
  69. /** 
  70.  *  设置时间 
  71.  * 
  72.  *  @param size  字体大小 
  73.  *  @param color 字体颜色 
  74.  */  
  75. - (void)setTimeFontSize:(CGFloat)size Color:(UIColor *)color;  

  • 让我们看看.m文件里需要做的准备工作有哪些(当然是还是声明我们需要的变量啦):
[objc]  view plain  copy
  1. @interface GYCCircularTimer () {  
  2.     //一个环形路径变量  
  3.     UIBezierPath *path;  
  4.     //最初设置的总时长  
  5.     NSInteger duration;  
  6.     //剩余时间  
  7.     NSInteger remain;  
  8.       
  9.     //缓存倒计时器的标题文本内容  
  10.     NSString *title;  
  11.     //小时  
  12.     NSUInteger hour;  
  13.     //分钟  
  14.     NSUInteger minute;  
  15.     //秒  
  16.     NSUInteger second;  
  17.       
  18.     //用于倒计时的NSTimer对象变量  
  19.     NSTimer *countDownTimer;  
  20. }  
  21.   
  22. @end  

GYCCircularTimer类的准备工作到此结束,接下来就是具体的实现。

2. 那么开始实现吧

  • 这次我们首先来完成倒计时器里面控件的布局和基本属性设置,先来看看initCircularTimer方法吧:
[objc]  view plain  copy
  1. - (void)initCircularTimer {  
  2.     //设置背景环形圈  
  3.     self.backgroundLayer = [CAShapeLayer layer];  
  4.     [self.backgroundLayer setPath:[path CGPath]];  
  5.     [self.backgroundLayer setFillColor:[[UIColor clearColor] CGColor]];  
  6.     [self.backgroundLayer setStrokeColor:[self.backgroundPathColor CGColor]];  
  7.     [self.backgroundLayer setLineWidth:self.backgroundPathWidth];  
  8.     [self.layer addSublayer:self.backgroundLayer];  
  9.       
  10.     //设置前景环形圈  
  11.     self.foregroundLayer = [CAShapeLayer layer];  
  12.     [self.foregroundLayer setPath:[path CGPath]];  
  13.     [self.foregroundLayer setFillColor:[[UIColor clearColor] CGColor]];  
  14.     [self.foregroundLayer setStrokeColor:[self.foregroundPathColor CGColor]];  
  15.     [self.foregroundLayer setLineWidth:self.foregroundPathWidth];  
  16.     [self.foregroundLayer setStrokeEnd:0];  
  17.     [self.layer addSublayer:self.foregroundLayer];  
  18.       
  19.     //内容视图包括标题的显示和时间的倒数  
  20.     self.contentView = [[UIView alloc] initWithFrame:CGRectMake(00, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)/3)];  
  21.     [self.contentView setCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))];  
  22.     [self addSubview:self.contentView];  
  23.       
  24.     //设置标题的Label  
  25.     self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(00, CGRectGetWidth(self.contentView.bounds), CGRectGetHeight(self.contentView.bounds)/2)];  
  26.     [self.titleLabel setText:title];  
  27.     [self.titleLabel setTextColor:[UIColor darkGrayColor]];  
  28.     [self.titleLabel setTextAlignment:NSTextAlignmentCenter];  
  29.     [self.contentView addSubview:self.titleLabel];  
  30.       
  31.     //设置倒计时的Label  
  32.     self.timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(self.titleLabel.bounds), CGRectGetWidth(self.titleLabel.bounds), CGRectGetHeight(self.titleLabel.bounds))];  
  33.     //设置时间格式为00:00:00  
  34.     [self.timeLabel setText:[NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second]];  
  35.     [self.timeLabel setTextColor:[UIColor darkGrayColor]];  
  36.     [self.timeLabel setTextAlignment:NSTextAlignmentCenter];  
  37.     [self.contentView addSubview:self.timeLabel];  
  38. }  

  • 然后实现类的初始化方法initWithCenter:
[objc]  view plain  copy
  1. - (instancetype)initWithCenter:(CGPoint)center Radius:(CGFloat)r PathWidth:(CGFloat)width PathColor:(UIColor *)color Clockwise:(BOOL)isClockWise Title:(NSString *)t Hour:(NSUInteger)h Minute:(NSUInteger)m Second:(NSUInteger)s {  
  2.     self = [super initWithFrame:CGRectMake(center.x-r, center.y-r, r*2, r*2)];  
  3.     if (self) {  
  4.         //设置path的路径为一个环形,这里从0°开始是3点钟水平方向,增大角度是顺时针方向,那么要实现秒表从12点钟开始旋转的轨迹,就得把开始角度设置为-90°,结束角度设置270°,是否顺时针转动由参数isClockWise决定。  
  5.         path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)) radius:r startAngle:-0.5*M_PI endAngle:1.5*M_PI clockwise:isClockWise];  
  6.         //算出总的时长  
  7.         duration = h*3600 + m*60 + s;  
  8.         //刚开始的剩余时间就等于总时长  
  9.         remain = duration;  
  10.           
  11.         self.foregroundPathColor = color;  
  12.         self.foregroundPathWidth = width;  
  13.         self.backgroundPathColor = [UIColor darkGrayColor];  
  14.         self.backgroundPathWidth = width;  
  15.           
  16.         title = t;  
  17.         hour = h;  
  18.         minute = m;  
  19.         second = s;  
  20.           
  21.         [self initCircularTimer];  
  22.     }  
  23.       
  24.     return self;  
  25. }  

  • 顺便把这两个改变环形进度条宽度和颜色的也给做了:
[objc]  view plain  copy
  1. - (void)setForegroundProcessWidth:(CGFloat)width Color:(UIColor *)color {  
  2.     [self.foregroundLayer setLineWidth:width];  
  3.     [self.foregroundLayer setStrokeColor:[color CGColor]];  
  4. }  
  5.   
  6. - (void)setBackgroundProcessWidth:(CGFloat)width Color:(UIColor *)color {  
  7.     [self.backgroundLayer setLineWidth:width];  
  8.     [self.backgroundLayer setBackgroundColor:[color CGColor]];  
  9. }  

  • 接下来就是重点实现部分了,先从环形进度条的动画开始切入:
[objc]  view plain  copy
  1. #pragma mark - count methods  
  2.   
  3. - (void)startCount {  
  4.     //将总时长细分成小时、分钟和秒,并按规定格式00:00:00显示出来  
  5.     remain = duration;  
  6.     hour = (remain / 3600);  
  7.     minute = (remain / 60) % 60;  
  8.     second = (remain % 60);  
  9.     [self.timeLabel setText:[NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second]];  
  10.       
  11.     [self startAnimation];  
  12.       
  13.     if ([countDownTimer isValid]) {  
  14.         [countDownTimer invalidate];  
  15.     }  
  16.     //开辟一个线程用于执行countDown方法,每一秒执行一次并一直重复下去。  
  17.     countDownTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countDown) userInfo:nil repeats:YES];  
  18. }  
  19.   
  20. - (void)pauseCount {  
  21.     [self pauseAnimation];  
  22.       
  23.     [countDownTimer invalidate];  
  24. }  
  25.   
  26. - (void)resumeCount {  
  27.     [self resumeAnimation];  
  28.       
  29.     [countDownTimer fire];  
  30. }  
  31.   
  32. - (void)stopCount {  
  33.     remain = 0;  
  34.       
  35.     [self stopAnimation];  
  36.       
  37.     [countDownTimer invalidate];  
  38.     [self countDown];  
  39. }  
  40.   
  41. - (void)countDown {  
  42.     //剩余时间进行自减  
  43.     remain--;  
  44.     if (remain < 0) {  
  45.         //完成倒计时  
  46.         remain = 0;  
  47.         [countDownTimer invalidate];  
  48.           
  49.         [self.timeLabel setText:@"00:00:00"];  
  50.         //调用委托方法finishedCountDown  
  51.         [delegate performSelector:@selector(finishedCountDown)];  
  52.     }  
  53.     else {  
  54.         //将剩余时间转换成时分秒  
  55.         hour = (remain / 3600);  
  56.         minute = (remain / 60) % 60;  
  57.         second = (remain % 60);  
  58.           
  59.         //然后显示在timeLabel上  
  60.         [self.timeLabel setText:[NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second]];  
  61.     }  
  62.       
  63.     NSLog(@"%d", remain);  
  64. }  

基本内容到此都实现完成,还有两个方法setTitleFontSize和setTimeFontSize读者可以自己去试着编写(作者又偷懒了!怎么?就是这么任性...)。

3. 跑一遍

写到这其实发现内容也不多的吧......总算可以来看看成果了。
  • 新建一个视图控制器,打开.m文件,在顶部导入GYCCircularTimer.h文件后添加如下代码:
[objc]  view plain  copy
  1. @interface GYCCircularTimerViewController () <CircularTimerDelegate>{  
  2.     GYCCircularTimer *cTimer;  
  3.     UIButton *cButton;  
  4. }  


  • 再在viewDidLoad中进行编写,代码参照下面:
[objc]  view plain  copy
  1. cTimer = [[GYCCircularTimer alloc] initWithCenter:CGPointMake(160160)  
  2.                                                Radius:100  
  3.                                             PathWidth:5  
  4.                                             PathColor:[UIColor brownColor]  
  5.                                             Clockwise:YES  
  6.                                                 Title:@"完成倒计时"  
  7.                                                  Hour:0  
  8.                                                Minute:0  
  9.                                                Second:10];  
  10.     cTimer.delegate = self;  
  11.     [cTimer setBackgroundProcessWidth:11 Color:[UIColor darkGrayColor]];  
  12.     [cTimer setForegroundProcessWidth:9 Color:[UIColor whiteColor]];  
  13.     [cTimer.titleLabel setFont:[UIFont systemFontOfSize:12]];  
  14.     [cTimer.titleLabel setTextColor:[UIColor lightGrayColor]];  
  15.     [cTimer.titleLabel setBackgroundColor:[UIColor clearColor]];  
  16.     [cTimer.timeLabel setFont:[UIFont boldSystemFontOfSize:18]];  
  17.     [cTimer.timeLabel setTextColor:[UIColor darkGrayColor]];  
  18.     [cTimer.timeLabel setBackgroundColor:[UIColor clearColor]];  
  19.     [self.view addSubview:cTimer];  
  20.       
  21.     cButton = [[UIButton alloc] initWithFrame:CGRectMake(4032024040)];  
  22.     [cButton setTitle:@"开始" forState:UIControlStateNormal];  
  23.     [cButton setBackgroundColor:[UIColor darkGrayColor]];  
  24.     [cButton addTarget:self action:@selector(clickedToStart) forControlEvents:UIControlEventTouchUpInside];  
  25.     [self.view addSubview:cButton];  

  • 定义个开始倒计时和暂停的方法:
[objc]  view plain  copy
  1. - (void)clickedToStart {  
  2.     NSLog(@"开始记时...");  
  3.     [cTimer startCount];  
  4.       
  5.     [cButton setTitle:@"暂停" forState:UIControlStateNormal];  
  6.     [cButton setBackgroundColor:[UIColor lightGrayColor]];  
  7.     [cButton removeTarget:self action:@selector(clickedToStart) forControlEvents:UIControlEventTouchUpInside];  
  8.     [cButton addTarget:self action:@selector(clickedToPause) forControlEvents:UIControlEventTouchUpInside];  
  9. }  
  10.   
  11. - (void)clickedToPause {  
  12.     NSLog(@"暂停");  
  13.     [cTimer pauseCount];  
  14.       
  15.     [cButton setTitle:@"重新开始" forState:UIControlStateNormal];  
  16.     [cButton setBackgroundColor:[UIColor darkGrayColor]];  
  17.     [cButton removeTarget:self action:@selector(clickedToPause) forControlEvents:UIControlEventTouchUpInside];  
  18.     [cButton addTarget:self action:@selector(clickedToStart) forControlEvents:UIControlEventTouchUpInside];  
  19. }  

  • 实现GYCCircularTime类的代理方法:
[objc]  view plain  copy
  1. #pragma mark - circular timer delegate  
  2.   
  3. - (void)finishedCountDown {  
  4.     NSLog(@"完成倒计时");  
  5.       
  6.     [cButton setTitle:@"开始" forState:UIControlStateNormal];  
  7.     [cButton setBackgroundColor:[UIColor darkGrayColor]];  
  8.     [cButton removeTarget:self action:@selector(clickedToPause) forControlEvents:UIControlEventTouchUpInside];  
  9.     [cButton addTarget:self action:@selector(clickedToStart) forControlEvents:UIControlEventTouchUpInside];  
  10. }  

完成后,就可以编译运行!

----------------------------------------------------------------------------------------华丽分割线------------------------------------------------------------------------------
文章有错之处,自行改之,不要指正(就是这么傲娇还带卖萌的...啧啧),让“我”安静得当一个美男子吧!
完!!!

源码下载地址: https://github.com/ganyuchuan/GYCBox,详见GYCCircularTimer。
请别猛戳!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值