iOS自定义控件之倒计时器

来源:互联网 发布:英国留学移民知乎 编辑:程序博客网 时间:2024/04/28 18:31

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

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

完成效果图


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

1. 创建我们的GYCCircularTimer类

  • 在Xcode里新建一个类,取名GYCCircularTimer,继承UIView
  • 打开GYCCircularTimer.h文件,先从添加属性成员开始,想想一个倒计时器有哪些属性?看图!得有个用于显示它名字的Label吧、还得有个显示剩余时间的Label,圆环这样形状的用半径就可以表示出它的大小了吧、前面的环形圈和后面的环形圈,想想都头大......上代码吧:
@property (assign, nonatomic) CGFloat radius;@property (strong, nonatomic) CAShapeLayer *foregroundLayer;@property (assign, nonatomic) CGFloat foregroundPathWidth;@property (strong, nonatomic) UIColor *foregroundPathColor;@property (strong, nonatomic) CAShapeLayer *backgroundLayer;@property (assign, nonatomic) CGFloat backgroundPathWidth;@property (strong, nonatomic) UIColor *backgroundPathColor;@property (strong, nonatomic) UIView *contentView;@property (strong, nonatomic) UILabel *titleLabel;@property (strong, nonatomic) UILabel *timeLabel;@property (assign, nonatomic) id <CircularTimerDelegate> delegata;

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

那么接下来就是公共接口的方法了(说明都写在注释里了,对就是给你看绿色字体来着):
/** *  初始化CircularTimer * *  @param center      圆心位置 *  @param r           半径 *  @param width       线宽 *  @param color       颜色 *  @param isClockWise 是否顺时针 *  @param t           标题 *  @param h           小时 *  @param m           分钟 *  @param s           秒 * *  @return CircularTimer实例 */- (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;/** *  开始记时 */- (void)startCount;/** *  暂停 */- (void)pauseCount;/** *  继续 */- (void)resumeCount;/** *  停止 */- (void)stopCount;/** *  设置前景进度条 * *  @param width 宽度 *  @param color 颜色 */- (void)setForegroundProcessWidth:(CGFloat)width Color:(UIColor *)color;/** *  设置背景进度条 * *  @param width 宽度 *  @param color 颜色 */- (void)setBackgroundProcessWidth:(CGFloat)width Color:(UIColor *)color;/** *  设置标题 * *  @param size  字体大小 *  @param color 字体颜色 */- (void)setTitleFontSize:(CGFloat)size Color:(UIColor *)color;/** *  设置时间 * *  @param size  字体大小 *  @param color 字体颜色 */- (void)setTimeFontSize:(CGFloat)size Color:(UIColor *)color;

  • 让我们看看.m文件里需要做的准备工作有哪些(当然是还是声明我们需要的变量啦):
@interface GYCCircularTimer () {    //一个环形路径变量    UIBezierPath *path;    //最初设置的总时长    NSInteger duration;    //剩余时间    NSInteger remain;        //缓存倒计时器的标题文本内容    NSString *title;    //小时    NSUInteger hour;    //分钟    NSUInteger minute;    //秒    NSUInteger second;        //用于倒计时的NSTimer对象变量    NSTimer *countDownTimer;}@end

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

2. 那么开始实现吧

  • 这次我们首先来完成倒计时器里面控件的布局和基本属性设置,先来看看initCircularTimer方法吧:
- (void)initCircularTimer {    //设置背景环形圈    self.backgroundLayer = [CAShapeLayer layer];    [self.backgroundLayer setPath:[path CGPath]];    [self.backgroundLayer setFillColor:[[UIColor clearColor] CGColor]];    [self.backgroundLayer setStrokeColor:[self.backgroundPathColor CGColor]];    [self.backgroundLayer setLineWidth:self.backgroundPathWidth];    [self.layer addSublayer:self.backgroundLayer];        //设置前景环形圈    self.foregroundLayer = [CAShapeLayer layer];    [self.foregroundLayer setPath:[path CGPath]];    [self.foregroundLayer setFillColor:[[UIColor clearColor] CGColor]];    [self.foregroundLayer setStrokeColor:[self.foregroundPathColor CGColor]];    [self.foregroundLayer setLineWidth:self.foregroundPathWidth];    [self.foregroundLayer setStrokeEnd:0];    [self.layer addSublayer:self.foregroundLayer];        //内容视图包括标题的显示和时间的倒数    self.contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds)/3)];    [self.contentView setCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds))];    [self addSubview:self.contentView];        //设置标题的Label    self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.contentView.bounds), CGRectGetHeight(self.contentView.bounds)/2)];    [self.titleLabel setText:title];    [self.titleLabel setTextColor:[UIColor darkGrayColor]];    [self.titleLabel setTextAlignment:NSTextAlignmentCenter];    [self.contentView addSubview:self.titleLabel];        //设置倒计时的Label    self.timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(self.titleLabel.bounds), CGRectGetWidth(self.titleLabel.bounds), CGRectGetHeight(self.titleLabel.bounds))];    //设置时间格式为00:00:00    [self.timeLabel setText:[NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second]];    [self.timeLabel setTextColor:[UIColor darkGrayColor]];    [self.timeLabel setTextAlignment:NSTextAlignmentCenter];    [self.contentView addSubview:self.timeLabel];}

  • 然后实现类的初始化方法initWithCenter:
- (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 {    self = [super initWithFrame:CGRectMake(center.x-r, center.y-r, r*2, r*2)];    if (self) {        //设置path的路径为一个环形,这里从0°开始是3点钟水平方向,增大角度是顺时针方向,那么要实现秒表从12点钟开始旋转的轨迹,就得把开始角度设置为-90°,结束角度设置270°,是否顺时针转动由参数isClockWise决定。        path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)) radius:r startAngle:-0.5*M_PI endAngle:1.5*M_PI clockwise:isClockWise];        //算出总的时长        duration = h*3600 + m*60 + s;        //刚开始的剩余时间就等于总时长        remain = duration;                self.foregroundPathColor = color;        self.foregroundPathWidth = width;        self.backgroundPathColor = [UIColor darkGrayColor];        self.backgroundPathWidth = width;                title = t;        hour = h;        minute = m;        second = s;                [self initCircularTimer];    }        return self;}

  • 顺便把这两个改变环形进度条宽度和颜色的也给做了:
- (void)setForegroundProcessWidth:(CGFloat)width Color:(UIColor *)color {    [self.foregroundLayer setLineWidth:width];    [self.foregroundLayer setStrokeColor:[color CGColor]];}- (void)setBackgroundProcessWidth:(CGFloat)width Color:(UIColor *)color {    [self.backgroundLayer setLineWidth:width];    [self.backgroundLayer setBackgroundColor:[color CGColor]];}

  • 接下来就是重点实现部分了,先从环形进度条的动画开始切入:
#pragma mark - count methods- (void)startCount {    //将总时长细分成小时、分钟和秒,并按规定格式00:00:00显示出来    remain = duration;    hour = (remain / 3600);    minute = (remain / 60) % 60;    second = (remain % 60);    [self.timeLabel setText:[NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second]];        [self startAnimation];        if ([countDownTimer isValid]) {        [countDownTimer invalidate];    }    //开辟一个线程用于执行countDown方法,每一秒执行一次并一直重复下去。    countDownTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countDown) userInfo:nil repeats:YES];}- (void)pauseCount {    [self pauseAnimation];        [countDownTimer invalidate];}- (void)resumeCount {    [self resumeAnimation];        [countDownTimer fire];}- (void)stopCount {    remain = 0;        [self stopAnimation];        [countDownTimer invalidate];    [self countDown];}- (void)countDown {    //剩余时间进行自减    remain--;    if (remain < 0) {        //完成倒计时        remain = 0;        [countDownTimer invalidate];                [self.timeLabel setText:@"00:00:00"];        //调用委托方法finishedCountDown        [delegate performSelector:@selector(finishedCountDown)];    }    else {        //将剩余时间转换成时分秒        hour = (remain / 3600);        minute = (remain / 60) % 60;        second = (remain % 60);                //然后显示在timeLabel上        [self.timeLabel setText:[NSString stringWithFormat:@"%02d:%02d:%02d", hour, minute, second]];    }        NSLog(@"%d", remain);}

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

3. 跑一遍

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


  • 再在viewDidLoad中进行编写,代码参照下面:
cTimer = [[GYCCircularTimer alloc] initWithCenter:CGPointMake(160, 160)                                               Radius:100                                            PathWidth:5                                            PathColor:[UIColor brownColor]                                            Clockwise:YES                                                Title:@"完成倒计时"                                                 Hour:0                                               Minute:0                                               Second:10];    cTimer.delegate = self;    [cTimer setBackgroundProcessWidth:11 Color:[UIColor darkGrayColor]];    [cTimer setForegroundProcessWidth:9 Color:[UIColor whiteColor]];    [cTimer.titleLabel setFont:[UIFont systemFontOfSize:12]];    [cTimer.titleLabel setTextColor:[UIColor lightGrayColor]];    [cTimer.titleLabel setBackgroundColor:[UIColor clearColor]];    [cTimer.timeLabel setFont:[UIFont boldSystemFontOfSize:18]];    [cTimer.timeLabel setTextColor:[UIColor darkGrayColor]];    [cTimer.timeLabel setBackgroundColor:[UIColor clearColor]];    [self.view addSubview:cTimer];        cButton = [[UIButton alloc] initWithFrame:CGRectMake(40, 320, 240, 40)];    [cButton setTitle:@"开始" forState:UIControlStateNormal];    [cButton setBackgroundColor:[UIColor darkGrayColor]];    [cButton addTarget:self action:@selector(clickedToStart) forControlEvents:UIControlEventTouchUpInside];    [self.view addSubview:cButton];

  • 定义个开始倒计时和暂停的方法:
- (void)clickedToStart {    NSLog(@"开始记时...");    [cTimer startCount];        [cButton setTitle:@"暂停" forState:UIControlStateNormal];    [cButton setBackgroundColor:[UIColor lightGrayColor]];    [cButton removeTarget:self action:@selector(clickedToStart) forControlEvents:UIControlEventTouchUpInside];    [cButton addTarget:self action:@selector(clickedToPause) forControlEvents:UIControlEventTouchUpInside];}- (void)clickedToPause {    NSLog(@"暂停");    [cTimer pauseCount];        [cButton setTitle:@"重新开始" forState:UIControlStateNormal];    [cButton setBackgroundColor:[UIColor darkGrayColor]];    [cButton removeTarget:self action:@selector(clickedToPause) forControlEvents:UIControlEventTouchUpInside];    [cButton addTarget:self action:@selector(clickedToStart) forControlEvents:UIControlEventTouchUpInside];}

  • 实现GYCCircularTime类的代理方法:
#pragma mark - circular timer delegate- (void)finishedCountDown {    NSLog(@"完成倒计时");        [cButton setTitle:@"开始" forState:UIControlStateNormal];    [cButton setBackgroundColor:[UIColor darkGrayColor]];    [cButton removeTarget:self action:@selector(clickedToPause) forControlEvents:UIControlEventTouchUpInside];    [cButton addTarget:self action:@selector(clickedToStart) forControlEvents:UIControlEventTouchUpInside];}

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

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

源码下载地址:https://github.com/ganyuchuan/GYCBox,详见GYCCircularTimer。
请别猛戳!!!
0 0