当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS学习——输入验证码界面封装

iOS学习——输入验证码界面封装

2018年12月19日  | 移动技术网IT编程  | 我要评论

完结动漫,优化营销,枫之谷之冒险岛

  在很多app中都有输入验证码的功能需求,最近项目需要也有这个功能。做完之后简单整理了一下,将实现的基本思路做下记录。实现后的效果大致如下图所示,当四位签到码全部输入时,提交按钮是可以提交的,否则提交按钮失效,不允许提交。

                    

1 整体布局

   上图整个界面的布局很简单,就不多说了,重点就是中间这一块的验证码输入功能,我把它单独封装拿出来封装在一个自定义view(klcoderesignview)里了,下图是klcoderesignview布局的层次结构。

                    

  验证码输入视图(klcoderesignview)的最底层用一个透明的uitextfield来接收键盘的输入信息,上面则用4个展示视图(klcodeview)来分别展示输入的验证码信息,所有的展示视图(klcodeview)都放在一个数组中,方便后续的访问和调用。所以,klcoderesignview应该向外提供两个处理入口,验证码输入完成和输入未完成时的操作入口,并在完成时提供输入验证码信息,这里我们采用block的方式进行向外提供操作入口。此外,我们还提供了一个可以修改验证码位数的入口,调用 initwithcodebits: 即可设置验证码的位数。klcoderesignview.h以及klcoderesignview分类的代码如下:

#import <uikit/uikit.h>

ns_assume_nonnull_begin

typedef void (^coderesigncompleted)(nsstring *content);
typedef void (^coderesignuncompleted)(nsstring *content);

@interface klcoderesignview : uiview

@property (copy, nonatomic) coderesigncompleted coderesigncompleted;
@property (copy, nonatomic) coderesignuncompleted coderesignuncompleted;

- (instancetype) initwithcodebits:(nsinteger)codebits;

@end
@interface klcoderesignview () <uitextfielddelegate>

@property (strong, nonatomic) uitextfield *contentf; //监听内容输入
@property (strong, nonatomic) nsarray<klcodeview *> *codeviewsarr;//展示验证码内容的codeview数组
@property (assign, nonatomic) nsinteger currindex;//当前待输入的codeview的下标

@property (assign, nonatomic) nsinteger codebits;//位数

@end

2 注意点

2.1  信息输入框uitextfield

  信息输入框uitextfield是最重要的一部分,布局在klcoderesignview的最底层,主要作用是用于接收验证码的输入,但是对应的光标肯定是不能显示出来的,而且该uitextfield不能进行复制、粘贴、选择等操作。所以信息输入框contentf的配置如下:

- (uitextfield *)contentf {
    if (!_contentf) {
        _contentf = [[uitextfield alloc] init];
        //背景颜色和字体颜色都设置为透明的,这样在界面上就看不到
        _contentf.backgroundcolor = [uicolor clearcolor];
        _contentf.textcolor = [uicolor clearcolor];
        _contentf.keyboardtype = uikeyboardtypenumberpad;//数字键盘
        _contentf.returnkeytype = uireturnkeydone;//完成
        _contentf.tintcolor = [uicolor clearcolor];//设置光标的颜色
        _contentf.delegate = self;
    }
    return _contentf;
}

   最后,我们通过添加uitextfield的分类来实现屏蔽复制、粘贴、选择等操作,其实这些都是在uitextfield的 - (bool)canperformaction:(sel)action withsender:(id)sender 进行控制的,返回yes则允许,否则不允许,所以这里我们不管什么操作,全部返回no,这就屏蔽了所有的操作。

@implementation uitextfield (forbiddenselect)

/*
 该函数控制是否允许 选择 全选 剪切 f粘贴等功能,可以针对不同功能进行限制
 返回yes表示允许对应的功能,返回no则表示不允许对应的功能
 直接返回no则表示不允许任何编辑
 */
- (bool)canperformaction:(sel)action withsender:(id)sender {
    return no;
}

@end

 2.2 展示视图(klcodeview)

  展示视图(klcodeview)就很简单了,布局就是一个uilabel在上面,最下面一个uiview的下划线,唯一需要考虑的点就是下划线的颜色问题,如何根据是否有内容进行颜色变化。这个问题的解决也很简单,因为这个 uilabel的内容是通过一个属性text来进行设置的,所以我们重写text的设置方法就ok了,当设置的text内容不为空时,我们就设置对应的颜色为需要的颜色(蓝色),否则设置为灰色。

- (void)settext:(nsstring *)text {
    if (text.length > 0) {//有数据时设置为蓝色
        self.codelabel.text = [text substringtoindex:1];//只取一位数
        self.lineview.backgroundcolor = [uicolor bluecolor];
    } else {
        self.codelabel.text = @"";
        self.lineview.backgroundcolor = [uicolor graycolor];
    }
}

2.3 输入逻辑处理

  输入处理逻辑就是在输入和删除时进内容进行判断,并将对应的内容显示到对应的展示视图(klcodeview)中,内容的输入就都在uitextfield的代理uitextfielddelegate中的 - (bool)textfield: shouldchangecharactersinrange: replacementstring:  方法中。

  • 我们用属性currindex来表示当前待输入的展示视图klcodeview的下标,所以,当输入一个合法的验证码时,currindex要加1,当删除一个验证码时,currindex要减1,并且当currindex == 0时,删除按钮不起作用,currindex不再减1了。
  • 如果在验证码输入完成和未完成时做不同的处理,通过我们前面提供的两个block  coderesigncompleted 和 coderesignuncompleted 就可以了,我们再这里通过判断currindex 是否等于 self.codebits,相等则完成,否则没有完成,并且调用对应的block进行处理。
  • 对输入内容进行判断是否是纯数字,这个很简单,判断方法网上有很多方案,这里也简单地贴在下面的代码中。
  • 对输入的字符串的长度进行判断,如果超过当前位数,则输入无效。
  • 完成、删除操作的判断一定要在是否是纯数字以及位数过长判断之前,否则可能会导致完成、删除操作失效。
#pragma mark --- uitextfield delegate
- (bool)textfield:(uitextfield *)textfield shouldchangecharactersinrange:(nsrange)range replacementstring:(nsstring *)string {
    //完成 则收回键盘
    if ([string isequaltostring:@"\n"]) {
        [textfield resignfirstresponder];
        return no;
    }
    //删除 操作
    if ([string isequaltostring:@""]) {
        if (self.currindex == 0) {//待输入的下标为0时 删除时下标不变化,否则下标减1
            self.codeviewsarr[self.currindex].text = string;
        } else {
            self.codeviewsarr[--self.currindex].text = string;
            if (self.coderesignuncompleted) {
                nsstring *content = [textfield.text substringtoindex:self.currindex];
                self.coderesignuncompleted(content);
            }
        }
        return yes;
    }
    //判断 输入的是否是纯数字,不是纯数字 输入无效
    if (![self judgepureint:string]) {
        return no;
    }
    //如果输入的内容超过了验证码的长度 则输入无效
    if ((textfield.text.length + string.length) > self.codebits) {
        return no;
    }
    //输入的数字,则当前待输入的下标对应的 view中添加输入的数字,并且下标加1
    self.codeviewsarr[self.currindex++].text = string;
    //当当前待输入的下标为codebits时表示已经输入了对应位数的验证码,执行完成操作
    if (self.currindex == self.codebits && self.coderesigncompleted) {
        nsstring *content = [nsstring stringwithformat:@"%@%@", textfield.text, string];
        self.coderesigncompleted(content);
    } else {
        if (self.coderesignuncompleted) {
            nsstring *content = [nsstring stringwithformat:@"%@%@", textfield.text, string];
            self.coderesignuncompleted(content);
        }
    }
    
    return yes;
}
//判断一个字符串是都是纯数字
- (bool)judgepureint:(nsstring *)content {
    nsscanner *scan = [nsscanner scannerwithstring:content];
    int val;
    return [scan scanint:&val] && [scan isatend];
}

3 使用

  使用时只需要创建对应的view进行布局就ok了,然后设置验证码输入完成和验证码输入未完成对应的处理方案。

- (void)viewdidload {
    [super viewdidload];
    self.view.backgroundcolor = [uicolor whitecolor];
    weakself
    klcoderesignview *codeview = [[klcoderesignview alloc] initwithcodebits:4];
    codeview.coderesigncompleted = ^(nsstring * _nonnull content) {
        //对应位数输入完成时 允许提交按钮有效 允许提交
        nslog(@"%@", content);
        weakself.submitbtn.enabled = yes;
        weakself.submitbtn.alpha = 1.0f;
    };
    codeview.coderesignuncompleted = ^(nsstring * _nonnull content) {
        //对应位数未输入完成时 提交按钮失效 不允许提交
        weakself.submitbtn.enabled = no;
        weakself.submitbtn.alpha = 0.5f;
    };
    [self.view addsubview:codeview];
    
    [codeview mas_makeconstraints:^(masconstraintmaker *make) {
        make.left.mas_equalto(weakself.view).mas_offset(15.0f);
        make.right.mas_equalto(weakself.view).mas_offset(-15.0f);
        make.top.mas_equalto(weakself.view).mas_offset(100.0f);
        make.height.mas_equalto(40.0f);
    }];
    
    _submitbtn = [uibutton buttonwithtype:uibuttontypecustom];
    _submitbtn.titlelabel.font = font(17.0f);
    [_submitbtn settitle:@"提交" forstate:uicontrolstatenormal];
    [_submitbtn settitlecolor:[uicolor whitecolor] forstate:uicontrolstatenormal];
    [_submitbtn setbackgroundcolor:xrgb(3d,9a,e8)];
    _submitbtn.enabled = no;
    _submitbtn.alpha = 0.5f;
    _submitbtn.layer.cornerradius = 5.0f;
//    [submitbtn addtarget:self action:@selector(submitbtnclicked:) forcontrolevents:uicontroleventtouchupinside];
    [self.view addsubview:_submitbtn];
    [_submitbtn mas_makeconstraints:^(masconstraintmaker *make) {
        make.left.mas_equalto(weakself.view).mas_offset(20.0f);
        make.right.mas_equalto(weakself.view).mas_offset(-20.0f);
        make.top.mas_equalto(weakself.view).mas_offset(260.0f);
        make.height.mas_equalto(45.0f);
    }];
}

  所有的代码如下,主要分为两个文件,一个 klcoderesignview.h,一个klcoderesignview.m,如下:

#import <uikit/uikit.h>

ns_assume_nonnull_begin

typedef void (^coderesigncompleted)(nsstring *content);
typedef void (^coderesignuncompleted)(nsstring *content);

@interface klcoderesignview : uiview

@property (copy, nonatomic) coderesigncompleted coderesigncompleted;
@property (copy, nonatomic) coderesignuncompleted coderesignuncompleted;

- (instancetype) initwithcodebits:(nsinteger)codebits;

@end
klcoderesignview.h
#import "klcoderesignview.h"

#define weakself typeof(self) __weak weakself = self;
//自定义 验证码展示视图 view,由一个label和一个下划线组成
@interface klcodeview : uiview

@property (strong, nonatomic) nsstring *text;
@property (strong, nonatomic) uilabel *codelabel;
@property (strong, nonatomic) uiview *lineview;

@end


@interface klcoderesignview () <uitextfielddelegate>

@property (strong, nonatomic) uitextfield *contentf; //监听内容输入
@property (strong, nonatomic) nsarray<klcodeview *> *codeviewsarr;//显示输入内容的codeview数组
@property (assign, nonatomic) nsinteger currindex;//当前待输入的codeview的下标

@property (assign, nonatomic) nsinteger codebits;//位数

@end

@implementation klcoderesignview

- (instancetype)initwithcodebits:(nsinteger)codebits {
    self = [super init];
    self.backgroundcolor = [uicolor whitecolor];
    self.codebits = codebits;
    if (self) {
        //验证码默认是4位
        if (self.codebits < 1) {
            self.codebits = 4;
        }
        weakself
        [self addsubview:self.contentf];
        [self.contentf mas_makeconstraints:^(masconstraintmaker *make) {
            make.top.bottom.right.left.mas_equalto(weakself).mas_offset(0.0f);
        }];
        
        for(nsinteger i = 0; i < self.codebits; i++) {
            klcodeview *codeview = self.codeviewsarr[i];
            [self addsubview:codeview];
        }
    }
    return self;
}

- (void)layoutsubviews {
    [super layoutsubviews];
    weakself
    //设定每个数字之间的间距为数字view宽度的一半 总宽度就是 bits + (bits - 1)* 0.5
    cgfloat codeviewwidth = self.bounds.size.width/(self.codebits * 1.5 - 0.5);
    for(nsinteger i = 0; i < self.codebits; i++) {
        klcodeview *codeview = self.codeviewsarr[i];
        [codeview mas_updateconstraints:^(masconstraintmaker *make) {
            cgfloat left = codeviewwidth * 1.5 * i;
            make.left.mas_equalto(weakself).mas_offset(left);
            make.top.bottom.mas_equalto(weakself).mas_offset(0.0f);
            make.width.mas_equalto(codeviewwidth);
        }];
    }
}

#pragma mark --- uitextfield delegate
- (bool)textfield:(uitextfield *)textfield shouldchangecharactersinrange:(nsrange)range replacementstring:(nsstring *)string {
    //完成 则收回键盘
    if ([string isequaltostring:@"\n"]) {
        [textfield resignfirstresponder];
        return no;
    }
    //删除 操作
    if ([string isequaltostring:@""]) {
        if (self.currindex == 0) {//待输入的下标为0时 删除时下标不变化,否则下标减1
            self.codeviewsarr[self.currindex].text = string;
        } else {
            self.codeviewsarr[--self.currindex].text = string;
            if (self.coderesignuncompleted) {
                nsstring *content = [textfield.text substringtoindex:self.currindex];
                self.coderesignuncompleted(content);
            }
        }
        return yes;
    }
    //判断 输入的是否是纯数字,不是纯数字 输入无效
    if (![self judgepureint:string]) {
        return no;
    }
    //如果输入的内容超过了验证码的长度 则输入无效
    if ((textfield.text.length + string.length) > self.codebits) {
        return no;
    }
    //输入的数字,则当前待输入的下标对应的 view中添加输入的数字,并且下标加1
    self.codeviewsarr[self.currindex++].text = string;
    //当当前待输入的下标为codebits时表示已经输入了对应位数的验证码,执行完成操作
    if (self.currindex == self.codebits && self.coderesigncompleted) {
        nsstring *content = [nsstring stringwithformat:@"%@%@", textfield.text, string];
        self.coderesigncompleted(content);
    } else {
        if (self.coderesignuncompleted) {
            nsstring *content = [nsstring stringwithformat:@"%@%@", textfield.text, string];
            self.coderesignuncompleted(content);
        }
    }
    
    return yes;
}

- (uitextfield *)contentf {
    if (!_contentf) {
        _contentf = [[uitextfield alloc] init];
        //背景颜色和字体颜色都设置为透明的,这样在界面上就看不到
        _contentf.backgroundcolor = [uicolor clearcolor];
        _contentf.textcolor = [uicolor clearcolor];
        _contentf.keyboardtype = uikeyboardtypenumberpad;//数字键盘
        _contentf.returnkeytype = uireturnkeydone;//完成
        _contentf.tintcolor = [uicolor clearcolor];//设置光标的颜色
        _contentf.delegate = self;
    }
    return _contentf;
}

- (nsarray<klcodeview *> *)codeviewsarr {
    if (!_codeviewsarr) {
        nsmutablearray *arr = [nsmutablearray array];
        for (nsinteger i = 0; i < self.codebits; i++) {
            klcodeview *codeview = [[klcodeview alloc] init];
            [arr addobject:codeview];
        }
        _codeviewsarr = [nsarray arraywitharray:arr];
    }
    return _codeviewsarr;
}

//判断一个字符串是都是纯数字
- (bool)judgepureint:(nsstring *)content {
    nsscanner *scan = [nsscanner scannerwithstring:content];
    int val;
    return [scan scanint:&val] && [scan isatend];
}

@end


@implementation uitextfield (forbiddenselect)

/*
 该函数控制是否允许 选择 全选 剪切 f粘贴等功能,可以针对不同功能进行限制
 返回yes表示允许对应的功能,返回no则表示不允许对应的功能
 直接返回no则表示不允许任何编辑
 */
- (bool)canperformaction:(sel)action withsender:(id)sender {
    return no;
}

@end


//验证码展示视图 的实现
@implementation klcodeview

- (instancetype)initwithframe:(cgrect)frame {
    self = [super initwithframe:frame];
    if (self) {
        weakself
        self.backgroundcolor = [uicolor whitecolor];
        self.userinteractionenabled = no;
        //数字编码 label
        _codelabel = [[uilabel alloc] init];
        _codelabel.textcolor = [uicolor bluecolor];
        _codelabel.font = font(25.0f);
        _codelabel.textalignment = nstextalignmentcenter;
        [self addsubview:_codelabel];
        [_codelabel mas_makeconstraints:^(masconstraintmaker *make) {
            make.top.left.right.mas_equalto(weakself).mas_offset(0.0f);
            make.bottom.mas_equalto(weakself).mas_offset(-10.0f);
        }];
        
        _lineview = [[uiview alloc] init];
        _lineview.backgroundcolor = [uicolor graycolor];
        [self addsubview:_lineview];
        [_lineview mas_makeconstraints:^(masconstraintmaker *make) {
            make.bottom.left.right.mas_equalto(weakself).mas_offset(0.0f);
            make.height.mas_equalto(2.0f);
        }];
    }
    return self;
}

- (void)settext:(nsstring *)text {
    if (text.length > 0) {
        self.codelabel.text = [text substringtoindex:1];
        self.lineview.backgroundcolor = [uicolor bluecolor];
    } else {
        self.codelabel.text = @"";
        self.lineview.backgroundcolor = [uicolor graycolor];
    }
}


@end
klcoderesignview.m

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网