iOS开发-4位或者6位验证码按顺序输入实现
之前实现了获取验证码后,验证码依次输入的实现功能, 输入手机号后,点击用短信验证码登录然后在界面点击用短信验证码登录,之后依次输入4位或者6位验证码。这里进行一下记录。
一、整体效果
在实现这个的时候,整体结果为4个显示输入框数字的labelView,有一个current记录当前的输入的第几个数字,有一个输入框在labelView的位置,当输入了数字之后,输入框移动到current+1的labelView的位置,当点击键盘的删除按钮,输入框移动到current-1的labelView位置,重置current后的labelView显示的数字文本。
整体效果如下
二、实现功能
我们有一个labelView来显示数字文本,这个labelView主要就是UILabel,在当前view上添加点击手势。用于显示数字。
INVerifyCodeNumberView.h
#import <UIKit/UIKit.h>
@protocol INVerifyCodeNumberViewDelegate;
@interface INVerifyCodeNumberView : UIView
@property (nonatomic, weak) id<INVerifyCodeNumberViewDelegate>delegate;
@property (nonatomic, strong) NSString *number;
@end
@protocol INVerifyCodeNumberViewDelegate <NSObject>
- (void)tapGestureDidAction;
@end
INVerifyCodeNumberView.m
#import "INVerifyCodeNumberView.h"
#import "UIColor+Addition.h"
@interface INVerifyCodeNumberView ()
@property (nonatomic, strong) UIImageView *lineImageView;
@property (nonatomic, strong) UILabel *textLabel;
@end
@implementation INVerifyCodeNumberView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.lineImageView];
[self addSubview:self.textLabel];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[self.lineImageView addGestureRecognizer:tap];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.textLabel.frame = self.bounds;
self.lineImageView.frame = CGRectMake(0.0, CGRectGetHeight(self.bounds) - 1.0, CGRectGetWidth(self.bounds), 1.0);
}
- (void)setNumber:(NSString *)number {
_number = (number?number:@"");
self.textLabel.text = _number;
[self setNeedsLayout];
}
- (void)tapAction {
if (self.delegate && [self.delegate respondsToSelector:@selector(tapGestureDidAction)]) {
[self.delegate tapGestureDidAction];
}
}
#pragma mark - SETTER/GETTER
- (UIImageView *)lineImageView {
if (!_lineImageView) {
_lineImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
_lineImageView.backgroundColor = [UIColor colorWithHexString:@"CCCCCC"];
_lineImageView.userInteractionEnabled = YES;
}
return _lineImageView;
}
- (UILabel *)textLabel {
if (!_textLabel) {
_textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_textLabel.font = [UIFont systemFontOfSize:18];
_textLabel.textColor = [UIColor colorWithHexString:@"999999"];
_textLabel.backgroundColor = [UIColor clearColor];
_textLabel.text = @"";
_textLabel.textAlignment = NSTextAlignmentCenter;
}
return _textLabel;
}
@end
之后根据验证码的数字的数量来创建多个labelView,有一个current记录当前的输入的第几个数字,有一个输入框在labelView的位置,当输入了数字之后,输入框移动到current+1的labelView的位置,当点击键盘的删除按钮,输入框移动到current-1的labelView位置,重置current后的labelView显示的数字文本。
INVerifyCodeView.h
#import <UIKit/UIKit.h>
#import "UIColor+Addition.h"
@protocol INVerifyCodeViewDelegate;
@interface INVerifyCodeView : UIView
@property (nonatomic, weak) id<INVerifyCodeViewDelegate>actionDelegate;
@property (nonatomic, weak) id delegate;
@property (nonatomic, strong) NSString *phone;
@end
@protocol INVerifyCodeViewDelegate <NSObject>
- (void)loginButtonDidAction;
- (void)codeButtonDidAction;
@end
INVerifyCodeView.m
#import "INVerifyCodeView.h"
#import "UIColor+Addition.h"
#import "INVerifyCodeNumberView.h"
#import "UITextField+Backward.h"
#import "UIImage+Color.h"
static CGFloat kNavHeight = 50.0;
static CGFloat kNavSubHeight = 30.0;
static const NSInteger kCodeNumber = 4;
static const CGFloat kTitleHeight = 30.0;
static const CGFloat kMidPadding = 30.0;
static const CGFloat kVerMidPadding = 20.0;
static const CGFloat kNumberSize = 44.0;
static const CGFloat kLoginHeight = 44.0;
static const CGFloat kCodeBtnHeight = 40.0;
@interface INVerifyCodeView ()<UITextFieldDelegate,INVerifyCodeNumberViewDelegate>
@property (nonatomic, strong) NSMutableArray <INVerifyCodeNumberView *>*codeNumberViews;
@property (nonatomic, strong) UIImageView *bgImageView;
@property (nonatomic, strong) UIVisualEffectView *effectView;
@property (nonatomic, strong) UILabel *navTitleLabel;
@property (nonatomic, strong) UILabel *navSubTitleLabel;
@property (nonatomic, strong) UILabel *phoneTitleLabel;
@property (nonatomic, strong) UITextField *phoneTextField;
@property (nonatomic, strong) UIButton *loginButton;
@property (nonatomic, strong) UIButton *codeButton;
@property (nonatomic, assign) NSInteger currentIndex; // 当前输入框所在的index
@end
@implementation INVerifyCodeView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor colorWithHexString:@"f4f4f4"];
[self addSubview:self.bgImageView];
[self.bgImageView addSubview:self.effectView];
[self addSubview:self.navTitleLabel];
[self addSubview:self.navSubTitleLabel];
//[self addSubview:self.phoneTitleLabel];
[self initNumberViews];
[self addSubview:self.phoneTextField];
//[self addSubview:self.loginButton];
[self addSubview:self.codeButton];
self.currentIndex = 0;
[self addObservers];
self.phone = @"17898989891";
}
return self;
}
- (id)init {
return [self initWithFrame:CGRectZero];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.bgImageView.frame = self.bounds;
self.effectView.frame = self.bgImageView.bounds;
self.navTitleLabel.frame = CGRectMake(kMidPadding, 100, CGRectGetWidth(self.bounds) - 2*kMidPadding, kNavHeight);
self.navSubTitleLabel.frame = CGRectMake(kMidPadding, CGRectGetMaxY(self.navTitleLabel.frame), CGRectGetWidth(self.bounds) - 2*kMidPadding, kNavSubHeight);
//self.phoneTitleLabel.frame = CGRectMake(kMidPadding, CGRectGetMaxY(self.navSubTitleLabel.frame) + kVerMidPadding, CGRectGetWidth(self.bounds) - 2*kMidPadding, kTitleHeight);
NSInteger number = self.codeNumberViews.count;
CGFloat originX = kMidPadding + 2;
CGFloat numPadding = (CGRectGetWidth(self.bounds) - 2*originX - number*kNumberSize)/(number - 1);
INVerifyCodeNumberView *lastView = nil;
for (INVerifyCodeNumberView *numberView in self.codeNumberViews) {
numberView.frame = CGRectMake(lastView?(CGRectGetMaxX(lastView.frame) + numPadding):originX, CGRectGetMaxY(self.navSubTitleLabel.frame) + kVerMidPadding, kNumberSize, kNumberSize);
lastView = numberView;
}
self.phoneTextField.frame = CGRectMake(0.0, 0.0, kNumberSize, kNumberSize);
if (self.currentIndex < self.codeNumberViews.count) {
INVerifyCodeNumberView *numberView = [self.codeNumberViews objectAtIndex:self.currentIndex];
if (numberView.number.length > 0) {
self.phoneTextField.center = CGPointMake(numberView.center.x + 8.0, numberView.center.y);
} else {
self.phoneTextField.center = CGPointMake(numberView.center.x, numberView.center.y);
}
}
//self.loginButton.frame = CGRectMake(originX, CGRectGetMaxY(lastView.frame) + kMidPadding, CGRectGetWidth(self.bounds) - 2*originX, kLoginHeight);
self.codeButton.frame = CGRectMake(kMidPadding, CGRectGetMaxY(lastView.frame) + kVerMidPadding, CGRectGetWidth(self.bounds) - 2*kMidPadding, kCodeBtnHeight);
}
- (void)setDelegate:(id)delegate {
_delegate = delegate;
self.actionDelegate = delegate;
}
- (void)setPhone:(NSString *)phone {
_phone = (phone?phone:@"");
[self setSubAttributeString];
}
- (void)setSubAttributeString {
NSString *title = @"已发送4位验证码至";
NSString *subTitle = self.phone;
NSString *content = [NSString stringWithFormat:@"%@ %@",title, subTitle];
NSRange subRange = [content rangeOfString:subTitle options:NSBackwardsSearch];
NSDictionary *attribute = @{NSForegroundColorAttributeName:[UIColor colorWithHexString:@"999999"],NSFontAttributeName:[UIFont systemFontOfSize:14]};
NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc] initWithString:content];
[attributedStr setAttributes:attribute range:subRange];
self.navSubTitleLabel.attributedText = attributedStr;
}
#pragma mark - ACTIONS
- (void)loginButtonAction {
if (self.actionDelegate && [self.actionDelegate respondsToSelector:@selector(loginButtonDidAction)]) {
[self.actionDelegate loginButtonDidAction];
}
}
- (void)codeButtonAction {
if (self.actionDelegate && [self.actionDelegate respondsToSelector:@selector(codeButtonDidAction)]) {
[self.actionDelegate codeButtonDidAction];
}
}
#pragma mark - TOUCH
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self singleTapGestureAction];
}
- (void)singleTapGestureAction {
// 发送resignFirstResponder.
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
}
#pragma mark - INVerifyCodeNumberViewDelegate
- (void)tapGestureDidAction {
[self setNeedsLayout];
[self.phoneTextField becomeFirstResponder];
}
#pragma mark - Observers
- (void)addObservers {
//监听键盘出现、消失
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShowOrChangeFrame:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldTextDidChange:) name:UITextFieldTextDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDeleteBackwardNotify:) name:kTextFieldDidDeleteBackwardNotification object:nil];
}
- (void)removeObervers {
//监听键盘出现、消失
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - 键盘将要出现
- (void)keyboardWillShowOrChangeFrame:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
//获取键盘的高度
}
#pragma mark - 键盘将要消失
- (void)keyboardWillHide:(NSNotification *)notification {
//收起键盘
}
#pragma mark - ACTIONS
- (void)textFieldTextDidChange:(NSNotification *)notification {
UITextField *textField = [notification object];
if (self.phoneTextField == textField) {
if (textField.text > 0) {
if (textField.text.length > 1) {
textField.text = [textField.text substringToIndex:1];
}
INVerifyCodeNumberView *numberView = [self.codeNumberViews objectAtIndex:self.currentIndex];
numberView.number = textField.text;
if (self.currentIndex == self.codeNumberViews.count - 1) {
[self.phoneTextField resignFirstResponder];
[self loginButtonAction];
}
if (self.currentIndex < self.codeNumberViews.count - 1) {
self.currentIndex ++;
}
textField.text = @"";
[self setNeedsLayout];
}
}
}
- (void)textFieldDeleteBackwardNotify:(NSNotification *)notification {
NSLog(@"notification object:%@",notification);
UITextField *textField = nil;
if ([notification isKindOfClass:[UITextField class]]) {
textField = (UITextField *)notification;
} else if (notification.object && [notification.object isKindOfClass:[UITextField class]]) {
textField = (UITextField *)[notification object];
}
if (self.phoneTextField == textField) {
if (self.currentIndex < 0) {
[self setNeedsLayout];
return;
}
INVerifyCodeNumberView *numberView = [self.codeNumberViews objectAtIndex:self.currentIndex];
numberView.number = @"";
self.currentIndex--;
if (self.currentIndex < 0) {
self.currentIndex = 0;
}
[self setNeedsLayout];
}
}
#pragma mark - SETTER/GETTER
- (void)initNumberViews {
self.codeNumberViews = [NSMutableArray arrayWithCapacity:0];
for (NSInteger index = 0; index < kCodeNumber; index++) {
INVerifyCodeNumberView *numberView = [[INVerifyCodeNumberView alloc] initWithFrame:CGRectZero];
numberView.delegate = self;
[self.codeNumberViews addObject:numberView];
[self addSubview:numberView];
}
}
- (UIImageView *)bgImageView {
if (!_bgImageView) {
_bgImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
_bgImageView.backgroundColor = [UIColor clearColor];
_bgImageView.image = [UIImage imageNamed:@"login_bg.png"];
_bgImageView.contentMode = UIViewContentModeScaleAspectFill;
}
return _bgImageView;
}
- (UIVisualEffectView *)effectView {
if (!_effectView) {
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
_effectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
}
return _effectView;
}
- (UILabel *)phoneTitleLabel {
if (!_phoneTitleLabel) {
_phoneTitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_phoneTitleLabel.font = [UIFont systemFontOfSize:14];
_phoneTitleLabel.textColor = [UIColor colorWithHexString:@"999999"];
_phoneTitleLabel.backgroundColor = [UIColor clearColor];
_phoneTitleLabel.text = @"手机号码";
}
return _phoneTitleLabel;
}
- (UITextField *)phoneTextField {
if (!_phoneTextField) {
_phoneTextField = [[UITextField alloc] initWithFrame:CGRectZero];
_phoneTextField.backgroundColor = [UIColor clearColor];
_phoneTextField.clipsToBounds = YES;
_phoneTextField.textColor = [UIColor clearColor];
_phoneTextField.font = [UIFont boldSystemFontOfSize:18.0];
_phoneTextField.delegate = self;
_phoneTextField.textAlignment = NSTextAlignmentCenter;
_phoneTextField.keyboardType = UIKeyboardTypeNumberPad;
_phoneTextField.clearButtonMode = UITextFieldViewModeNever;
_phoneTextField.returnKeyType = UIReturnKeySearch;
_phoneTextField.tintColor = [UIColor colorWithHexString:@"666666"];
}
return _phoneTextField;
}
- (UIButton *)loginButton {
if (!_loginButton) {
_loginButton = [UIButton buttonWithType:UIButtonTypeCustom];
_loginButton.backgroundColor = [UIColor clearColor];
_loginButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
[_loginButton setBackgroundImage:[UIImage imageWithColor:[UIColor colorWithHexString:@"ff7e48"]] forState:UIControlStateNormal];
[_loginButton setBackgroundImage:[UIImage imageWithColor:[UIColor colorWithHexString:@"ff7e48"]] forState:UIControlStateHighlighted];
_loginButton.titleLabel.font = [UIFont boldSystemFontOfSize:14.0];
[_loginButton setTitle:@"登录" forState:UIControlStateNormal];
[_loginButton setTitleColor:[UIColor colorWithHexString:@"ffffff"] forState:UIControlStateNormal];
_loginButton.layer.cornerRadius = kLoginHeight/2;
_loginButton.layer.masksToBounds = YES;
[_loginButton addTarget:self action:@selector(loginButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _loginButton;
}
- (UIButton *)codeButton {
if (!_codeButton) {
_codeButton = [UIButton buttonWithType:UIButtonTypeCustom];
_codeButton.backgroundColor = [UIColor clearColor];
_codeButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
_codeButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
_codeButton.titleLabel.font = [UIFont systemFontOfSize:14.0];
[_codeButton setTitle:@"重新获取验证码" forState:UIControlStateNormal];
[_codeButton setTitleColor:[UIColor colorWithHexString:@"888888"] forState:UIControlStateNormal];
[_codeButton addTarget:self action:@selector(codeButtonAction) forControlEvents:UIControlEventTouchUpInside];
}
return _codeButton;
}
- (UILabel *)navTitleLabel {
if (!_navTitleLabel) {
_navTitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_navTitleLabel.font = [UIFont boldSystemFontOfSize:20];
_navTitleLabel.textColor = [UIColor colorWithHexString:@"131619"];
_navTitleLabel.backgroundColor = [UIColor clearColor];
_navTitleLabel.text = @"手机号登录";
}
return _navTitleLabel;
}
- (UILabel *)navSubTitleLabel {
if (!_navSubTitleLabel) {
_navSubTitleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_navSubTitleLabel.font = [UIFont systemFontOfSize:14];
_navSubTitleLabel.textColor = [UIColor colorWithHexString:@"666666"];
_navSubTitleLabel.backgroundColor = [UIColor clearColor];
_navSubTitleLabel.text = @"已发送4位验证码至";
}
return _navSubTitleLabel;
}
- (void)dealloc {
[self removeObervers];
}
@end
当点击键盘的删除键,即deleteBackward键时候,我们需要使用method_exchangeImplementations来控制点击键盘执行我们的方法。比如这里定义了一个textFieldDidDeleteBackward
完整代码如下
UITextField+Backward.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
static NSString *const kTextFieldDidDeleteBackwardNotification = @"kTextFieldDidDeleteBackwardNotification";
@protocol INTextFieldDelegate <UITextFieldDelegate>
@optional
- (void)textFieldDidDeleteBackward:(UITextField *)textField;
@end
@interface UITextField (Backward)
@property (weak, nonatomic) id<INTextFieldDelegate> delegate;
@end
UITextField+Backward.m
#import "UITextField+Backward.h"
#import <objc/runtime.h>
@implementation UITextField (Backward)
+ (void)load {
Method method1 = class_getInstanceMethod([self class], NSSelectorFromString(@"deleteBackward"));
Method method2 = class_getInstanceMethod([self class], @selector(in_deleteBackward));
method_exchangeImplementations(method1, method2);
}
- (void)in_deleteBackward {
[self in_deleteBackward];
if ([self.delegate respondsToSelector:@selector(textFieldDidDeleteBackward:)]) {
id <INTextFieldDelegate> delegate = (id<INTextFieldDelegate>)self.delegate;
[delegate textFieldDidDeleteBackward:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:kTextFieldDidDeleteBackwardNotification object:self];
}
@end
至此,实现数字验证码按顺序输入的代码完成。
三、小结
iOS开发-4位或者6位验证码按顺序输入实现
学习记录,每天不停进步。