密碼輸入框:HQLPasswordViewDemo

之前一篇文章:密碼輸入框:CYPasswordView_Block 源碼解析 粗略的分析了 CYPasswordView 的源碼呈宇,因為要用到,但是總覺得實現(xiàn)的方式不夠優(yōu)雅昔榴,于是我又照著重寫了一遍黍析,在大致實現(xiàn)方式基本不變的情況下,優(yōu)化了些許地方:

一史煎、框架結(jié)構(gòu)


框架結(jié)構(gòu)基本不變:

二、HQLPasswordBackgroundView


背景視圖:

修改或者優(yōu)化的地方:

  • 標(biāo)題改用 label 標(biāo)簽的形式顯示驳糯;
  • drawRect: 方法中只畫了背景視圖和輸入框篇梭;
  • 重構(gòu)了重置小圓點的實現(xiàn)方式;

1?? 標(biāo)題改用 label 標(biāo)簽的形式顯示

#pragma mark - Lifecycle

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setupSubViews];
    }
    return self;
}

/** 添加子控件 */
- (void)setupSubViews {
    [self addSubview:self.titleLabel];
    [self addSubview:self.closeButton];
    [self addSubview:self.forgetPwdButton];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    // 設(shè)置【標(biāo)題】的坐標(biāo)
    self.titleLabel.centerX = HQLScreenWidth * 0.5;
    self.titleLabel.centerY = HQLPasswordViewTitleHeight * 0.5;
    
    // 設(shè)置【關(guān)閉按鈕】的坐標(biāo)
    self.closeButton.width  = HQLPasswordViewCloseButtonWH;
    self.closeButton.height = HQLPasswordViewCloseButtonWH;
    self.closeButton.x = HQLPasswordViewCloseButtonMarginLeft;
    self.closeButton.centerY = HQLPasswordViewTitleHeight * 0.5;
    
    // 設(shè)置【忘記密碼】按鈕的坐標(biāo)
    self.forgetPwdButton.x = HQLScreenWidth - (HQLScreenWidth - HQLPasswordViewTextFieldWidth) * 0.5 - self.forgetPwdButton.width;
    self.forgetPwdButton.y = HQLPasswordViewTitleHeight + HQLPasswordViewTextFieldMarginTop + HQLPasswordViewTextFieldHeight + HQLPasswordViewForgetPWDButtonMarginTop;
}

2?? drawRect: 方法中只畫了背景視圖和輸入框酝枢;

- (void)drawRect:(CGRect)rect {
    // 畫背景視圖
    UIImage *backgroundImage =
        [UIImage imageNamed:HQLPasswordViewSrcName(@"password_background")];
    [backgroundImage drawInRect:rect];
    
    // 畫輸入框
    UIImage *imgTextField =
        [UIImage imageNamed:HQLPasswordViewSrcName(@"password_textfield")];
    [imgTextField drawInRect:[self textFieldRect]];
}

3?? 重構(gòu)了重置小圓點的實現(xiàn)方式很洋;

  1. 首先數(shù)組中存放的是6個 dotsImageView 對象,對應(yīng)6個不同位置的 ●
- (NSMutableArray *)dotsImgArray {
    if (!_dotsImgArray) {
        _dotsImgArray = [NSMutableArray arrayWithCapacity:KPasswordNumber];
        for (int i = 0; i < KPasswordNumber; i++) {
            
            // textField 的 Rect
            CGFloat textFieldW = HQLPasswordViewTextFieldWidth;
            CGFloat textFieldH = HQLPasswordViewTextFieldHeight;
            CGFloat textFieldX = (HQLScreenWidth - textFieldW) * 0.5;
            CGFloat textFieldY = HQLPasswordViewTitleHeight + HQLPasswordViewTextFieldMarginTop;
           
            // 圓點 的 Rect
            CGFloat pointW = HQLPasswordViewPointnWH;
            CGFloat pointH = HQLPasswordViewPointnWH;
            CGFloat pointY = textFieldY + (textFieldH - pointH) * 0.5;
            // 一個格子的寬度
            CGFloat cellW = textFieldW / KPasswordNumber;
            CGFloat padding = (cellW - pointW) * 0.5;
            // 圓點的 X
            CGFloat pointX = textFieldX + (2 * i + 1) * padding + i * pointW;
            // 添加圓形圖片
            UIImage *dotsImage =
                [UIImage imageNamed:HQLPasswordViewSrcName(@"password_point")];
            UIImageView *dotsImageView =
                [[UIImageView alloc] initWithImage:dotsImage];
            dotsImageView.contentMode = UIViewContentModeScaleAspectFit;
            dotsImageView.frame = CGRectMake(pointX, pointY, pointW, pointH);
            // 先全部隱藏
            dotsImageView.hidden = YES;
            
            [self addSubview:dotsImageView];
            [_dotsImgArray addObject:dotsImageView];
        }
    }
    return _dotsImgArray;
}
  1. 通過傳入當(dāng)前密碼的長度 length 重置小圓點隧枫,比 length值大的 dotsImgArray 視圖設(shè)置為顯示喉磁,比 length值小的 dotsImgArray 視圖設(shè)置為隱藏谓苟。
// 重置圓點
- (void)resetDotsWithLength:(NSUInteger)length {
    for (int i = 0; i < self.dotsImgArray.count; i++) {
        if (length == 0 || i >= length) {
            ((UIImageView *)[self.dotsImgArray objectAtIndex:i]).hidden = YES;
        }else {
            ((UIImageView *)[self.dotsImgArray objectAtIndex:i]).hidden = NO;
        }
    }
}

HQLPasswordView

密碼輸入視圖:

修改或者優(yōu)化的地方:

  • 密碼輸入框 pwdTextField 和畫上去的輸入框圖片一樣大,可以響應(yīng)用戶觸摸并彈出鍵盤协怒,而不是 CGRectMake(0, 0, 1, 1) 涝焙;
  • 之前多出來的單擊手勢也不浪費,組織又有新任務(wù)了:用戶如果觸摸上方灰色的蒙版視圖也是可以關(guān)閉密碼輸入框的孕暇。
  • 因為修改了小圓點的實現(xiàn)方式仑撞,所以 UITextFieldDelegate 委托協(xié)議實現(xiàn)方式也有改動。

1?? 密碼輸入框 pwdTextField 可以響應(yīng)用戶觸摸并彈出鍵盤

只要把它所有的顏色設(shè)置為透明就可以了妖滔。

- (UITextField *)pwdTextField {
    if (!_pwdTextField) {
        _pwdTextField = [[UITextField alloc] init];
        _pwdTextField.frame = [self.backgroundView textFieldRect];
        _pwdTextField.backgroundColor = [UIColor clearColor];
        _pwdTextField.textColor = [UIColor clearColor];
        _pwdTextField.tintColor = [UIColor clearColor];
        _pwdTextField.keyboardType = UIKeyboardTypeNumberPad;
        _pwdTextField.delegate = self;
    }
    return _pwdTextField;
}

2?? 用戶如果觸摸上方灰色的蒙版視圖可以關(guān)閉密碼輸入框隧哮。

/** 
 用戶點擊事件,觸摸灰色蒙版區(qū)域,實現(xiàn)關(guān)閉操作
 */
- (void)tap:(UITapGestureRecognizer *)recognizer {
    // 獲取點擊的坐標(biāo)位置
    CGPoint point = [recognizer locationInView:self];
    // 輸入框上方的蒙版區(qū)域
    CGRect frame = CGRectMake(0,
                              0,
                              HQLScreenWidth,
                              HQLScreenHeight - HQLPasswordInputViewHeight);
    // 判斷點擊區(qū)域是否包含在蒙版矩形中
    if (CGRectContainsPoint(frame, point)) {
        [self removePasswordView];
    }
}

/** 移除密碼輸入視圖 */
- (void)removePasswordView {
    [self.pwdTextField resignFirstResponder];
    [self removeFromSuperview];
}

3?? UITextFieldDelegate 委托協(xié)議實現(xiàn)方式也有改動:

用協(xié)議的方式自動重置密碼輸入框座舍,而不是手動燒腦判斷

每次彈出鍵盤就重置密碼框

1.第一次跟隨密碼輸入視圖彈出會調(diào)用沮翔;

2.提示密碼輸入錯誤,再次輸入密碼會調(diào)用曲秉;

#pragma mark - UITextFieldDelegate

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    NSLog(@"%@",NSStringFromSelector(_cmd));
    
    // 每次 TextField 開始編輯采蚀,都要重置密碼框
    [self clearUpPassword];
}

// 清除密碼
- (void)clearUpPassword {
    // 1.清空輸入文本框密碼
    self.pwdTextField.text = @"";
    // 2.清空黑色圓點
    [self.backgroundView resetDotsWithLength:0];
    // 3.隱藏加載圖片和文字
    self.rotationImageView.hidden = YES;
    self.loadingTextLabel.hidden  = YES;
}

// 每當(dāng)用戶操作導(dǎo)致其文本更改時,文本字段將調(diào)用此方法承二。 使用此方法來驗證用戶鍵入的文本榆鼠。 例如,您可以使用此方法來防止用戶輸入任何數(shù)字亥鸠,但不能輸入數(shù)值妆够。
// 每當(dāng)一個字符被鍵入時,都會首先調(diào)用此方法负蚊,詢問是否應(yīng)該將輸入的字符添加進(jìn) TextField 中神妹。
// 因此調(diào)用該方法時,正被輸入的字符實際上還沒有被添加進(jìn) TextField 中
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {    
    // 輸入進(jìn) TextField 的數(shù)字個數(shù)
    NSUInteger numberLength = textField.text.length + string.length;
    
    if([string isEqualToString:@""]) {
        // 1.判斷是不是刪除鍵盖桥?
        [self.backgroundView resetDotsWithLength:numberLength - 1];
        return YES;
    } else if(numberLength >= KPasswordNumber) {
        // 2.判斷此次輸入數(shù)字的是不是第6個數(shù)字灾螃?
        [self.backgroundView resetDotsWithLength:numberLength];
        // 2.1 收起鍵盤
        [self.pwdTextField resignFirstResponder];
        // 2.2 發(fā)起請求 Block
        if (self.finishBlock) {
            [self startLoading];
            NSString *password = [textField.text stringByAppendingString:string];
            self.finishBlock(password);
        }
        return NO;
    } else {
        // 3.每次鍵入一個值题翻,都要重設(shè)黑點
        [self.backgroundView resetDotsWithLength:numberLength];
        return YES;
    }
}

4?? 其它的沒什么變化揩徊,少了幾個移除視圖的一些重置方法

UIView 動畫

+ (void)animateWithDuration:(NSTimeInterval)duration 
                      delay:(NSTimeInterval)delay 
                    options:(UIViewAnimationOptions)options 
                  animations:(void (^)(void))animations 
                  completion:(void (^ __nullable)(BOOL finished))completion ;

[UIView animateWithDuration:(NSTimeInterval)          // 動畫的持續(xù)時間
                      delay:(NSTimeInterval)          // 動畫執(zhí)行的延遲時間
                    options:(UIViewAnimationOptions)  // 執(zhí)行的動畫選項,
                 animations:^{

        // 要執(zhí)行的動畫代碼
 } completion:^(BOOL finished) {
        // 動畫執(zhí)行完畢后的調(diào)用
}];

通過指定動畫持續(xù)時間嵌赠、動畫延遲塑荒、執(zhí)行動畫選項和動畫完成后回調(diào)的 Block 對象 更改一個或多個視圖的動畫。

使用示例:

- (IBAction)paymentButtonDidClicked:(id)sender {

    self.passwordView = [[HQLPasswordView alloc] init];
    [self.passwordView showInView:self.view.window];
    self.passwordView.title = @"我是標(biāo)題";
    self.passwordView.loadingText = @"正在支付中...";
    self.passwordView.closeBlock = ^{
        NSLog(@"取消支付回調(diào)...");
    };
    self.passwordView.forgetPasswordBlock = ^{
        NSLog(@"忘記密碼回調(diào)...");
    };
    WS(weakself);
    self.passwordView.finishBlock = ^(NSString *password) {
        NSLog(@"完成支付回調(diào)...");
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kRequestTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            flag = !flag;
            if (flag) {
                // 購買成功姜挺,跳轉(zhuǎn)到成功頁
                [weakself.passwordView requestComplete:YES message:@"購買成功"];
                
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)( KDelay* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    // 從父視圖移除密碼輸入視圖
                    [weakself.passwordView removePasswordView];
                });
            } else {
                // 購買失敗齿税,跳轉(zhuǎn)到失敗頁
                [weakself.passwordView requestComplete:NO];
                
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(KDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    // 購買失敗的處理,也可以繼續(xù)支付
                    
                });
            }
        });
    };
}

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凌箕,一起剝皮案震驚了整個濱河市拧篮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牵舱,老刑警劉巖串绩,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芜壁,居然都是意外死亡礁凡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門慧妄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顷牌,“玉大人,你說我怎么就攤上這事塞淹】呃叮” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵窖铡,是天一觀的道長疗锐。 經(jīng)常有香客問我,道長费彼,這世上最難降的妖魔是什么滑臊? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮箍铲,結(jié)果婚禮上雇卷,老公的妹妹穿的比我還像新娘。我一直安慰自己颠猴,他們只是感情好关划,可當(dāng)我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翘瓮,像睡著了一般贮折。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上资盅,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天调榄,我揣著相機(jī)與錄音,去河邊找鬼呵扛。 笑死每庆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的今穿。 我是一名探鬼主播缤灵,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腮出?” 一聲冷哼從身側(cè)響起帖鸦,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胚嘲,沒想到半個月后富蓄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡慢逾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年立倍,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侣滩。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡口注,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出君珠,到底是詐尸還是另有隱情寝志,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布策添,位于F島的核電站材部,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏唯竹。R本人自食惡果不足惜乐导,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浸颓。 院中可真熱鬧物臂,春花似錦、人聲如沸产上。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晋涣。三九已至仪媒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谢鹊,已是汗流浹背算吩。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留撇贺,地道東北人赌莺。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓冰抢,卻偏偏與公主長得像松嘶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挎扰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,619評論 2 354

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,077評論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫翠订、插件巢音、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,094評論 4 62
  • 青春似一顆蠶豆, 彎彎的壯實尽超, 同腎臟一樣充滿活力官撼。 青春中的我們做著各式的夢, 夢過清華似谁,夢過富足傲绣, 夢過攬我心...
    深秋之吻閱讀 357評論 0 0
  • 姥姥去世已經(jīng)很多年了,每次想起姥姥巩踏,總想寫點什么秃诵,但因為各種原因終沒寫成,今天看了一篇文章塞琼,引起了我對姥姥的懷念菠净。...
    梅好時光er閱讀 363評論 0 2
  • 寶相莊嚴(yán)袈裟傅,佛光普照萬民心彪杉∫阃苦口婆心,救蒼生水火派近。 清幽深處浮屠現(xiàn)攀唯,鐘聲凈寺僧侶意。參禪悟道渴丸,領(lǐng)諸佛之心革答。
    遨游于天際閱讀 153評論 0 6