學(xué)習(xí)計(jì)劃 (2) - 自定義驗(yàn)證碼按鈕

今天要做的是一個(gè)倒計(jì)時(shí)按鈕,也是項(xiàng)目當(dāng)中經(jīng)常遇到的一類需求认境。
最終實(shí)現(xiàn)效果如下:


驗(yàn)證碼按鈕.png

而實(shí)現(xiàn)這種驗(yàn)證碼,方式很多,有人用NSTimer進(jìn)行開發(fā)的垃喊,也有用dispatch_source_t 進(jìn)行開發(fā)的笑撞。
但是NSTimer在運(yùn)用過程中需要注意的地方很多默终,如果使用不當(dāng)就會(huì)造成內(nèi)存無法釋放,導(dǎo)致內(nèi)存溢出的可能兼蜈。
所以历等,我后來又改用dispatch_source_t 完成了這一需求妇智。
至于dispatch_source_t 的具體詳解滥玷,有需要的可以去查看相關(guān)資料,畢竟今天的主題是利用它其中的一項(xiàng)功能dispatch_source_set_timer巍棱。

資料傳送門:
https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html

https://www.tuicool.com/articles/nIRJf2e
http://www.dreamingwish.com/article/grand-central-dispatch-basic-3.html

下面開始我們的正題:

由于該項(xiàng)目需要用到上一篇中的文本輸入框,所以直接在上一篇的項(xiàng)目中進(jìn)行使用了。
同樣是選用Model作為參數(shù)的模式
其實(shí)關(guān)于模式的選用,就是根據(jù)自己的工作環(huán)境來決定的航徙。
我選用這種模式的原因就是,很長時(shí)間沒看的代碼杠袱,需要時(shí)間成本去了解.
而這種模式的可讀性較強(qiáng),而且將屬性設(shè)置和構(gòu)建分離凿掂,方便以后的維護(hù)纹蝴。

model的數(shù)據(jù)也就是常見的屬性和一些需要自定義屬性設(shè)置,當(dāng)然糠涛,以后擴(kuò)展還會(huì)很多耙旦,今天的按鈕只是其中之一,所以目前屬性就這些吧.

@interface GWButtonModel : NSObject
//標(biāo)題
@property (nonatomic,readwrite,copy)NSString *title;
//標(biāo)題顏色
@property (nonatomic,readwrite,strong)UIColor *titleColor;
//邊框顏色
@property (nonatomic,readwrite,strong)UIColor *borderColor;
//邊框?qū)挾?@property (nonatomic,readwrite,unsafe_unretained)CGFloat borderWidth;
//刷新計(jì)時(shí)
@property (nonatomic,readwrite,unsafe_unretained)NSTimeInterval refreshTime;
//字體
@property (nonatomic,readwrite,strong)UIFont *font;
//背景色
@property (nonatomic,readwrite,strong)UIColor *backgroundColor;
//圓角
@property (nonatomic,readwrite,unsafe_unretained)CGFloat cornerRadius;
@end

在構(gòu)建GWButton類之前锉罐,我們應(yīng)該去分析我們的實(shí)際應(yīng)用場(chǎng)景,所以我先來說說我在項(xiàng)目中用的場(chǎng)景:

1. 點(diǎn)擊按鈕 
2. 啟用代理到控制器中判斷用戶是否存在(因?yàn)槭强勺兊男枨笕颇铮圆贿m合放在GWButton類中)
3. 如果用戶存在,那么執(zhí)行獲取驗(yàn)證碼的網(wǎng)絡(luò)請(qǐng)求
(我將獲取驗(yàn)證碼的網(wǎng)絡(luò)請(qǐng)求放在GWButton當(dāng)中,因?yàn)檫@是一個(gè)公用方法险领,無論其他需求怎么變,驗(yàn)證碼你得獲取吧绢陌。也減小了控制器中代碼的冗余)
4. 獲取到驗(yàn)證碼之后開始倒計(jì)時(shí)。否則就是保持原樣
5. 倒計(jì)時(shí)完畢恢復(fù)原來的樣式

分析出了完整的需求之后臭笆,事情就簡單多了

  1. 構(gòu)建GWButton類
@protocol GWSecurityCodeDelegate <NSObject>

/**
 發(fā)送驗(yàn)證碼
 */
- (void)sendSecurityCode;
@end

@interface GWButton : UIButton

@property (nonatomic,readwrite,weak)id<GWSecurityCodeDelegate>   securityCodeDelegate;

/**
 驗(yàn)證碼

 @param frame 布局
 @param buttonModel 按鈕數(shù)據(jù)模型

 */
+ (instancetype)securityCodeButtonWithFrame:(CGRect)frame
                                buttonModel:(GWButtonModel *)buttonModel;

/**
 發(fā)送獲取驗(yàn)證碼請(qǐng)求

 @param type 驗(yàn)證碼類型
 @param tel 手機(jī)號(hào)碼
 @param intervalTime 倒計(jì)時(shí)時(shí)間
 */
- (void)securityCodeType:(NSString *)type
           userTelephone:(NSString *)tel
              refresTime:(NSTimeInterval)intervalTime;
@end
這里的兩個(gè)說明:
1. 為什么要用securityCodeDelegate命名,不用delegate.因?yàn)轵?yàn)證碼只是這個(gè)按鈕的其中一種愁铺。
以后還會(huì)添加別的按鈕類型闻鉴,它們的代理可能是別的類型。這樣命名更加容易識(shí)別孟岛。就像WKWebview中的兩個(gè)代理一樣。

2. 因?yàn)楂@取驗(yàn)證碼的請(qǐng)求在验,根據(jù)不同后臺(tái)人員,寫的可能不同盏触,所以我只能列出個(gè)樣例块饺,這里需要的是驗(yàn)證碼類型和手機(jī)號(hào)碼兩個(gè)參數(shù)。

接下來我們就看一下辨嗽,我們的核心代碼了淮腾。
其實(shí)關(guān)于為什么選用dispatch_source,我們可以看一下洲押,官方的介紹:

A dispatch source is a fundamental data type that coordinates the processing of specific low-level system events. 
Grand Central Dispatch supports the following types of dispatch sources:
Timer dispatch sources generate periodic notifications.

好吧圆凰。有很多條,我就不展示了挑童,上面有網(wǎng)址自己看了跃须。
那么第一條就是定時(shí)器資源的使用。我們都知道dispatch系列存在都是不用我們自主管理線程和內(nèi)存釋放的尽楔。這個(gè)就要比NSTimer有優(yōu)勢(shì)多了玉雾。

而且下面這段話也說明了的Dispatch Sources方便之處:

Unlike tasks that you submit to a queue manually, dispatch sources provide a continuous source of events for your application. 
A dispatch source remains attached to its dispatch queue until you cancel it explicitly.
 While attached, it submits its associated task code to the dispatch queue whenever the corresponding event occurs. 
Some events, such as timer events, occur at regular intervals but most occur only sporadically as specific conditions arise. 
For this reason, dispatch sources retain their associated dispatch queue to prevent it from being released prematurely while events may still be pending.

簡單來說就是:

Unlike tasks that you submit to a queue manually, dispatch sources provide a continuous source of events for your application. 
與您手動(dòng)提交到隊(duì)列的任務(wù)不同复旬,分派源為您的應(yīng)用程序提供了一個(gè)連續(xù)的事件源(有道翻譯冲泥,你值得擁有)

我們只需要設(shè)定好參數(shù)壁涎,結(jié)束和需要改變的事件就可以了志秃,其他的它會(huì)幫我們做好。

下面來看實(shí)際的代碼:

 __block int timeout = intervalTime; //倒計(jì)時(shí)時(shí)間

//創(chuàng)建dispatch_source源
1. dispatch_source_t   _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
                                       dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
//間隔時(shí)間
uint64_t  interval = 1.0*NSEC_PER_SEC;
//獲取開始的時(shí)間
dispatch_time_t  start = dispatch_walltime(NULL, 0);
//設(shè)置執(zhí)行的時(shí)間
2. dispatch_source_set_timer(_timer, start, interval, 0);

//設(shè)置資源回調(diào).
3.dispatch_source_set_event_handler(_timer, ^{
       if(timeout<=0){//倒計(jì)時(shí)結(jié)束,關(guān)閉的事件
  
       }else{  //計(jì)時(shí)中的事件
      
            timeout--;
        }
   });

//啟動(dòng)_timer
4. dispatch_resume(_timer);

上面代碼中的NSEC_PER_SEC嘛意思?

#define NSEC_PER_SEC 1000000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull

NSEC:納秒竟坛。
USEC:微秒钧舌。
SEC:秒
PER:每
1   NSEC_PER_SEC洼冻,每秒有多少納秒崭歧。
2   USEC_PER_SEC率碾,每秒有多少毫秒屋彪。(注意是指在納秒的基礎(chǔ)上)
3   NSEC_PER_USEC,每毫秒有多少納秒歧匈。

最后就是簡單的執(zhí)行順序:
執(zhí)行按鈕事件:

#pragma mark 驗(yàn)證碼刷新事件
- (void)securityCodeButtonAction:(GWButton *)button{
    
  //這里寫的目的是為了防止用戶頻繁點(diǎn)擊按鈕造成多次請(qǐng)求的情況
  //網(wǎng)上一些其他的方法例如 分類什么的都試過了還是這個(gè)好用砰嘁。
    button.enabled = NO;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        button.enabled = YES;
    });
    
    if([button.securityCodeDelegate respondsToSelector:@selector(sendSecurityCode)]){
        [button.securityCodeDelegate sendSecurityCode];
    }
}

然后執(zhí)行代理:

- (void)securityCodeType:(NSString *)type
           userTelephone:(NSString *)tel
              refresTime:(NSTimeInterval)intervalTime{
    //可以將網(wǎng)絡(luò)請(qǐng)求的方法放在這里,請(qǐng)求成功執(zhí)行開始計(jì)時(shí)的方法矮湘。
    //還有這個(gè)請(qǐng)求只是放在這里給大家看個(gè)樣例,demo中是沒有的缅阳。
    kWeakSelf
    [GWVerificationCodeManager GWGetVerifyNUmberRequstWithPhone:[PMBTools convertNullOrNil:tel]
                                                             type:[PMBTools convertNullOrNil:type]
              success:^(id responseobject) {
                  NSDictionary *dict = (NSDictionary *)responseobject;
                  NSLog(@"驗(yàn)證碼請(qǐng)求結(jié)果:%@",dict);
                  if([dict[@"resCode"] isEqualToString:@"020000"]){
                      [SVProgressHUD showSuccessWithStatus:dict[@"resMsg"]];
                      [weakself beginToCountDownWithRefresTime:intervalTime];
                  }else{
                      [SVProgressHUD showSuccessWithStatus:dict[@"resMsg"]];
                  }
              } failure:^(NSError *error) {
                  
              } progress:^(float progress) {
                  
              }];
}

最后就是執(zhí)行倒計(jì)時(shí)了

- (void)beginToCountDownWithRefresTime:(NSTimeInterval)intervalTime{
    __block int timeout = intervalTime; //倒計(jì)時(shí)時(shí)間
    kWeakSelf
    dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
    
    dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0);
    
    dispatch_source_set_event_handler(_timer, ^{
        if(timeout<=0){//倒計(jì)時(shí)結(jié)束,關(guān)閉
            dispatch_source_cancel(_timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakself setUserInteractionEnabled:YES];
                [weakself setTitle:@"獲取驗(yàn)證碼" forState:UIControlStateNormal];
                [weakself setBackgroundImage:[PMBTools convertUIColorToUIImage:[PMBTools colorWithHexString:@"#FF9129"]] forState:UIControlStateNormal];
            });
        }else{
            int seconds = (timeout==60?timeout-1:timeout) % 60;
            dispatch_async(dispatch_get_main_queue(), ^{
                //設(shè)置界面的按鈕顯示 根據(jù)自己需求設(shè)置
                [weakself setBackgroundImage:[PMBTools convertUIColorToUIImage:getColor(207, 204, 207, 1)] forState:UIControlStateNormal];
                [weakself setTitle:[NSString stringWithFormat:@"重新獲取(%ds)",seconds]
                          forState:UIControlStateNormal];
                [weakself setUserInteractionEnabled:NO];
            });
            timeout--;
        }
    });
    
    dispatch_resume(_timer);
}

demo地址:
https://github.com/yanggenwei/GWTextField


嗯秀撇,看到這里說明你認(rèn)真看了呵燕,我會(huì)冥冥中開心一下下的件相。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市泛范,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赡突,老刑警劉巖柠傍,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惧笛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拜效,警方通過查閱死者的電腦和手機(jī)各谚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門昌渤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人般眉,你說我怎么就攤上這事潜支∪吣穑” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵项玛,是天一觀的道長弱判。 經(jīng)常有香客問我,道長臣嚣,這世上最難降的妖魔是什么剥哑? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任株婴,我火速辦了婚禮,結(jié)果婚禮上大审,老公的妹妹穿的比我還像新娘座哩。我一直安慰自己,他們只是感情好姜骡,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布圈澈。 她就那樣靜靜地躺著尘惧,像睡著了一般喷橙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上重慢,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天似踱,我揣著相機(jī)與錄音核芽,去河邊找鬼。 笑死驰坊,一個(gè)胖子當(dāng)著我的面吹牛拳芙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舟扎,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼譬猫,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼染服!你這毒婦竟也來了叨恨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤诚亚,失蹤者是張志新(化名)和其女友劉穎站宗,沒想到半個(gè)月后益愈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜂大。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奶浦。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澳叉,死狀恐怖成洗,靈堂內(nèi)的尸體忽然破棺而出瓶殃,到底是詐尸還是另有隱情碌燕,我是刑警寧澤继薛,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布慈鸠,位于F島的核電站青团,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏娃肿。R本人自食惡果不足惜料扰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望焙蹭。 院中可真熱鬧晒杈,春花似錦、人聲如沸孔厉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撰豺。三九已至说庭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郑趁,已是汗流浹背刊驴。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捆憎。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓舅柜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親躲惰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子致份,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,189評(píng)論 25 707
  • 關(guān)于Dispatch Source Dispatch Source是GCD中的一種基本數(shù)據(jù)類型,從字面意思可稱其為...
    杭研融合通信iOS閱讀 3,592評(píng)論 1 9
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理础拨,服務(wù)發(fā)現(xiàn)氮块,斷路器,智...
    卡卡羅2017閱讀 134,672評(píng)論 18 139
  • 名句:“一日之計(jì)在于晨诡宗,一晨之計(jì)在于起滔蝉。” 時(shí)間管理的第一習(xí)慣是早起~ 早起是治家之本~曾國藩 叫醒我們的不是鬧鐘...
    潔子烏托邦閱讀 254評(píng)論 0 0
  • 證件(深戶適用) 大陸居民往來臺(tái)灣通行證 入臺(tái)證 我在深圳塔沃,從內(nèi)地來此求學(xué)蝠引,戶口形式是之前在內(nèi)地(安徽),后隨學(xué)習(xí)...
    tltangliang閱讀 409評(píng)論 2 1