之前一篇文章:密碼輸入框: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)方式很洋;
- 首先數(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;
}
- 通過傳入當(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ù)支付
});
}
});
};
}