iOS development-implementation of 4-digit or 6-digit verification code input in order

iOS development-implementation of 4-digit or 6-digit verification code input in order

Previously, we implemented the function of entering the verification code in sequence after obtaining the verification code. After entering the mobile phone number, click Login with SMS Verification Code and then click Login with SMS Verification Code on the interface, and then enter the 4-digit or 6-digit verification code in sequence. Let’s record it here.

1. Overall effect

When implementing this, the overall result is 4 labelViews that display the numbers in the input boxes. There is a current that records the number of the current input. There is an input box at the position of the labelView. When the number is entered, the input box moves to The position of the labelView of current+1. When the delete button of the keyboard is clicked, the input box moves to the position of the labelView of current-1, and the digital text displayed by the labelView after current is reset.

The overall effect is as follows
Insert image description here

2. Implement functions

We have a labelView to display digital text. This labelView is mainly a UILabel that adds a click gesture to the current view. Used to display numbers.

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
    

Then multiple labelViews are created based on the number of numbers in the verification code. There is a current that records the number of the current input. There is an input box at the position of the labelView. When the number is entered, the input box moves to current+1. The position of the labelView. When the delete button on the keyboard is clicked, the input box moves to the labelView position of current-1, and the digital text displayed by the labelView after current is reset.

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
    

When clicking the delete key on the keyboard, that is, the deleteBackward key, we need to use method_exchangeImplementations to control clicking on the keyboard to execute our method. For example, a textFieldDidDeleteBackward is defined here

The complete code is as follows

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
    

At this point, the code to enter the digital verification code in order is completed.
Insert image description here

3. Summary

iOS development-implementation of 4-digit or 6-digit verification code input in order

Study and record, keep improving every day.

Guess you like

Origin blog.csdn.net/gloryFlow/article/details/134070162