iOS开发-4位或者6位验证码按顺序输入实现

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位验证码按顺序输入实现

学习记录,每天不停进步。

相关推荐
懒惰的bit3 小时前
基础网络安全知识
学习·web安全·1024程序员节
天行健王春城老师6 小时前
基于TRIZ的教育机器人功能创新
经验分享·机器人
ROCKY_8176 小时前
AndroidStudio-文本显示
1024程序员节
AI大模型训练家9 小时前
Python 语法及入门 丨 专为Python零基础 一篇博客让你完全掌握Python语法
服务器·开发语言·网络·python·1024程序员节
HPC_fac130520678169 小时前
科学计算服务器:如何计算算力?如何提升科学研究效率?
运维·服务器·人工智能·科技·gpu算力·1024程序员节
鹏大师运维9 小时前
【系统配置】命令行配置麒麟安全中心应用程序来源检查
linux·安全·国产化·麒麟·国产操作系统·1024程序员节·应用检测
网安_秋刀鱼10 小时前
中间件安全
安全·web安全·网络安全·中间件·1024程序员节
如泡似影14 小时前
【Linux】拆分详解 - vim / gcc / makefile
linux·运维·服务器·经验分享·笔记
白总Server17 小时前
Swagger UI
后端·ui·spring cloud·ribbon·架构·scala·1024程序员节
WANGWUSAN6617 小时前
爬虫新姿势——使用Chrome Devtools写一个小说爬虫
开发语言·前端·经验分享·爬虫·python·学习·chrome devtools