众多App经常使用的短信验证码输入框,没有啥好介绍的。
效果展示
基本原理
多个UITextField
组成的,输入完成后,进入下一个UITextField
进行编辑操作,最后一个输入完成后进行校验。
思维导图.png
自定义SMSCodeTextField
创建FLSMSCodeTextField
继承自UITextField
,重写UIKeyInput
的- (void)deleteBackward;
方法,增加自定义的UITextFieldDelegate
@protocol FLSMSCodeTextFieldDelegate <UITextFieldDelegate>
@optional;
/// 删除内容后的代理方法
/// @param textField 输入框
- (void)smsCodeTextFieldDeleteBackward:(FLSMSCodeTextField *)textField;
@end
@interface FLSMSCodeTextField : UITextField
/**
删除的代理
*/
@property (nonatomic, assign) id<FLSMSCodeTextFieldDelegate> delegate;
@end
@implementation FLSMSCodeTextField
@dynamic delegate;
- (void)deleteBackward {
[super deleteBackward];
//删除操作的代理方法
if (self.delegate && [self.delegate respondsToSelector:@selector(smsCodeTextFieldDeleteBackward:)]) {
[self.delegate smsCodeTextFieldDeleteBackward:self];
}
}
@end
FLSMSCodeEditView逻辑和代码
初始化
- (instancetype)init {
if (self = [super init]) {
[self p_initializeSubviews];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self p_initializeSubviews];
}
return self;
}
- (void)p_initializeSubviews {
self.textFieldArray = [NSMutableArray array];
self.textFieldSeparatorLineArray = [NSMutableArray array];
[self addSubview:self.contentView];
[_contentView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(UIEdgeInsetsZero);
}];
}
设置需要创建的输入框数量和完成后的回调
/**
验证码输入框数量
*/
@property (nonatomic, assign) NSInteger codeEditCount;
/**
验证码输入完成的回调
*/
@property (nonatomic, copy) void (^smsCodeEditFinishBlock)(NSString *smsCode);
#pragma mark - setter
- (void)setCodeEditCount:(NSInteger)codeEditCount {
_codeEditCount = codeEditCount;
NSAssert(codeEditCount >= 4, @"数量不能少于4个");
for (NSInteger i = 0; i < codeEditCount; i ++) {
FLSMSCodeTextField *textField = [[FLSMSCodeTextField alloc]init];
textField.tag = i;
textField.keyboardType = UIKeyboardTypeNumberPad;
textField.font = UIFontBoldMake(40);
textField.textColor = UIColorBlack;
textField.tintColor = UIColorGreen;
textField.textAlignment = NSTextAlignmentCenter;
textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.enabled = NO;
textField.delegate = self;
[textField addTarget:self action:@selector(p_textFieldEditingChangedAction:) forControlEvents:UIControlEventEditingChanged];
[_contentView addSubview:textField];
UIView *separatorLineView = [[UIView alloc]init];
separatorLineView.backgroundColor = UIColorSeparator;
[_contentView addSubview:separatorLineView];
[separatorLineView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.right.mas_equalTo(textField);
make.top.mas_equalTo(textField.mas_bottom).mas_offset(14.0);
make.height.mas_equalTo(PixelOne);
make.bottom.mas_equalTo(0.0);
}];
[_textFieldArray addObject:textField];
[_textFieldSeparatorLineArray addObject:separatorLineView];
}
[_textFieldArray mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(0.0);
make.height.mas_equalTo(40.0);
}];
[_textFieldArray mas_distributeViewsAlongAxis:MASAxisTypeHorizontal withFixedSpacing:14.0 leadSpacing:0.0 tailSpacing:0.0];
}
输入内容时的逻辑判断方法
#pragma mark - Action
- (void)p_textFieldEditingChangedAction:(FLSMSCodeTextField *)textField {
//判断是否为最后一个textField
if (textField.tag == self.textFieldArray.count - 1 && textField.text.length > 0) {
//判断text的长度
textField.text = textField.text.length > 1 ? [textField.text substringFromIndex:1] : [textField.text substringWithRange:NSMakeRange(0, 1)];
[textField resignFirstResponder];
textField.enabled = NO;
//判断SMSCode已经达到最大数量,且又增加更改
if (self.smsCodeString.length == self.codeEditCount) {
self.smsCodeString = [self.smsCodeString substringToIndex:self.smsCodeString.length - 1];
}
self.smsCodeString = [self.smsCodeString stringByAppendingString:textField.text];
if (self.smsCodeEditFinishBlock) {
self.smsCodeEditFinishBlock(self.smsCodeString);
}
QMUILog(NSStringFromClass(self.class), @"%@",self.smsCodeString);
} else {
if (![textField.text isEqualToString:@""]) {
FLSMSCodeTextField *nextTF = self.textFieldArray[textField.tag + 1];
nextTF.enabled = YES;
if (textField.text.length > 1) {
NSString *lengthString = textField.text;
textField.text = [lengthString substringWithRange:NSMakeRange(0, 1)];
nextTF.text = [lengthString substringWithRange:NSMakeRange(lengthString.length - 1, 1)];
//判断是否是最后一个
if (nextTF.tag == self.textFieldArray.count - 1 && textField.text.length > 0) {
[nextTF resignFirstResponder];
nextTF.enabled = NO;
self.smsCodeString = self.smsCodeString = [self.smsCodeString stringByAppendingString:nextTF.text];
if (self.smsCodeEditFinishBlock) {
self.smsCodeEditFinishBlock(self.smsCodeString);
}
QMUILog(NSStringFromClass(self.class), @"%@",self.smsCodeString);
} else {
[nextTF becomeFirstResponder];
}
} else {
[nextTF becomeFirstResponder];
}
textField.enabled = NO;
}
self.smsCodeString = @"";
for (FLSMSCodeTextField *tf in self.textFieldArray) {
if (tf.text.length > 0) {
self.smsCodeString = [self.smsCodeString stringByAppendingString:tf.text];
}
}
}
}
删除内容时的逻辑判断方法
#pragma mark - FLSMSCodeTextFieldDelegate
- (void)smsCodeTextFieldDeleteBackward:(FLSMSCodeTextField *)textField {
if (textField.tag != 0 && !textField.text.length) {
textField.enabled = NO;
FLSMSCodeTextField *beforeTextField = self.textFieldArray[textField.tag - 1];
beforeTextField.enabled = YES;
[beforeTextField becomeFirstResponder];
}
}
- (void)p_closeAllTextFieldEnabled {
for (FLSMSCodeTextField *textField in self.textFieldArray) {
textField.enabled = NO;
}
}
点击编辑视图时,需要触发第一响应的方法
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self p_closeAllTextFieldEnabled];
if (self.smsCodeString.length) {
if (self.smsCodeString.length == self.codeEditCount) {
FLSMSCodeTextField *textField = self.textFieldArray.lastObject;
textField.enabled = YES;
[textField becomeFirstResponder];
} else {
FLSMSCodeTextField *textField = self.textFieldArray[self.smsCodeString.length];
textField.enabled = YES;
[textField becomeFirstResponder];
}
} else {
FLSMSCodeTextField *textField = self.textFieldArray.firstObject;
textField.enabled = YES;
[textField becomeFirstResponder];
}
}
最后
因为输入和删除都会触发p_textFieldEditingChangedAction
方法,所以内部逻辑只针对输入内容时,删除内容会重新赋值smsCodeString
,最后大家注意避免stringByAppendingString
不能添加为空的字符串。
// 先置空smsCodeString,删除/输入时都会触发此方法
self.smsCodeString = @"";
for (VPSMSCodeTextField *tf in self.textFieldArray) {
if (tf.text.length > 0) {
self.smsCodeString = [self.smsCodeString stringByAppendingString:tf.text];
}
}
以上就是一个简单的验证码的所有内容,以后追加输入框选中效果。