美文网首页
iOS 自定义短信验证码输入框

iOS 自定义短信验证码输入框

作者: 曾經蠟筆沒有小新 | 来源:发表于2020-04-23 17:31 被阅读0次

众多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];
     }
}

以上就是一个简单的验证码的所有内容,以后追加输入框选中效果。

相关文章

网友评论

      本文标题:iOS 自定义短信验证码输入框

      本文链接:https://www.haomeiwen.com/subject/sgsoihtx.html