最近想整理下关于OC的工具类,常用到的也方便自己之后查阅,也可以给需要的朋友参考。
1、两种倒计时
-
第一种:
GCD
__block NSInteger timeout = time;//可以假设time等于60秒
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
if(timeout<=0){ //倒计时结束,关闭
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
//主线程中处理UI
});
}else{
dispatch_async(dispatch_get_main_queue(), ^{
//主线程中处理UI
presellTimeLB.text = [self dateFormatDays_Hours_MinsWith:timeout];
});
timeout--;
}
});
dispatch_resume(_timer);
这里我假设我有一个名为presellTimeLB
的Label,处理倒计时的方法dateFormatDays_Hours_MinsWith:
(仅供参考)
- (NSString *)dateFormatDays_Hours_MinsWith:(NSInteger)time
{
NSInteger days = 0,hours = 0,minutes = 0,second = 0;
NSInteger daySeconds = 24*60*60;
//获取天数
if (time >= daySeconds) {
days = time/daySeconds;
time = time%daySeconds;
}
//获取小时数
if(time>=60*60){
hours = time/3600;
time = time%3600;
}
//获取分钟数
if (time>=60) {
minutes = time/60;
time = time%60;
}
//获取秒数
second = time;
if (days>0) {
return [NSString stringWithFormat:@"%ld天%ld时%ld分",(long)days,(long)hours,(long)minutes];
}else{
return [NSString stringWithFormat:@"%ld时%ld分%ld秒",(long)hours,(long)minutes,(long)second];
}
}
-
第二种:
NSTimer
1)声明
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES];
2)实现select方法
- (void)timerFireMethod:(NSTimer *)theTimer
{
timer = theTimer;
static NSInteger hhh;
if (timeStart) {
hhh = [self.leftTime integerValue];
timeStart = NO;
}
if (hhh <= 1) {
preSellView.hidden = YES;
//注①
[timer invalidate];
}else {
//注②
[[NSRunLoop currentRunLoop] addTimer:theTimer forMode:NSRunLoopCommonModes];
hhh--;
dispatch_async(dispatch_get_main_queue(), ^{
[presellTimeLB setText:[self dateFormatDays_Hours_MinsWith:hhh]];
});
}
}
注:
①NSTimer
对象用完之后必须进行invalidate
,NSTimer
才会消失。比如说你还需要考虑到倒计时没结束时退出当前页面时是不是也需要把当前的NSTimer invalidate
呢?
②这里为什么要用NSRunLoop
写这句代码呢?如果没有这句代码,会发现在界面上滚动一个scrollView
,那么滚动停止前,timer像是暂停了一样,其实这是runloop的mode在作怪。
runloop
可以理解为cocoa下的一种消息循环机制,用来处理各种消息事件,我们在开发的时候并不需要手动去创建一个runloop
,因为框架为我们创建了一个默认的runloop
,通过[NSRunloop currentRunloop]
我们可以得到一个当前线程下面对应的runloop
对象,不过我们需要注意的是不同的runloop
之间消息的通知方式。
在开启一个NSTimer
实质上是在当前的runloop
中注册了一个新的事件源,而当scrollView
滚动的时候,当前的MainRunLoop
是处于UITrackingRunLoopMode
的模式下,在这个模式下,是不会处理NSDefaultRunLoopMode
的消息(因为RunLoop Mode不一样),要
要想在scrollView
滚动的同时也响应其他runloop
的消息,需要改变两者之间的runloopMode
.
[[NSRunLoop currentRunLoop] addTimer:theTimer forMode:NSRunLoopCommonModes];
-
NSTimer 的另一种写法
- (void)timerFireMethod:(NSTimer *)theTimer
{
NSCalendar *cal = [NSCalendar currentCalendar];//定义一个NSCalendar对象
NSDateComponents *endTime = [[NSDateComponents alloc] init]; //初始化目标时间...
NSDate *today = [NSDate date]; //得到当前时间
NSDate *date = [NSDate dateWithTimeInterval:80 sinceDate:today];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString *dateString = [dateFormatter stringFromDate:date];
static int year;
static int month;
static int day;
static int hour;
static int minute;
static int second;
if(timeStart) {//从NSDate中取出年月日,时分秒,但是只能取一次
year = [[dateString substringWithRange:NSMakeRange(0, 4)] intValue];
month = [[dateString substringWithRange:NSMakeRange(5, 2)] intValue];
day = [[dateString substringWithRange:NSMakeRange(8, 2)] intValue];
hour = [[dateString substringWithRange:NSMakeRange(11, 2)] intValue];
minute = [[dateString substringWithRange:NSMakeRange(14, 2)] intValue];
second = [[dateString substringWithRange:NSMakeRange(17, 2)] intValue];
timeStart= NO;
}
[endTime setYear:year];
[endTime setMonth:month];
[endTime setDay:day];
[endTime setHour:hour];
[endTime setMinute:minute];
[endTime setSecond:second];
NSDate *todate = [cal dateFromComponents:endTime]; //把目标时间装载入date
//用来得到具体的时差,是为了统一成北京时间
unsigned int unitFlags = NSYearCalendarUnit| NSMonthCalendarUnit| NSDayCalendarUnit| NSHourCalendarUnit| NSMinuteCalendarUnit| NSSecondCalendarUnit;
NSDateComponents *d = [cal components:unitFlags fromDate:today toDate:todate options:0];
NSString *h = [NSString stringWithFormat:@"%ld",(long)[d hour]];
NSString *fen = [NSString stringWithFormat:@"%ld", (long)[d minute]];
if([d minute] < 10) {
fen = [NSString stringWithFormat:@"%ld",[d minute]];
}
NSString *miao = [NSString stringWithFormat:@"%ld", (long)[d second]];
if([d second] < 10) {
miao = [NSString stringWithFormat:@"0%ld",(long)[d second]];
}
if([d second] >= 0) {
//计时尚未结束,do_something
dispatch_async(dispatch_get_main_queue(), ^{
[[NSRunLoop currentRunLoop] addTimer:theTimer forMode:NSRunLoopCommonModes];
[presellTimeLB setText:[NSString stringWithFormat:@"%ld时%ld分%ld秒",[h integerValue],[fen integerValue], [miao integerValue]]];
});
} else{
//倒计时结束,do something
[theTimer invalidate];
}
}
注:这里我们以80
秒倒计时为例,还是假设我有一个BOOL
类型的timeStart
为了取一次原始时间,有名为presellTimeLB
的Laber,nsrunloop
改变mode的状态照样写上。这里把时间处理和逻辑判断写在了一起。