密碼輸入框:CYPasswordView_Block 源碼解析

支付類應(yīng)用通常都需要輸入6位支付密碼,有的是用 AlertView 的方式實(shí)現(xiàn)的运挫,例如在 iPhone的 App Store 中下載應(yīng)用侵俗,有時(shí)會(huì)彈窗讓你輸入密碼,界面大概是這樣的:

還有一種類似這樣的痛倚,把輸入框放在單獨(dú)的頁面上:

支付寶微信做的是類似半模態(tài)視圖的界面(下面的是淘票票的輸入密碼界面):

網(wǎng)上搜尋了許久,找了個(gè)最接近第三種方式的 Demo :

CYPasswordView_Block 源碼解析

一耸峭、概述


CYPasswordView_Block 是一個(gè)模仿支付寶輸入支付密碼的密碼框(這是一個(gè)用block傳遞事件的版本桩蓉,避免了因多級(jí)頁面切換并且都彈出密碼框而造成通知監(jiān)聽混亂的bug)

二、使用方法


- (IBAction)showPasswordView:(id)sender {

    WS(weakSelf);
    // 實(shí)例化 CYPasswordView 對(duì)象
    self.passwordView = [[CYPasswordView alloc] init];
    // 彈出密碼框
    [self.passwordView showInView:self.view.window];
    self.passwordView.loadingText = @"提交中...";
    // 關(guān)閉密碼框
    self.passwordView.cancelBlock = ^{
        [weakSelf cancel];
    };
    // 忘記密碼
    self.passwordView.forgetPasswordBlock = ^{
        [weakSelf forgetPWD];
    };
    // 密碼輸入完成的回調(diào)
    self.passwordView.finish = ^(NSString *password) {
        [weakSelf.passwordView hidenKeyboard];
        [weakSelf.passwordView startLoading];
        CYLog(@"cy ========= 發(fā)送網(wǎng)絡(luò)請(qǐng)求  pwd=%@", password);
        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:@"購買成功……"];
                [weakSelf.passwordView stopLoading];
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [weakSelf.passwordView hide];
                });
            } else {
                // 購買失敗触机,跳轉(zhuǎn)到失敗頁
                [weakSelf.passwordView requestComplete:NO];
                [weakSelf.passwordView stopLoading];
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDelay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [weakSelf.passwordView hide];
                });
            }
        });
    };
}

三帚戳、實(shí)現(xiàn)方式


需要哪些控件元素?

照著圖片看需要哪些控件元素儡首,自上而下片任,從左往右看:

  1. 灰色的背景圖層 coverView,用于覆蓋上一個(gè)視圖;
  2. 占據(jù)半個(gè)屏幕的輸入框視圖蔬胯,該視圖上有:
    • 標(biāo)題文本对供;
    • 關(guān)閉按鈕、忘記密碼按鈕氛濒;
    • 帶6個(gè)格子的密碼輸入框产场;
    • 加載圖片、加載文本

如何顯示密碼舞竿?

  1. 鍵盤上輸入數(shù)字時(shí)京景,并不會(huì)實(shí)時(shí)顯示123456...,而是顯示 ●●●●●●骗奖, 這是為了密碼輸入安全起見的一貫做法确徙。為 UITextField 設(shè)置 secureTextEntry 屬性為 YES≈醋溃可以禁用用戶在視圖中復(fù)制文本的功能鄙皇。 還會(huì)用* 代替輸入的字符。
  2. 但是原生的控件無法實(shí)現(xiàn)在一個(gè)格子里輸入一個(gè)字符仰挣,而且還要在這個(gè)格子中心顯示●伴逸,所以密碼輸入的6個(gè)方框和●都是假象,是用代碼畫上去的膘壶。
  3. 每次在 UITextField 中輸入错蝴、刪除數(shù)字,都要進(jìn)行判斷颓芭,根據(jù) UITextField 中文本的個(gè)數(shù)同步添加或刪除●
  4. 邊界情況漱竖,當(dāng)輸入的數(shù)字個(gè)數(shù)大于等于6個(gè),禁止輸入畜伐。
  5. 為了提高用戶體驗(yàn),當(dāng)輸入完第6個(gè)數(shù)字時(shí)躺率,自動(dòng)關(guān)閉鍵盤玛界,發(fā)起網(wǎng)絡(luò)請(qǐng)求以驗(yàn)證密碼。

CYPasswordView_Block 源碼分析

CYPasswordView代碼結(jié)構(gòu)

  • CYPasswordView.bundle

    圖片庫悼吱,包含所有圖片素材

  • CYConst

    通用宏定義慎框、密碼框各控件的尺寸常量

  • UIView+Extension

    簡(jiǎn)化設(shè)置視圖尺寸的范疇方法

  • CYPasswordInputView

    密碼框的輸入背景視圖(把 password_background@2x.pngpassword_textfield@2x.png兩個(gè)背景圖片繪制上去)后添,主要包含標(biāo)題笨枯、關(guān)閉按鈕、忘記密碼按鈕、以及繪制6個(gè)●的操作馅精。

  • CYPasswordView

    包含蒙版圖層严嗜、CYPasswordInputView、UITextField 控件洲敢、再添加加載圖片漫玄、加載文字標(biāo)簽、處理判斷輸入邏輯压彭、加載顯示旋轉(zhuǎn)動(dòng)畫等

前三個(gè)不多說了睦优,主要看后兩個(gè)

CYPasswordInputView

在指定初始化方法 initWithFrame:中添加 關(guān)閉按鈕忘記密碼按鈕,然后在 layoutSubviews 方法中對(duì)這兩個(gè)控件重新布局,按鈕的 Target-Action 點(diǎn)擊事件通過 Blocks 傳遞壮不。

#pragma mark  - 生命周期方法
- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self.backgroundColor = [UIColor clearColor];
        /** 添加子控件 */
        [self setupSubViews];
    }
    return self;
}

/** 添加子控件 */
- (void)setupSubViews {
    /** 關(guān)閉按鈕 */
    UIButton *btnCancel = [UIButton buttonWithType:UIButtonTypeCustom];
    [self addSubview:btnCancel];
    [btnCancel setBackgroundImage:[UIImage imageNamed:CYPasswordViewSrcName(@"password_close")] forState:UIControlStateNormal];
    [btnCancel setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
    self.btnClose = btnCancel;
    [self.btnClose addTarget:self action:@selector(btnClose_Click:) forControlEvents:UIControlEventTouchUpInside];
    
    /** 忘記密碼按鈕 */
    UIButton *btnForgetPWD = [UIButton buttonWithType:UIButtonTypeCustom];
    [self addSubview:btnForgetPWD];
    [btnForgetPWD setTitle:@"忘記密碼汗盘?" forState:UIControlStateNormal];
    [btnForgetPWD setTitleColor:CYColor(0, 125, 227) forState:UIControlStateNormal];
    btnForgetPWD.titleLabel.font = CYFont(13);
    [btnForgetPWD sizeToFit];
    self.btnForgetPWD = btnForgetPWD;
    [self.btnForgetPWD addTarget:self action:@selector(btnForgetPWD_Click:) forControlEvents:UIControlEventTouchUpInside];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    // 設(shè)置關(guān)閉按鈕的坐標(biāo)
    self.btnClose.width = CYPasswordViewCloseButtonWH;
    self.btnClose.height = CYPasswordViewCloseButtonWH;
    self.btnClose.x = CYPasswordViewCloseButtonMarginLeft;
    self.btnClose.centerY = CYPasswordViewTitleHeight * 0.5;
    
    // 設(shè)置忘記密碼按鈕的坐標(biāo)
    self.btnForgetPWD.x = CYScreenWidth - (CYScreenWidth - CYPasswordViewTextFieldWidth) * 0.5 - self.btnForgetPWD.width;
    self.btnForgetPWD.y = CYPasswordViewTitleHeight + CYPasswordViewTextFieldMarginTop + CYPasswordViewTextFieldHeight + CYPasswordViewForgetPWDButtonMarginTop;
}

// 按鈕點(diǎn)擊
- (void)btnClose_Click:(UIButton *)sender {
    if (self.cancelBlock) {
        self.cancelBlock();
    }
    [self.inputNumArray removeAllObjects];
}

- (void)btnForgetPWD_Click:(UIButton *)sender {
    if (self.forgetPasswordBlock) {
        self.forgetPasswordBlock();
    }
}

drawRect: 方法中把圖片畫在圖層上,標(biāo)題也并不是 Label 控件询一,而是畫上去的字符串隐孽,6個(gè)●也同時(shí)被畫上去了,它是遍歷數(shù)組中元素的個(gè)數(shù)畫上去的家凯。

所以說缓醋,每次在輸入框中刪除、添加數(shù)字绊诲,都會(huì)通過調(diào)用 [self setNeedsDisplay]drawRect:方法中的所有視圖重新繪制一遍送粱,目的是需要遍歷數(shù)組,重畫●

- (void)drawRect:(CGRect)rect {
    // 畫背景視圖
    UIImage *imgBackground =
        [UIImage imageNamed:CYPasswordViewSrcName(@"password_background")];
    [imgBackground drawInRect:rect];
    
    // 畫輸入框
    CGFloat textfieldY = CYPasswordViewTitleHeight + CYPasswordViewTextFieldMarginTop;
    CGFloat textfieldW = CYPasswordViewTextFieldWidth;
    CGFloat textfieldX = (CYScreenWidth - textfieldW) * 0.5;
    CGFloat textfieldH = CYPasswordViewTextFieldHeight;
    UIImage *imgTextfield =
        [UIImage imageNamed:CYPasswordViewSrcName(@"password_textfield")];
    [imgTextfield drawInRect:CGRectMake(textfieldX, textfieldY, textfieldW, textfieldH)];
    
    // 畫標(biāo)題
    NSString *title = (self.title ? self.title : @"輸入交易密碼");
    NSDictionary *arrts = @{NSFontAttributeName:CYFontB(18)};
    CGSize size = [title boundingRectWithSize:CGSizeMake(MAXFLOAT, MAXFLOAT)
                                      options:NSStringDrawingUsesLineFragmentOrigin
                                   attributes:arrts
                                      context:nil].size;
    CGFloat titleW = size.width;
    CGFloat titleH = size.height;
    CGFloat titleX = (self.width - titleW) * 0.5;
    CGFloat titleY = (CYPasswordViewTitleHeight - titleH) * 0.5;
    CGRect titleRect = CGRectMake(titleX, titleY, titleW, titleH);
    
    NSMutableDictionary *attr = [NSMutableDictionary dictionary];
    attr[NSFontAttributeName] = CYFontB(18);
    attr[NSForegroundColorAttributeName] = CYColor(102, 102, 102);
    [title drawInRect:titleRect withAttributes:attr];
    
    
    // 畫點(diǎn)
    UIImage *pointImage =
        [UIImage imageNamed:CYPasswordViewSrcName(@"password_point")];
    CGFloat pointW = CYPasswordViewPointnWH;
    CGFloat pointH = CYPasswordViewPointnWH;
    CGFloat pointY = textfieldY + (textfieldH - pointH) * 0.5;
    __block CGFloat pointX;
    
    // 一個(gè)格子的寬度
    CGFloat cellW = textfieldW / kNumCount;
    CGFloat padding = (cellW - pointW) * 0.5;
    // 根據(jù)數(shù)組中元素的個(gè)數(shù)畫黑點(diǎn)
    [self.inputNumArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        pointX = textfieldX + (2 * idx + 1) * padding + idx * pointW;
        [pointImage drawInRect:CGRectMake(pointX, pointY, pointW, pointH)];
    }];
}

首先來看在類擴(kuò)展中聲明的可變數(shù)組:

/** 保存用戶輸入的數(shù)字集合 */
@property (nonatomic, strong) NSMutableArray *inputNumArray;

響應(yīng)輸入的兩個(gè)方法

#pragma mark  - 私有方法
// 響應(yīng)用戶按下刪除鍵事件
- (void)deleteNumber {
    [self.inputNumArray removeLastObject]; // 如果是刪除鍵掂之,就移除數(shù)組中的最后一個(gè)數(shù)字
    [self setNeedsDisplay]; // 根據(jù)數(shù)組的個(gè)數(shù)重繪整個(gè)視圖
}

// 響應(yīng)用戶按下數(shù)字鍵事件
- (void)number:(NSDictionary *)userInfo {
    NSString *numObj = userInfo[CYPasswordViewKeyboardNumberKey];
    if (numObj.length >= kNumCount) return; // ?? 這行代碼應(yīng)該是永遠(yuǎn)不會(huì)執(zhí)行的吧抗俄。等會(huì)再說為啥
    [self.inputNumArray addObject:numObj]; // 這里每次輸入一個(gè)數(shù)字,就會(huì)把該數(shù)字存進(jìn)數(shù)組中
    [self setNeedsDisplay]; // 根據(jù)數(shù)組的個(gè)數(shù)重繪整個(gè)視圖
}

其實(shí)可以不用重繪整個(gè)視圖世舰,因?yàn)橹皇侵刂眯A點(diǎn)动雹,可以重繪指定區(qū)域視圖:setNeedsDisplayInRect:

// 響應(yīng)用戶按下刪除鍵事件
- (void)deleteNumber {
    [self.inputNumArray removeLastObject];
    [self setNeedsDisplayInRect:[self textFieldRect]];
}

// 響應(yīng)用戶按下數(shù)字鍵事件
- (void)number:(NSDictionary *)userInfo {
    NSString *numObj = userInfo[CYPasswordViewKeyboardNumberKey];
    if (numObj.length >= kNumCount) return;    
    [self.inputNumArray addObject:numObj];
    [self setNeedsDisplayInRect:[self textFieldRect]];
}

- (CGRect)textFieldRect {
    CGFloat textfieldY = CYPasswordViewTitleHeight + CYPasswordViewTextFieldMarginTop;
    CGFloat textfieldW = CYPasswordViewTextFieldWidth;
    CGFloat textfieldX = (CYScreenWidth - textfieldW) * 0.5;
    CGFloat textfieldH = CYPasswordViewTextFieldHeight;
    return CGRectMake(textfieldX, textfieldY, textfieldW, textfieldH);
}

CYPasswordView

再看 CYPasswordView 這個(gè)類,指定初始化方法中添加了幾個(gè)子視圖跟压、蒙版胰蝠、輸入框、響應(yīng)者 UITextField震蒋、加載圖片和加載文字茸塞、單擊手勢(shì)。布局子視圖方法 layoutSubviews中重新布局了加載圖片和加載文字標(biāo)簽的位置查剖。

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:CYScreen.bounds]; // frame 是固定的
    if (self) {
        [self setupUI];
    }
    return self;
}

- (void)setupUI {
    self.backgroundColor = [UIColor clearColor];
    // 蒙版
    [self addSubview:self.coverView];
    // 輸入框
    [self addSubview:self.passwordInputView];
    // 響應(yīng)者
    [self addSubview:self.txfResponsder];
    [self.passwordInputView addSubview:self.imgRotation];
    [self.passwordInputView addSubview:self.lblMessage];
    // 手勢(shì)
    [self addGestureRecognizer:[[UITapGestureRecognizer alloc]
                                initWithTarget:self
                                action:@selector(tap:)]];
}

- (void)layoutSubviews {
    [super layoutSubviews];
    // 調(diào)整旋轉(zhuǎn)圖片布局
    self.imgRotation.centerX = self.passwordInputView.centerX;
    self.imgRotation.centerY = self.passwordInputView.height * 0.5;
    // 調(diào)整提示文本布局
    self.lblMessage.x = 0;
    self.lblMessage.y = CGRectGetMaxY(self.imgRotation.frame) + 20;
    self.lblMessage.width = CYScreenWidth;
    self.lblMessage.height = 30;
}

  • ① 添加單擊手勢(shì)的作用:點(diǎn)擊輸入框钾虐,彈出鍵盤
- (void)tap:(UITapGestureRecognizer *)recognizer {
    // 獲取點(diǎn)擊的坐標(biāo)位置
    CGPoint p = [recognizer locationInView:self.passwordInputView];
    // 6塊矩形輸入框區(qū)域
    CGRect f = CGRectMake(39, 80, 297, 50);
    // 判斷點(diǎn)擊區(qū)域是否包含在輸入框區(qū)域中
    if (CGRectContainsPoint(f, p)) {
        [self.txfResponsder becomeFirstResponder];
    } else {
        NSLog(@"==============");
    }
}

因?yàn)橐憫?yīng)點(diǎn)擊事件,所以蒙版是一個(gè) UIControl

- (UIControl *)coverView {
    if (_coverView == nil) {
        _coverView = [[UIControl alloc] init];
        [_coverView setBackgroundColor:[UIColor blackColor]];
        _coverView.alpha = 0.4;
        _coverView.frame = self.bounds;
    }
    return _coverView;
}
  • ② 為何要手動(dòng)通過點(diǎn)擊事件彈出鍵盤笋庄,而不是點(diǎn)擊 UITextField 自動(dòng)彈出鍵盤 效扫?
// 這里的 UITextField 實(shí)例對(duì)象就像名字一樣倔监,只是作為一個(gè)響應(yīng)者,
// 它的 frame 是 CGRectMake(0, 0, 1, 1)
// 優(yōu)化點(diǎn):其實(shí)還是可以把它設(shè)置得跟圖片一樣大的菌仁,只要把它設(shè)置為透明就行了浩习,背景色、text掘托、tintColor瘦锹、通通設(shè)置為透明色,1.可以充分利用控件本身的特性闪盔。2.不用手動(dòng)添加點(diǎn)擊事件弯院。
- (UITextField *)txfResponsder {
    if (_txfResponsder == nil) {
        _txfResponsder = [[UITextField alloc] init];
        _txfResponsder.delegate = self;
        _txfResponsder.keyboardType = UIKeyboardTypeNumberPad;
        _txfResponsder.frame = CGRectMake(0, 0, 1, 1);
        _txfResponsder.secureTextEntry = YES;
    }
    return _txfResponsder;
}

這個(gè)類中實(shí)現(xiàn)取消按鈕和忘記密碼按鈕依舊是通過 Block 的方式傳遞,只是增加了重置密碼框操作

- (CYPasswordInputView *)passwordInputView {
    WS(weakself);
    if (_passwordInputView == nil) {
        _passwordInputView = [[CYPasswordInputView alloc] init];
      
        _passwordInputView.cancelBlock = ^{
            [weakself cancel];
        };
      
        _passwordInputView.forgetPasswordBlock = ^{
            [weakself forgetPassword];
        };
    }
    return _passwordInputView;
}

/** 輸入框的取消按鈕點(diǎn)擊 */
- (void)cancel {
    [self resetPasswordView];
    if (self.cancelBlock) {
        self.cancelBlock();
    }
}
/** 輸入框的忘記密碼按鈕點(diǎn)擊 */
- (void)forgetPassword {
    [self resetPasswordView];
    if (self.forgetPasswordBlock) {
        self.forgetPasswordBlock();
    }
}

/** 重置密碼框 */
- (void)resetPasswordView {
    [self hidenKeyboard:^(BOOL finished) {
        self.passwordInputView.hidden = YES;
        tempStr = nil;
        [self removeFromSuperview];
        [self hidenKeyboard:nil];
        [self.passwordInputView setNeedsDisplay];
    }];
}

處理鍵盤輸入的 UITextFieldDelegate 協(xié)議方法:

#pragma mark  - 常量區(qū)
// 前面的角落里聲明了一個(gè)變量用戶保存密碼字符串
static NSString *tempStr;

//...

#pragma mark  - <UITextFieldDelegate>
#pragma mark  處理字符串 和 刪除鍵

// 每當(dāng)一個(gè)字符被鍵入時(shí)泪掀,都會(huì)首先調(diào)用此方法听绳,詢問是否應(yīng)該將輸入的字符添加進(jìn) TextField 中。
// 因此調(diào)用該方法時(shí)异赫,正被輸入的字符實(shí)際上還沒有被添加進(jìn) TextField 中
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if (!tempStr) {
        tempStr = string;
    }else{
        // 把正在輸入的數(shù)字和之前所有輸入的數(shù)字拼接起來
        // 因此 tempStr 字符串中存放的是所有的數(shù)字
        tempStr = [NSString stringWithFormat:@"%@%@",tempStr,string];
    }
    // 判斷是否是刪除按鈕椅挣?
    if ([string isEqualToString:@""]) {
        // 響應(yīng)用戶按下刪除鍵事件,刪除小黑點(diǎn)
        [self.passwordInputView deleteNumber];
        // 如果是刪除按鈕塔拳,再判斷容器字符串的長(zhǎng)度鼠证?
        if (tempStr.length > 0) {   //  刪除最后一個(gè)字符串
            NSString *lastStr = [tempStr substringToIndex:[tempStr length] - 1];
            tempStr = lastStr;
        }
    } else{
        // 如果不是刪除按鈕,先判斷容器字符串的長(zhǎng)度是否=6靠抑?
        if (tempStr.length == 6) {
            if (self.finish) {
                self.finish(tempStr);
                self.finish = nil;
            }
            tempStr = nil;
        }
        // 更新 CYPasswordInputView 中的黑點(diǎn)
        // ??把當(dāng)前輸入的數(shù)字通過字典的方式傳遞,key為常量量九,value 是當(dāng)前輸入的數(shù)字
        NSMutableDictionary *userInfoDict = [NSMutableDictionary dictionary];
        userInfoDict[CYPasswordViewKeyboardNumberKey] = string;
        [self.passwordInputView number:userInfoDict];
    }
    return YES;
}

??因?yàn)楫?dāng)前輸入的數(shù)字是通過鍵值對(duì)的方式發(fā)送給 passwordInputView 對(duì)象的,所以它響應(yīng)用戶按下數(shù)字的方法中:

// 響應(yīng)用戶按下數(shù)字鍵事件
- (void)number:(NSDictionary *)userInfo {
    NSString *numObj = userInfo[CYPasswordViewKeyboardNumberKey];
    if (numObj.length >= kNumCount) return; // ?? 
    [self.inputNumArray addObject:numObj]; // 這里每次輸入一個(gè)數(shù)字颂碧,就會(huì)把該數(shù)字存進(jìn)數(shù)組中
    [self setNeedsDisplay]; // 根據(jù)數(shù)組的個(gè)數(shù)重繪整個(gè)視圖
}

方法中的 numObj 就是當(dāng)前輸入的數(shù)字荠列,numObj.length 永遠(yuǎn)等于1,所以我說這行代碼是永遠(yuǎn)不會(huì)執(zhí)行的载城。

彈出密碼框的方法:

// 彈出密碼框
[self.passwordView showInView:self.view.window];

源碼:

- (void)showInView:(UIView *)view {
    [view addSubview:self];
    /** 輸入框起始frame */
    self.passwordInputView.height = CYPasswordInputViewHeight;
    self.passwordInputView.y = self.height;
    self.passwordInputView.width = CYScreenWidth;
    self.passwordInputView.x = 0;
    /** 彈出鍵盤 */
    [self showKeyboard];
}

/** 鍵盤彈出 */
- (void)showKeyboard {
    [self.txfResponsder becomeFirstResponder];
    [UIView animateWithDuration:CYPasswordViewAnimationDuration
                          delay:0
                        options:UIViewAnimationOptionTransitionNone
                     animations:^{
         self.passwordInputView.y = (self.height - self.passwordInputView.height);
                     }
                     completion:^(BOOL finished) {
         NSLog(@" ========= %@", NSStringFromCGRect(self.passwordInputView.frame));
                     }];
}

這段代碼添加了一個(gè)動(dòng)畫讓 passwordInputView 的 Y 點(diǎn)從:

self.height(self.height - self.passwordInputView.height)

就是從底部向上推出的動(dòng)畫肌似,它是先彈出鍵盤,再彈出passwordInputView诉瓦。

最后川队,旋轉(zhuǎn)動(dòng)畫都是對(duì)加載圖片和加載文字作了一些更改:

/**
 *  開始旋轉(zhuǎn)
 */
- (void)startRotation:(UIView *)view {
    _imgRotation.hidden = NO;
    _imgRotation.image = 
      [UIImage imageNamed:CYPasswordViewSrcName(@"password_loading_b")];
    _lblMessage.hidden = NO;
    self.lblMessage.text = _loadingText;
  
    CABasicAnimation* rotationAnimation;
    rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 ];
    rotationAnimation.duration = 2.0;
    rotationAnimation.cumulative = YES;
    rotationAnimation.repeatCount = MAXFLOAT;
    [view.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}

/**
 *  結(jié)束旋轉(zhuǎn)
 */
- (void)stopRotation:(UIView *)view {
    [view.layer removeAllAnimations];
}

- (void)startLoading {
    [self startRotation:self.imgRotation];
    [self.passwordInputView disEnalbeCloseButton:NO];
}

- (void)stopLoading {
    [self stopRotation:self.imgRotation];
    [self.passwordInputView disEnalbeCloseButton:YES];
}

- (void)requestComplete:(BOOL)state {
    if (state) {
        [self requestComplete:state message:@"支付成功"];
    } else {
        [self requestComplete:state message:@"支付失敗"];
    }
}
- (void)requestComplete:(BOOL)state message:(NSString *)message {
    if (state) {
        // 請(qǐng)求成功
        self.lblMessage.text = message;
        self.imgRotation.image = 
          [UIImage imageNamed:CYPasswordViewSrcName(@"password_success")];
    } else {
        // 請(qǐng)求失敗
        self.lblMessage.text = message;
        self.imgRotation.image = 
          [UIImage imageNamed:CYPasswordViewSrcName(@"password_error")];
    }
}

分析完以上源碼后,我又對(duì)該項(xiàng)目重構(gòu)并進(jìn)行了些許修改:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末睬澡,一起剝皮案震驚了整個(gè)濱河市固额,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猴贰,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件河狐,死亡現(xiàn)場(chǎng)離奇詭異米绕,居然都是意外死亡瑟捣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門栅干,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迈套,“玉大人,你說我怎么就攤上這事碱鳞∩@睿” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵窿给,是天一觀的道長(zhǎng)贵白。 經(jīng)常有香客問我,道長(zhǎng)崩泡,這世上最難降的妖魔是什么禁荒? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮角撞,結(jié)果婚禮上呛伴,老公的妹妹穿的比我還像新娘。我一直安慰自己谒所,他們只是感情好热康,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著劣领,像睡著了一般姐军。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剖踊,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天庶弃,我揣著相機(jī)與錄音,去河邊找鬼德澈。 笑死歇攻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梆造。 我是一名探鬼主播缴守,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼镇辉!你這毒婦竟也來了屡穗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤忽肛,失蹤者是張志新(化名)和其女友劉穎村砂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屹逛,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡础废,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年汛骂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片评腺。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡帘瞭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蒿讥,到底是詐尸還是另有隱情蝶念,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布芋绸,位于F島的核電站媒殉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侥钳。R本人自食惡果不足惜适袜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舷夺。 院中可真熱鬧苦酱,春花似錦、人聲如沸给猾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敢伸。三九已至扯饶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間池颈,已是汗流浹背尾序。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留躯砰,地道東北人每币。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像琢歇,于是被迫代替她去往敵國和親兰怠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

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

  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 46,791評(píng)論 22 665
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫李茫、插件揭保、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,124評(píng)論 4 61
  • 漫步在林蔭大道上的兩個(gè)人, 思緒像插上了一對(duì)展開的翅膀魄宏。 不知是什么的原因秸侣, 兩人如同高山上小松鼠般的目光, 若即...
    唯愛12閱讀 303評(píng)論 0 0
  • 大年初三開始播出《三生三世十里桃花》,我偶爾瞟了幾眼味榛。 直到第8集趙夜華上線方篮,才開啟了追劇模式。 目前劇情已經(jīng)進(jìn)行...
    早_哇閱讀 1,519評(píng)論 3 7
  • 昨日忘記打卡励负,早起補(bǔ)上,因文章看的少匕得,留言“昨天助攻的不多”继榆,下面就有小伙伴疑問了“昨日上墻人數(shù)很多“≈樱看了兩遍略吨,...
    豆子121閱讀 276評(píng)論 19 11