核心思想:用CACurrentMediaTime方法获取到手机从开机一直到当前所经过的秒数,这个是不容易篡改的,而且从前台退到后台,再回到前台的过程中,CACurrentMediaTime一直在自增。
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIButton (MZCountdown)
- (void)mz_customCountDownWithTime:(NSInteger)timeNum endTitle:(NSString *)endTitle endTitleColor:(NSString *)endTitleColor endBgColor:(NSString *)endBgColor leftTimeBlock:(void (^)(NSInteger leftTime))block;
- (void)mz_countDownWithTime:(NSInteger)timeNum countingTitleColor:(NSString *)countingTitleColor countingFormat:(NSString *)countingFormat countingBgColor:(NSString *)countingBgColor endTitle:(NSString *)endTitle endTitleColor:(NSString *)endTitleColor endBgColor:(NSString *)endBgColor;
@end
NS_ASSUME_NONNULL_END
// .m
#import "UIButton+MZCountdown.h"
#import <objc/message.h>
@implementation UIColor (MZExtension)
+ (UIColor *)colorWithHex:(NSString *)hexColor alpha:(CGFloat)alpha {
while ([hexColor hasPrefix:@"#"]) {
hexColor = [hexColor substringFromIndex:1];
}
if ([hexColor length] < 6) {
return nil;
}
unsigned int red, green, blue;
NSRange range;
range.length = 2;
range.location = 0;
[[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&red];
range.location = 2;
[[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&green];
range.location = 4;
[[NSScanner scannerWithString:[hexColor substringWithRange:range]] scanHexInt:&blue];
return [UIColor colorWithRed:(float)(red/255.f) green:(float)(green / 255.f) blue:(float)(blue / 255.f) alpha:alpha];
}
+ (UIColor *)colorWithHex:(NSString *)hexColor {
return [self colorWithHex:hexColor alpha:1.f];
}
@end
@interface UIButton ()
@property (nonatomic, strong) dispatch_source_t timer;
@end
@implementation UIButton (MZCountdown)
- (void)setTimer:(dispatch_source_t)timer {
objc_setAssociatedObject(self, @selector(timer), timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (dispatch_source_t)timer {
return objc_getAssociatedObject(self, _cmd);
}
- (void)mz_countDownWithTime:(NSInteger)timeNum countingTitleColor:(NSString *)countingTitleColor countingFormat:(NSString *)countingFormat countingBgColor:(NSString *)countingBgColor endTitle:(NSString *)endTitle endTitleColor:(NSString *)endTitleColor endBgColor:(NSString *)endBgColor {
//倒计时时间
__block NSInteger timeOut = timeNum + CACurrentMediaTime() + 1;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//每秒执行一次
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(self.timer, ^{
NSInteger left = timeOut - CACurrentMediaTime();
//倒计时结束,关闭
if (left <= 0) {
[self mz_cancelTimerWithTitle:endTitle titleColor:endTitleColor bgColor:endBgColor];
} else {//倒计时中
dispatch_async(dispatch_get_main_queue(), ^{
if (countingBgColor) {
NSAssert(countingBgColor.length != 0, @"countingBgColor不能为空字符串");
self.layer.backgroundColor = [UIColor colorWithHex:countingBgColor].CGColor;
}
if (countingTitleColor) {
NSAssert(countingTitleColor.length != 0, @"countingTitleColor不能为空字符串");
[self setTitleColor:[UIColor colorWithHex:countingTitleColor] forState:UIControlStateNormal];
}
[self p_showLeftTimeWithFormat:countingFormat leftTime:left];
});
}
});
dispatch_resume(self.timer);
}
- (void)p_showLeftTimeWithFormat:(NSString *)countingFormat leftTime:(NSInteger)leftTime {
if (!countingFormat || !countingFormat.length) {
[self setTitle:[NSString stringWithFormat:@"%ld秒", leftTime] forState:UIControlStateNormal];
} else {
[self setTitle:[NSString stringWithFormat:countingFormat, leftTime] forState:UIControlStateNormal];
}
self.userInteractionEnabled = NO;
}
- (void)mz_customCountDownWithTime:(NSInteger)timeNum endTitle:(NSString *)endTitle endTitleColor:(NSString *)endTitleColor endBgColor:(NSString *)endBgColor leftTimeBlock:(void (^)(NSInteger leftTime))block {
//倒计时时间
__block NSInteger timeOut = timeNum + CACurrentMediaTime() + 1;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//每秒执行一次
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(self.timer, ^{
NSInteger left = timeOut - CACurrentMediaTime();
//倒计时结束,关闭
if (left <= 0) {
[self mz_cancelTimerWithTitle:endTitle titleColor:endTitleColor bgColor:endBgColor];
} else {//倒计时中
dispatch_async(dispatch_get_main_queue(), ^{
!block ?: block(left);
});
}
});
dispatch_resume(self.timer);
}
/** 取消倒计时 */
- (void)mz_cancelTimerWithTitle:(NSString *)title titleColor:(NSString *)titleColor bgColor:(NSString *)bgColor {
if (!self.timer) {
return;
}
dispatch_source_cancel(self.timer);
dispatch_async(dispatch_get_main_queue(), ^{
if (bgColor) {
NSAssert(bgColor.length != 0, @"bgColor不能为空字符串");
self.layer.backgroundColor = [UIColor colorWithHex:bgColor].CGColor;
}
if (titleColor) {
NSAssert(titleColor.length != 0, @"titleColor不能为空字符串");
[self setTitleColor:[UIColor colorWithHex:titleColor] forState:UIControlStateNormal];
}
[self setTitle:title forState:UIControlStateNormal];
self.userInteractionEnabled = YES;
});
}
@end