iOS開發(fā):仿支付寶密碼/常規(guī)驗證碼輸入框

.前言

最簡單的方式實現(xiàn):支付寶的密碼輸入框.以及常規(guī)的驗證碼輸入框

先上效果圖

驗證碼輸入框.gif

提供的demo有兩套思路:
1.使用UIKeyInput協(xié)議做的文本控件開發(fā)
2.使用多個UITextField協(xié)同操作

.思路1.

1.首先聲明一個類繼承自UiView.

@interface FQ_TextView : UIView<UIKeyInput,UITextInputTraits> //前者自定義響應(yīng)鍵盤的文本view.后者定義鍵盤樣式

2.并且聲明幾個常用類型

//驗證碼個數(shù) @property (nonatomic, assign) NSInteger number; //輸入完成的block @property (nonatomic, copy) void(^completeBlock)(); //當(dāng)前是否顯示黑色小球的樣式 @property (nonatomic, assign) BOOL mineSecureTextEntry; //是否需要選中效果 @property (nonatomic, assign) BOOL isSelectStatus;

3.UIBezierPath曲線畫外框的線
 -(void)addTextLineView
{
UIColor * lineColor = [UIColor grayColor];

UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointZero];
[bezierPath addLineToPoint:CGPointMake(textViewW, 0)];
[bezierPath addLineToPoint:CGPointMake(textViewW, textViewH)];
[bezierPath addLineToPoint:CGPointMake(0, textViewH)];
[bezierPath addLineToPoint:CGPointMake(0, 0)];

for (int i = 1; i < self.number ; ++i) {
    UIBezierPath * bezierPath1 = [UIBezierPath bezierPath];
    [bezierPath1 moveToPoint:CGPointMake(i * self.sizeW, 0)];
    [bezierPath1 addLineToPoint:CGPointMake(i * self.sizeW, textViewH)];
    [bezierPath appendPath:bezierPath1];
}

CAShapeLayer * layer = [[CAShapeLayer alloc]init];
layer.borderColor = lineColor.CGColor;
layer.borderWidth = lineW;
layer.fillColor = [UIColor clearColor].CGColor;
layer.strokeColor = [UIColor grayColor].CGColor;
layer.lineJoin = kCALineJoinRound;
layer.path = bezierPath.CGPath;
layer.frame = self.bounds;

[self.layer addSublayer:layer];
 }

獲得外部的框:

Pasted Graphic 1.tiff.jpg
4.實現(xiàn)UIKeyInput協(xié)議方法
- (BOOL)hasText
{
return self.textTot.length > 0;
}

鍵盤上每輸入一個字符就會調(diào)用.主要是獲取鍵盤輸入的字符.在這里做處理
- (void)insertText:(NSString *)text
{

if (self.textTot.length == self.number) { //已經(jīng)是最長
    return;
}

[self.textTot appendString:text];
[self uploadTextLineViewWithInex:self.textTot.length];
[self setNeedsDisplay];

if (self.textTot.length == self.number) {
    
    if (_completeBlock) {
        _completeBlock();
    }
    [self resignFirstResponder];
    return;
}    
}

鍵盤上刪除按鈕的回調(diào)
- (void)deleteBackward
{
if (self.textTot.length == 0) {
return;
}
[self.textTot deleteCharactersInRange:NSMakeRange(self.textTot.length - 1, 1)];
[self uploadTextLineViewWithInex:self.textTot.length];
[self setNeedsDisplay];
}

當(dāng)然還需要注意:默認(rèn)不會成為第一響應(yīng)者.需要重寫canBecomeFirstResponder方法獲取資格

-(BOOL)canBecomeFirstResponder
 {
  return YES;
  }
5.繪制出用戶輸入的文本
 - (void)drawRect:(CGRect)rect {

//設(shè)置當(dāng)前繪制顏色
[[UIColor blackColor] set];
//加密樣式.
if (self.mineSecureTextEntry) {
    
    for (int i = 0; i < self.textTot.length; ++i) {
        
        UIImage * img = [UIImage imageNamed:@"code_黑點"];
        CGSize size = img.size;
        CGRect rect = CGRectMake(i * self.sizeW + self.sizeW * 0.5 - img.size.width * 0.5 , textViewH * 0.5 -size.height * 0.5, img.size.width, img.size.height);
        [img drawInRect:rect];
    }
    
}else{//非加密樣式
    
    for (int i = 0; i < self.textTot.length; ++i) {
        NSString * string = [self.textTot substringWithRange:NSMakeRange(i, 1)];
        
        CGSize size = [string boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:0 attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:18]} context:nil].size;
        
        NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc]init];
        style.alignment = NSTextAlignmentCenter;
        
        CGRect rect = CGRectMake(i * self.sizeW, textViewH * 0.5 -size.height * 0.5, self.sizeW, textViewH);
        //這里需要強(qiáng)調(diào)一下.文本只能水平居中.所以豎直居中需要自己計算
        [string drawInRect:rect withAttributes:@{NSFontAttributeName : [UIFont systemFontOfSize:18],NSParagraphStyleAttributeName:style}];
    }
    
}
}
6.添加選中效果

添加一個CAShapeLayer屬性.

 -(void)addTextLineViewSelectLayer
 {

CAShapeLayer * layer = [[CAShapeLayer alloc]init];
layer.fillColor = [UIColor clearColor].CGColor;
layer.strokeColor = [UIColor redColor].CGColor;
layer.lineJoin = kCALineJoinRound;
layer.frame = self.bounds;

self.selectLayer = layer;

[self.layer addSublayer:self.selectLayer];    
}

更新其路徑值即可.這樣就會覆蓋顯示出來

-(void)uploadTextLineViewWithInex: (NSInteger)index
{
 UIBezierPath * bezierPath = [UIBezierPath bezierPath];

if (index == 1000) {
}else{
    [bezierPath moveToPoint:CGPointMake(index * self.sizeW, 0)];
    [bezierPath addLineToPoint:CGPointMake((index + 1) * self.sizeW, 0)];
    [bezierPath addLineToPoint:CGPointMake((index + 1) * self.sizeW, textViewH)];
    [bezierPath addLineToPoint:CGPointMake(index * self.sizeW, textViewH)];
    [bezierPath addLineToPoint:CGPointMake(index * self.sizeW, 0)];
}
self.selectLayer.path = bezierPath.CGPath;
}

到這里一個支付寶密碼的輸入框大致已經(jīng)完成.是不是超級簡單.只需要處理一點細(xì)節(jié)即可.我在demo中已經(jīng)添加了選中樣式.其他的邊框顏色文字顏色等大家自定義即可

.思路2.

來源:看到某個App使用的是有光標(biāo)的驗證碼輸入框.所以想通過以上方式來添加光標(biāo).但是沒有找到相應(yīng)的資料.所以采用的最直男的方式:

創(chuàng)建多個UITextField,這個看似沒有什么好講的.確實是一個體力活.但里面還是有一些坑

1.首先聲明一個類繼承自UiView
23.同上
4.整體---局部---整體
整體:因為是多個控件組合而成.為了外部方便調(diào)用.所以準(zhǔn)備了兩個方法.整體的成為或辭去第一響應(yīng)者
    //整個控件成為第一響應(yīng)
    -(void)codeView_BecomeFirstResponder
    {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowOnDelay:) name:UIKeyboardWillShowNotification object:nil];
        [self codeViewBecomeFirstResponderWithTag:self.seletTag];
        self.codeView_IsFirstResponder = YES;
    }

    //整個控件辭去第一響應(yīng)
    -(void)codeView_ResignFirstResponder
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
        //辭去第一響應(yīng)者
        [self uploadTextLineViewWithInex:1000];
        [self removeXButtonFromKeyBoard];
        [self codeViewResignFirstResponderWithTag:self.seletTag];
        self.codeView_IsFirstResponder = NO;
    }
局部:UITextField之間的切換.我們使用單個控件成為第一響應(yīng)或辭去第一響應(yīng).
   //單個控件成為第一響應(yīng)者
    - (void)codeViewBecomeFirstResponderWithTag:(NSInteger)tag
    {
        self.seletTag = tag;
        [self uploadTextLineViewWithInex:self.seletTag - 1];
        UITextField *textField = [self viewWithTag:self.seletTag];
        textField.enabled = YES;
        [textField becomeFirstResponder];
        
        self.flogIndex = 0;
    }

    //單個控件辭去第一響應(yīng)者
    -(void)codeViewResignFirstResponderWithTag:(NSInteger)tag
    {
        UITextField *textField = [self viewWithTag:tag];
        textField.enabled = NO;
        [textField resignFirstResponder];
        self.flogIndex = 0;
        
    }
整體:都是UITextField.所以訂閱一個文本更改的通知

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changTextFieldTextNotification) name:UITextFieldTextDidChangeNotification object:nil];

可以統(tǒng)一使用這個通知做"無文本->有文本"的事情.
為什么說是增加的事情.因為UIKeyboardTypeNumberPad類型的鍵盤.如果文本框沒有文本.我們點擊刪除按鈕.不會收到該通知.

既然這樣?
那么刪除文本框怎么操作呢?

看到一個伙伴的實現(xiàn)方式:每個UITextField選中的時候添加一個@“ “空字符.這樣刪除的時候.就能監(jiān)聽到通知.這樣也能實現(xiàn)

我采取的方式是粗暴的:直接在鍵盤的刪除按鈕上添加一個刪除按鈕.取代它.

Pasted Graphic 2.tiff.jpg
監(jiān)聽鍵盤即將出現(xiàn)的通知.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowOnDelay:) name:UIKeyboardWillShowNotification object:nil];

  #pragma mark ==========自定義鍵盤.================

    -(void)keyboardWillShowOnDelay:(NSNotification *)notification {
        [self performSelector:@selector(keyboardWillShow:) withObject:nil afterDelay:0.1];
    }

    - (void)keyboardWillShow:(NSNotification *)notification {
        
        NSUInteger cnt = [UIApplication sharedApplication].windows.count;
        UIWindow *keyboardWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:cnt - 1];
        if (!self.deleteBtn.superview) {
            [keyboardWindow addSubview:self.deleteBtn];
            [keyboardWindow bringSubviewToFront:self.deleteBtn];
        }
    }

有創(chuàng)建.就需要刪除.否則當(dāng)前界面其他輸入控件喚起鍵盤時.也會觸發(fā)通知.就會有問題
- (void)removeXButtonFromKeyBoard
{
[self.deleteBtn removeFromSuperview];
self.deleteBtn.hidden = YES;
self.deleteBtn = nil;
}

所以在整體成為第一響應(yīng)者的時候注冊通知.辭去第一響應(yīng)的時候注銷通知

點擊刪除按鈕的響應(yīng)事件.
  -(void)DeleteButtonDidTouch:(UIButton *)btn
    {
        NSLog(@"=刪除=====12345555");
        if (self.seletTag != 1) {
            UITextField *textField = [self viewWithTag:self.seletTag];
            [self codeViewResignFirstResponderWithTag:self.seletTag];
            textField.text = nil;
            self.seletTag -= 1;
            UITextField *selectTextField = [self viewWithTag:self.seletTag];
            selectTextField.text = nil;
            [self codeViewBecomeFirstResponderWithTag:self.seletTag];
        }
    }
5.本控件與外部的整體交互:
  -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        if (!self.codeView_IsFirstResponder) {
            UITextField *textField = [self viewWithTag:self.seletTag];
            if (self.codeNum == self.seletTag) {
                UITextField *textField = [self viewWithTag:self.seletTag];
                textField.text = nil;
            }
            [self codeView_BecomeFirstResponder];
            textField.enabled = YES;
            [textField becomeFirstResponder];
            
        }else{
            
            [self codeView_ResignFirstResponder];
        }
    }

如果是本控件的點擊.那么可以很輕松的自己設(shè)定為成為或辭去第一響應(yīng).
但有一種情況.當(dāng)我點擊了別的輸入文本時.本控件會辭去第一響應(yīng).這個只能使用UITextField的代理.即UITextField結(jié)束時調(diào)用.
但是局部的UITextField也會有辭去第一響應(yīng)的時候.
所以此處我使用一個比較low的方法.即找規(guī)律.

如果是本控件自己辭去第一響應(yīng).那么textFieldShouldEndEditing會調(diào)用2次-> 再調(diào)textFieldDidEndEditing1次.
如果是點擊其他輸入框辭去的第一響應(yīng).那么textFieldShouldEndEditing會調(diào)用1次-> 再調(diào)textFieldDidEndEditing1次.

 - (BOOL)textFieldShouldEndEditing:(UITextField *)textField
    {
        self.flogIndex ++;
        return YES;
    }

    - (void)textFieldDidEndEditing:(UITextField *)textField
    {
        
        if (self.flogIndex == 2) {
            //那么是正常退出.不用理會
        }else if(self.flogIndex == 1)
        {
            //點擊轉(zhuǎn)到其他編輯文本的辭去第一響應(yīng).應(yīng)該要刪除刪除按鈕
            NSLog(@"===========self.selectTag %zd",self.seletTag);
            self.codeView_IsFirstResponder = NO;
            [self codeView_ResignFirstResponder];
        }
    }
6.添加選中效果同上.

至此第二種思路也已經(jīng)完成.能獲取到光標(biāo).實現(xiàn)的方式真的很多.如果有時間自己實現(xiàn)和把人家的代碼照搬過來.自己會理解的更透徹

謝謝:iOS 簡易文本控件開發(fā)(UIKeyInput協(xié)議學(xué)習(xí))
謝謝:iOS開發(fā):自定義數(shù)字鍵盤(兩種方式)
.希望對你有幫助:https://github.com/FQDEVER/FQ_CodeTextView
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末工闺,一起剝皮案震驚了整個濱河市芭碍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌妓盲,老刑警劉巖浩嫌,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件檐迟,死亡現(xiàn)場離奇詭異,居然都是意外死亡码耐,警方通過查閱死者的電腦和手機(jī)追迟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骚腥,“玉大人敦间,你說我怎么就攤上這事∈” “怎么了廓块?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纯露。 經(jīng)常有香客問我剿骨,道長,這世上最難降的妖魔是什么埠褪? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任浓利,我火速辦了婚禮,結(jié)果婚禮上钞速,老公的妹妹穿的比我還像新娘贷掖。我一直安慰自己,他們只是感情好渴语,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布苹威。 她就那樣靜靜地躺著,像睡著了一般驾凶。 火紅的嫁衣襯著肌膚如雪牙甫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天调违,我揣著相機(jī)與錄音窟哺,去河邊找鬼。 笑死技肩,一個胖子當(dāng)著我的面吹牛且轨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼旋奢,長吁一口氣:“原來是場噩夢啊……” “哼泳挥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起至朗,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屉符,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后爽丹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筑煮,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年粤蝎,在試婚紗的時候發(fā)現(xiàn)自己被綠了真仲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡初澎,死狀恐怖秸应,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情碑宴,我是刑警寧澤软啼,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站延柠,受9級特大地震影響祸挪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贞间,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一贿条、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧增热,春花似錦整以、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至摄咆,卻和暖如春凡蚜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吭从。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工朝蜘, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人影锈。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓芹务,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鸭廷。 傳聞我的和親對象是個殘疾皇子枣抱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

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