iOS 驗(yàn)證碼View的實(shí)現(xiàn)方案

本以為實(shí)現(xiàn)一個(gè)輸入驗(yàn)證碼的界面是很容易褐着,但是沒(méi)想到交互起來(lái)還是挺麻煩的逃顶。例如下圖的驗(yàn)證碼輸入校驗(yàn),在許多場(chǎng)景下會(huì)使用到類(lèi)似驗(yàn)證碼顯示與輸入甲捏。這里記錄一下實(shí)現(xiàn)的過(guò)程演熟,踩過(guò)的坑與比較好的方案。這個(gè)過(guò)程重新去了解了drawRect方法司顿,使用了IBInspectable和IB_DESIGNABLE芒粹,封裝控件(代碼與xib都可用)。

需求效果圖.png

嘗試方案

一開(kāi)始實(shí)現(xiàn)時(shí)大溜,我添加4個(gè)textField作為這4個(gè)驗(yàn)證碼的顯示與輸入化漆。接著添加響應(yīng)事件與代理實(shí)現(xiàn)輸入一個(gè)驗(yàn)證碼后,焦點(diǎn)跳轉(zhuǎn)下一個(gè)驗(yàn)證碼钦奋。正向流程沒(méi)有問(wèn)題获三,但在使用退格鍵時(shí),焦點(diǎn)需要回到上一個(gè)锨苏。這里就出現(xiàn)了一個(gè)坑疙教,textField內(nèi)容為空,監(jiān)聽(tīng)不到退格鍵的使用伞租。網(wǎng)上也搜尋相關(guān)方案贞谓,但是都沒(méi)有好的辦法】看到一個(gè)做法是插入在textField為空裸弦,插入一個(gè)字符(一個(gè)沒(méi)有顯示寬度的字符),此時(shí)再按退格鍵就可以監(jiān)聽(tīng)刪除內(nèi)容作喘。

推薦方案

在網(wǎng)上查找相關(guān)實(shí)現(xiàn)的方案理疙,參考不多,筆者看到了一個(gè)鏈接泞坦,如果還有更好推薦留言一下窖贤。
實(shí)現(xiàn)原理是:使用一個(gè)Label來(lái)顯示每一位驗(yàn)證碼。使用一個(gè)隱藏TextField來(lái)接收輸入。將TextField上編輯的內(nèi)容顯示到Label上赃梧。這樣就不會(huì)有太多交互細(xì)節(jié)需要處理延赌,就像這個(gè)驗(yàn)證碼的View是一個(gè)TextField一樣(實(shí)際上也是)沸毁。

封裝與使用

筆者將它進(jìn)行了整理與封裝優(yōu)化:

  • 適用于帶邊框顯示的驗(yàn)證碼樣式。
  • Label 顯示drawRect時(shí)魔慷,添加邊框抡句,邊框默認(rèn)是顯示矩形區(qū)域外部的膀息,進(jìn)行了處理旧噪,顯示在矩形區(qū)域內(nèi)菌湃。
  • 添加間距,邊框?qū)挾鹊葏?shù)巷折,正確顯示邊框與文本压鉴。
  • 優(yōu)化支持xib中使用控件。
  • 優(yōu)化支持xcode及時(shí)顯示UI盔几。
  • 接口簡(jiǎn)單易用晴弃。

文件結(jié)構(gòu):

文件結(jié)構(gòu).png

封裝的Label:

- (void)drawRect:(CGRect)rect {
    
    // 第一次drawRect rect的數(shù)值不正確掩幢,不知道為什么逊拍,麻煩有知道的issue下我。這里處理了一下际邻。
    rect.size.width = rect.size.width > self.bounds.size.width ? self.bounds.size.width : rect.size.width;
    rect.size.height = rect.size.height > self.bounds.size.height ? self.bounds.size.height : rect.size.height;
    
    //計(jì)算每位驗(yàn)證碼/密碼的所在區(qū)域的寬和高
    float width = (rect.size.width - (self.numberOfVertificationCode -1) * self.spacing)/ (float)self.numberOfVertificationCode;;
    float height = rect.size.height;
    
    // 畫(huà)矩形邊框 (邊框會(huì)顯示到rect區(qū)域外芯丧,進(jìn)行了處理)
    for (int i = 0; i < self.numberOfVertificationCode; i++) {
        // 計(jì)算每位驗(yàn)證碼/密碼的繪制區(qū)域
        CGRect tempRect = CGRectMake(i * (width +self.spacing) + self.lineWidth, self.lineWidth, width - self.lineWidth*2, height - self.lineWidth*2);
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSetLineWidth(context, self.lineWidth);
        CGContextSetStrokeColorWithColor(context, self.borderColor.CGColor);
        UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:tempRect cornerRadius:self.cornerRadius];
        CGContextAddPath(context, bezierPath.CGPath);
        CGContextStrokePath(context);
    }
    
    // 將每位驗(yàn)證碼/密碼繪制到指定區(qū)域
    for (int i = 0; i < self.text.length; i++) {
        CGRect tempRect = CGRectMake(i * (width +self.spacing) +self.lineWidth, self.lineWidth, width - self.lineWidth * 2, height - self.lineWidth*2);
        // 遍歷驗(yàn)證碼/密碼的每個(gè)字符
        NSString *charecterString = [NSString stringWithFormat:@"%c", [self.text characterAtIndex:i]];
        // 設(shè)置驗(yàn)證碼/密碼的現(xiàn)實(shí)屬性
        NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
        attributes[NSFontAttributeName] = self.font;
        attributes[NSForegroundColorAttributeName] = self.textColor;
        // 計(jì)算每位驗(yàn)證碼的繪制起點(diǎn)
        // 計(jì)算每位驗(yàn)證碼的在指定樣式下的size
        CGSize characterSize = [charecterString sizeWithAttributes:attributes];
        CGPoint vertificationCodeDrawStartPoint = CGPointMake(CGRectGetMidX(tempRect) - characterSize.width /2, CGRectGetMidY(tempRect) - characterSize.height /2);
        // 繪制驗(yàn)證碼
        [charecterString drawAtPoint:vertificationCodeDrawStartPoint withAttributes:attributes];   
    }
    
}

xib中使用:
直接拖一個(gè)view,修改xib中類(lèi)名即可世曾,可通過(guò)如圖設(shè)置屬性缨恒。


xib內(nèi)使用.png

代碼中使用:

- (QTVertificationCodeInputView *)vertificationCodeInputView{
    if (!_vertificationCodeInputView) {
        _vertificationCodeInputView = [[QTVertificationCodeInputView alloc]initWithFrame:CGRectMake(30, 100, 300, 200)];
        _vertificationCodeInputView.numberOfVertificationCode = 4;
        _vertificationCodeInputView.lineWidth = 10;
        _vertificationCodeInputView.spacing = 10;
        _vertificationCodeInputView.cornerRadius = 5;
        _vertificationCodeInputView.backgroundColor = [UIColor orangeColor];
    }
    return _vertificationCodeInputView;
}

疑問(wèn)

UILabel初始化第一次調(diào)用的drawRect,rect的值并不是設(shè)置frame的值轮听,而是稍微大了一點(diǎn)骗露,不知道為什么不對(duì),麻煩有知道的issue下我血巍。這里處理了一下萧锉。

- (void)drawRect:(CGRect)rect {
    // 第一次drawRect rect的數(shù)值不正確,不知道為什么述寡,麻煩有知道的issue下我柿隙。這里處理了一下。
    rect.size.width = rect.size.width > self.bounds.size.width ? self.bounds.size.width : rect.size.width;
    rect.size.height = rect.size.height > self.bounds.size.height ? self.bounds.size.height : rect.size.height;
}

工程文件在github上:https://github.com/casscqt/QTVertificationCodeInputView
參考鏈接:這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鲫凶,一起剝皮案震驚了整個(gè)濱河市禀崖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌螟炫,老刑警劉巖波附,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡叶雹,警方通過(guò)查閱死者的電腦和手機(jī)财饥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)折晦,“玉大人钥星,你說(shuō)我怎么就攤上這事÷牛” “怎么了谦炒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)风喇。 經(jīng)常有香客問(wèn)我宁改,道長(zhǎng),這世上最難降的妖魔是什么魂莫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任还蹲,我火速辦了婚禮,結(jié)果婚禮上耙考,老公的妹妹穿的比我還像新娘谜喊。我一直安慰自己,他們只是感情好倦始,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布斗遏。 她就那樣靜靜地躺著,像睡著了一般鞋邑。 火紅的嫁衣襯著肌膚如雪诵次。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天枚碗,我揣著相機(jī)與錄音逾一,去河邊找鬼。 笑死肮雨,一個(gè)胖子當(dāng)著我的面吹牛遵堵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酷含,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鄙早,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了椅亚?” 一聲冷哼從身側(cè)響起限番,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呀舔,沒(méi)想到半個(gè)月后弥虐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體扩灯,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年霜瘪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了珠插。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡颖对,死狀恐怖捻撑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缤底,我是刑警寧澤顾患,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站个唧,受9級(jí)特大地震影響江解,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜徙歼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一犁河、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧魄梯,春花似錦桨螺、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吸奴。三九已至允扇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間则奥,已是汗流浹背考润。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留读处,地道東北人糊治。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像罚舱,于是被迫代替她去往敵國(guó)和親井辜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)管闷、插件粥脚、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,101評(píng)論 4 62
  • 16年春節(jié)后,我見(jiàn)了 x先生包个,準(zhǔn)確的說(shuō)從14年年后就沒(méi)有在見(jiàn)過(guò)他了刷允。我和x先生相識(shí)是在13年春節(jié)后,匆匆的相識(shí)又...
    小魔怪x閱讀 294評(píng)論 0 1
  • 《雕像》 海底石 作 或許這樣的開(kāi)端過(guò)于平淡 平淡到一立就是百年 一百年啊树灶!換過(guò)了幾代人 一百年跋伺!改過(guò)了多少里路...
    王自鵬閱讀 284評(píng)論 0 1
  • 改革開(kāi)放30多年來(lái)天通,國(guó)家頒布了幾個(gè)具有代表性的經(jīng)濟(jì)條例泊窘,每個(gè)條例的頒布都造就了一大批富翁與成功人士。 1像寒、1980...
    曹丞鈁湲閱讀 444評(píng)論 0 0
  • 一般在Ubuntu平臺(tái)上尋找數(shù)據(jù)庫(kù)瀏覽器不是很方便州既,不過(guò)還是搜到了SQLite3的瀏覽器,這個(gè)網(wǎng)站提供了多平臺(tái)的解...
    Danny_Boy閱讀 1,391評(píng)論 0 49