現(xiàn)在的APP應用中,用手機獲取短信驗證碼是非常常見的一個功能,而往往要求的效果就是在按下獲取驗證碼之后,驗證碼的按鈕開始倒計時,例如30秒后重新獲取。而我們?nèi)绾蝸硗瓿蛇@個效果呢,其實很簡單,用一個定時器來計時,設(shè)置定時器的時間為UIButton的Title,而這個步驟我們一般用多線程的定時器dispatch source來定時產(chǎn)生事件。
在網(wǎng)上看了別人寫的代碼,復用性比較差,沒有對這個方法進行良好的封裝,我在這里貼一段修改后的代碼,基本上大家黏貼過去就能復用。而后面我會解釋這個代碼的多線程方面的一些問題。
#pragma mark - 倒計時獲取驗證碼
-(void)changeTimeOut:(int)timeOut btnTag:(int)btnTag{
__block int timeout=timeOut;
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_release(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
//倒計時結(jié)束
_smsCodeBtn=(UIButton*)[self.view viewWithTag:[[NSString stringWithFormat:@"%d",btnTag] intValue]];
[_smsCodeBtn setTitle:@"重新獲取驗證碼" forState:UIControlStateNormal];
[_smsCodeBtn setUserInteractionEnabled:YES];
[_smsCodeBtn setTitleColor:[UIColor colorWithRed:53.0f/255.0f green:53.0f/255.0f blue:68.0f/255.0f alpha:1] forState:UIControlStateNormal];
[_smsCodeBtn.layer setBorderColor:[UIColor colorWithRed:53.0f/255.0f green:53.0f/255.0f blue:68.0f/255.0f alpha:1].CGColor];
});
}else{
NSString* strTime=[NSString stringWithFormat:@"%d秒后重新獲取",(int)(timeout)];
dispatch_async(dispatch_get_main_queue(), ^{
//倒計時
_smsCodeBtn=(UIButton*)[self.view viewWithTag:[[NSString stringWithFormat:@"%d",(int)btnTag] intValue]];
[_smsCodeBtn setTitle:strTime forState:UIControlStateNormal];
[_smsCodeBtn setTitleColor:[UIColor colorWithRed:153.0f/255.0f green:153.0f/255.0f blue:153.0f/255.0f alpha:1] forState:UIControlStateNormal];
[_smsCodeBtn.layer setBorderColor:[UIColor colorWithRed:153.0f/255.0f green:153.0f/255.0f blue:153.0f/255.0f alpha:1].CGColor];
[_smsCodeBtn setUserInteractionEnabled:NO];
});
timeout--;
}
});
dispatch_resume(_timer);
}
這段代碼中smsCodeBtn就是我定義的獲取短信驗證碼的UIButton,你們復制下去只要把frame屬性和ui屬性改一改,就完全可以直接用了。
那么接下來我們來講講這段代碼中多線程的問題。
首先我們先用dispatch_get_global_queue來開啟一個全局隊列,之后用dispatch_source_t來產(chǎn)生定時事件。所有定時器dispatch_source都是間隔定時器,一旦創(chuàng)建,會按你指定的間隔定期遞送事件。你需要為定時器dispatch_source指定一個期望的定時器事件精度,也就是leeway值,讓系統(tǒng)能夠靈活地管理電源并喚醒內(nèi)核。例如系統(tǒng)可以使用leeway值來提前或延遲觸發(fā)定時器,使其更好地與其他系統(tǒng)事件結(jié)合。創(chuàng)建自己的定時器時,你應該盡量指定一個leeway值。
就算你指定的leeway值為0,也不要期望定時器能夠按照精確地納秒來觸發(fā)事件,系統(tǒng)會盡可能地滿足你的需求,但是無法保證完全精確的觸發(fā)時間。
如果你使用dispatch_walltime函數(shù)來設(shè)置定時器dispatch_source,則定時器會根據(jù)掛鐘時間來跟蹤,這種定時器比較適合觸發(fā)間隔相對比較大的場合,可以防止定時器觸發(fā)時間出現(xiàn)太大誤差。
dispatch_time類型的時間我們可以通過dispatch_time來創(chuàng)建,也可以通過dispatch_walltime來創(chuàng)建。前者創(chuàng)建的時間多以第一個參數(shù)為參照物,之后過多久執(zhí)行任務。后者多用于創(chuàng)建絕對時間,如某年某月某日某時某分執(zhí)行某任務,比如鬧鐘的設(shè)置。
最后我們用dispatch_source_set_event_handler這個方法來創(chuàng)建我們要完成的任務,很簡單的語句,一個block閉包,里面的內(nèi)容當然是自由發(fā)揮的咯。
所以交代清楚GCD中時間事件,這段代碼就非常容易理解了。
代碼很短也就不傳到Github上的demo了。如果有寫的不對的地方,歡迎交流。