iOS开发造轮子 | 优雅的封装一个倒计时button
来源:互联网 发布:jquery ui.min.js下载 编辑:程序博客网 时间:2024/06/13 01:10
点击上方“iOS开发”,选择“置顶公众号”
关键时刻,第一时间送达!
目标
封装一个满足基本功能并且方便使用的倒计时按钮,关键是:
要优雅
需要实现的基本功能
以获取短信验证码为例。用户点击获取验证码按钮后,按钮enabled立即设置为NO,并且向后台发送请求,若请求失败,恢复按钮的enabled,反之开始倒计时,期间持续刷新按钮文本,倒计时结束后重置按钮。
需要处理的几个点
1.用户点击按钮
此时按钮的enabled立即变为NO,并向后台发起请求。
2.请求结束
若请求成功,开始倒计时,反之恢复按钮的可点状态并提示用户重试。
3.倒计时进行中
持续刷新按钮文本。
4.倒计时结束
恢复按钮的可点状态,重置按钮文本。
倒计时按钮封装
将上述几个需要处理的点以block的方式封装:
#import "CQCountDownButton.h"
#import <MSWeakTimer.h>
typedef void(^ButtonClickedBlock)();
typedef void(^CountDownStartBlock)();
typedef void(^CountDownUnderwayBlock)(NSInteger restCountDownNum);
typedef void(^CountDownCompletionBlock)();
@interface CQCountDownButton ()
/** 控制倒计时的timer */
@property (nonatomic, strong) MSWeakTimer *timer;
/** 按钮点击事件的回调 */
@property (nonatomic, copy) ButtonClickedBlock buttonClickedBlock;
/** 倒计时开始时的回调 */
@property (nonatomic, copy) CountDownStartBlock countDownStartBlock;
/** 倒计时进行中的回调(每秒一次) */
@property (nonatomic, copy) CountDownUnderwayBlock countDownUnderwayBlock;
/** 倒计时完成时的回调 */
@property (nonatomic, copy) CountDownCompletionBlock countDownCompletionBlock;
@end
@implementation CQCountDownButton {
/** 倒计时开始值 */
NSInteger _startCountDownNum;
/** 剩余倒计时的值 */
NSInteger _restCountDownNum;
}
/**
构造方法
@param frame frame
@param duration 倒计时时间
@param buttonClicked 按钮点击事件的回调
@param countDownStart 倒计时开始时的回调
@param countDownUnderway 倒计时进行中的回调(每秒一次)
@param countDownCompletion 倒计时完成时的回调
@return 倒计时button
*/
- (instancetype)initWithFrame:(CGRect)frame
duration:(NSInteger)duration
buttonClicked:(void(^)())buttonClicked
countDownStart:(void(^)())countDownStart
countDownUnderway:(void(^)(NSInteger restCountDownNum))countDownUnderway
countDownCompletion:(void(^)())countDownCompletion {
if (self = [super initWithFrame:frame]) {
_startCountDownNum = duration;
self.buttonClickedBlock = buttonClicked;
self.countDownStartBlock = countDownStart;
self.countDownUnderwayBlock = countDownUnderway;
self.countDownCompletionBlock = countDownCompletion;
[self addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
return self;
}
/** 按钮点击 */
- (void)buttonClicked:(CQCountDownButton *)sender {
sender.enabled = NO;
self.buttonClickedBlock();
}
/** 开始倒计时 */
- (void)startCountDown {
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
_restCountDownNum = _startCountDownNum;
self.countDownStartBlock(); // 调用倒计时开始的block
self.timer = [MSWeakTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(refreshButton) userInfo:nil repeats:YES dispatchQueue:dispatch_get_main_queue()];
}
/** 刷新按钮内容 */
- (void)refreshButton {
_restCountDownNum --;
self.countDownUnderwayBlock(_restCountDownNum); // 调用倒计时进行中的回调
if (_restCountDownNum == 0) {
[self.timer invalidate];
self.timer = nil;
_restCountDownNum = _startCountDownNum;
self.countDownCompletionBlock(); // 调用倒计时完成的回调
self.enabled = YES;
}
}
畅快使用,一个方法搞定所有事件处理及回调
__weak __typeof__(self) weakSelf = self;
self.countDownButton = [[CQCountDownButton alloc] initWithFrame:CGRectMake(90, 90, 150, 30) duration:10 buttonClicked:^{
//------- 按钮点击 -------//
[SVProgressHUD showWithStatus:@"正在获取验证码..."];
// 请求数据
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
int a = arc4random() % 2;
if (a == 0) {
// 获取成功
[SVProgressHUD showSuccessWithStatus:@"验证码已发送"];
// 获取到验证码后开始倒计时
[weakSelf.countDownButton startCountDown];
} else {
// 获取失败
[SVProgressHUD showErrorWithStatus:@"获取失败,请重试"];
weakSelf.countDownButton.enabled = YES;
}
});
} countDownStart:^{
//------- 倒计时开始 -------//
NSLog(@"倒计时开始");
} countDownUnderway:^(NSInteger restCountDownNum) {
//------- 倒计时进行中 -------//
[weakSelf.countDownButton setTitle:[NSString stringWithFormat:@"再次获取(%ld秒)", restCountDownNum] forState:UIControlStateNormal];
} countDownCompletion:^{
//------- 倒计时结束 -------//
[weakSelf.countDownButton setTitle:@"点击获取验证码" forState:UIControlStateNormal];
NSLog(@"倒计时结束");
}];
亮点
倒计时进行中的block,通过传递剩余倒计时数值,优雅实现按钮的持续更新:
countDownUnderway:^(NSInteger restCountDownNum) {
//------- 倒计时进行中 -------//
[weakSelf.countDownButton setTitle:[NSString stringWithFormat:@"再次获取(%ld秒)", restCountDownNum] forState:UIControlStateNormal];
}
Block or Delegate?
每当涉及到回调的时候我都会考虑这个问题。
block强调结果而delegate强调过程,在这里我们显然要的是结果。还有就是,如果这里采用delegate,代码的组织将更繁琐(需要4个代理方法依次对应4个block)。
但是,如果真的用delegate的话,用#pragma mark - count down将4个代理方法放在一起,想较block而言代码结构会更加的层次分明,这也是大家通常认为delegate更容易维护的原因之一吧。
使用block还是delegate这是一个仁者见仁智者见智的问题,我个人认为如果你对你的代码有较高要求、懂得换位思考,那么你不管使用delegate还是block都可以写出赏心悦目的代码,反之,都将惨不忍睹。
内存管理
因为涉及到timer和block,所以内存这一块要警惕。
关于block
注意使用weakSelf。
最重要的还是用instrument彻底的检查一下。
我已经用instrument检查多次了,请放心使用。
给dalao递优秀三方库.gif
Demo
demo(https://github.com/CaiWanFeng/CQCountDownButton)
作者:无夜之星辰
链接:http://www.jianshu.com/p/34e87194fb83
來源:简书
iOS开发整理发布,转载请联系作者授权
【点击成为安卓大神】
- iOS开发造轮子 | 优雅的封装一个倒计时button
- iOS开发怎么在Button的显示倒计时
- iOS 简单的倒计时封装
- iOS开发造轮子 | UIView及其子类的占位图
- Swift封装的一种带动画的倒计时Button
- iOS开发封装带有Button的UIView控件,使用block给button添加点击事件
- iOS开发封装带有Button的UIView控件,使用代理给button添加点击事件
- iOS开发造轮子 | Loading图
- Dialog封装大全NiceDialog一个强大的轮子
- iOS封装浅谈-一句代码弹出actionSheet,如何优雅的设计一个ActionSheetManager
- iOS开发 - 封装一个自己的按钮
- 造了一个轮子:注册表键编辑封装->registry类
- 【积淀】封装一个简单的倒计时组件
- Button的点击倒计时
- CountDownButton:倒计时的Button
- 一个设置button上字体的封装
- Button按钮的倒计时实现的一个帮助类
- 亲手造的一个轮子--jiptux
- Android开发之Intent的使用
- Zookeeper和Curator-Framework实践系列之: 配置管理
- UVA11178 计算几何入门题
- Ubuntu安装、基本命令和常见故障处理
- iOS开发造轮子 | UIView及其子类的占位图
- iOS开发造轮子 | 优雅的封装一个倒计时button
- nyoj111分数加减法
- DNA Prefix (字典树)
- thread中join和detach的区别
- 异常控制流
- 安卓开发-Broadcast接受者+六种常见Broadcast接受者案例+进程的优先级
- 轻松告别OpenCV Manager
- 图解什么是编译程序?程序设计语言典型的处理过程(预处理、编译、汇编)
- Python的生成器表达式与生成器函数