您的位置:首页 > 移动开发 > IOS开发

iOS自定义控件之倒计时器

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

完成效果图



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

1. 创建我们的GYCCircularTimer类

在Xcode里新建一个类,取名GYCCircularTimer,继承UIView
打开GYCCircularTimer.h文件,先从添加属性成员开始,想想一个倒计时器有哪些属性?看图!得有个用于显示它名字的Label吧、还得有个显示剩余时间的Label,圆环这样形状的用半径就可以表示出它的大小了吧、前面的环形圈和后面的环形圈,想想都头大......上代码吧:

[objc] view
plain copy

@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,后面会用到,这里先添加以下代码到文件顶部位置:

[objc] view
plain copy

@protocol CircularTimerDelegate <NSObject>

- (void)finishedCountDown;

@end

那么接下来就是公共接口的方法了(说明都写在注释里了,对就是给你看绿色字体来着):

[objc] view
plain copy

/**

* 初始化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文件里需要做的准备工作有哪些(当然是还是声明我们需要的变量啦):

[objc] view
plain copy

@interface GYCCircularTimer () {

//一个环形路径变量

UIBezierPath *path;

//最初设置的总时长

NSInteger duration;

//剩余时间

NSInteger remain;

//缓存倒计时器的标题文本内容

NSString *title;

//小时

NSUInteger hour;

//分钟

NSUInteger minute;

//秒

NSUInteger second;

//用于倒计时的NSTimer对象变量

NSTimer *countDownTimer;

}

@end

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

2. 那么开始实现吧

这次我们首先来完成倒计时器里面控件的布局和基本属性设置,先来看看initCircularTimer方法吧:

[objc] view
plain copy

- (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:

[objc] view
plain copy

- (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;

}

顺便把这两个改变环形进度条宽度和颜色的也给做了:

[objc] view
plain copy

- (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]];

}

接下来就是重点实现部分了,先从环形进度条的动画开始切入:

[objc] view
plain copy

#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文件后添加如下代码:

[objc] view
plain copy

@interface GYCCircularTimerViewController () <CircularTimerDelegate>{

GYCCircularTimer *cTimer;

UIButton *cButton;

}

再在viewDidLoad中进行编写,代码参照下面:

[objc] view
plain copy

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];

定义个开始倒计时和暂停的方法:

[objc] view
plain copy

- (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类的代理方法:

[objc] view
plain copy

#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。

请别猛戳!!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: