经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
iOS学习——输入验证码界面封装
来源:cnblogs  作者:mukekeheart  时间:2018/12/20 9:31:50  对本文有异议

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

                    

1 整体布局

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

                    

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

  1. #import <UIKit/UIKit.h>
  2. NS_ASSUME_NONNULL_BEGIN
  3. typedef void (^CodeResignCompleted)(NSString *content);
  4. typedef void (^CodeResignUnCompleted)(NSString *content);
  5. @interface KLCodeResignView : UIView
  6. @property (copy, nonatomic) CodeResignCompleted codeResignCompleted;
  7. @property (copy, nonatomic) CodeResignUnCompleted codeResignUnCompleted;
  8. - (instancetype) initWithCodeBits:(NSInteger)codeBits;
  9. @end
  1. @interface KLCodeResignView () <UITextFieldDelegate>
  2. @property (strong, nonatomic) UITextField *contentF; //监听内容输入
  3. @property (strong, nonatomic) NSArray<KLCodeView *> *codeViewsArr;//展示验证码内容的codeView数组
  4. @property (assign, nonatomic) NSInteger currIndex;//当前待输入的codeView的下标
  5. @property (assign, nonatomic) NSInteger codeBits;//位数
  6.  
  7. @end

2 注意点

2.1  信息输入框UITextField

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

  1. - (UITextField *)contentF {
  2. if (!_contentF) {
  3. _contentF = [[UITextField alloc] init];
  4. //背景颜色和字体颜色都设置为透明的,这样在界面上就看不到
  5. _contentF.backgroundColor = [UIColor clearColor];
  6. _contentF.textColor = [UIColor clearColor];
  7. _contentF.keyboardType = UIKeyboardTypeNumberPad;//数字键盘
  8. _contentF.returnKeyType = UIReturnKeyDone;//完成
  9. _contentF.tintColor = [UIColor clearColor];//设置光标的颜色
  10. _contentF.delegate = self;
  11. }
  12. return _contentF;
  13. }

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

  1. @implementation UITextField (ForbiddenSelect)
  2. /*
  3. 该函数控制是否允许 选择 全选 剪切 f粘贴等功能,可以针对不同功能进行限制
  4. 返回YES表示允许对应的功能,返回NO则表示不允许对应的功能
  5. 直接返回NO则表示不允许任何编辑
  6. */
  7. - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
  8. return NO;
  9. }
  10. @end

 2.2 展示视图(KLCodeView)

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

  1. - (void)setText:(NSString *)text {
  2. if (text.length > 0) {//有数据时设置为蓝色
  3. self.codeLabel.text = [text substringToIndex:1];//只取一位数
  4. self.lineView.backgroundColor = [UIColor blueColor];
  5. } else {
  6. self.codeLabel.text = @"";
  7. self.lineView.backgroundColor = [UIColor grayColor];
  8. }
  9. }

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进行处理。
  • 对输入内容进行判断是否是纯数字,这个很简单,判断方法网上有很多方案,这里也简单地贴在下面的代码中。
  • 对输入的字符串的长度进行判断,如果超过当前位数,则输入无效。
  • 完成、删除操作的判断一定要在是否是纯数字以及位数过长判断之前,否则可能会导致完成、删除操作失效。
  1. #pragma mark --- UITextField delegate
  2. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  3. //完成 则收回键盘
  4. if ([string isEqualToString:@"\n"]) {
  5. [textField resignFirstResponder];
  6. return NO;
  7. }
  8. //删除 操作
  9. if ([string isEqualToString:@""]) {
  10. if (self.currIndex == 0) {//待输入的下标为0时 删除时下标不变化,否则下标减1
  11. self.codeViewsArr[self.currIndex].text = string;
  12. } else {
  13. self.codeViewsArr[--self.currIndex].text = string;
  14. if (self.codeResignUnCompleted) {
  15. NSString *content = [textField.text substringToIndex:self.currIndex];
  16. self.codeResignUnCompleted(content);
  17. }
  18. }
  19. return YES;
  20. }
  21. //判断 输入的是否是纯数字,不是纯数字 输入无效
  22. if (![self judgePureInt:string]) {
  23. return NO;
  24. }
  25. //如果输入的内容超过了验证码的长度 则输入无效
  26. if ((textField.text.length + string.length) > self.codeBits) {
  27. return NO;
  28. }
  29. //输入的数字,则当前待输入的下标对应的 view中添加输入的数字,并且下标加1
  30. self.codeViewsArr[self.currIndex++].text = string;
  31. //当当前待输入的下标为codebits时表示已经输入了对应位数的验证码,执行完成操作
  32. if (self.currIndex == self.codeBits && self.codeResignCompleted) {
  33. NSString *content = [NSString stringWithFormat:@"%@%@", textField.text, string];
  34. self.codeResignCompleted(content);
  35. } else {
  36. if (self.codeResignUnCompleted) {
  37. NSString *content = [NSString stringWithFormat:@"%@%@", textField.text, string];
  38. self.codeResignUnCompleted(content);
  39. }
  40. }
  41. return YES;
  42. }
  1. //判断一个字符串是都是纯数字
  2. - (BOOL)judgePureInt:(NSString *)content {
  3. NSScanner *scan = [NSScanner scannerWithString:content];
  4. int val;
  5. return [scan scanInt:&val] && [scan isAtEnd];
  6. }

3 使用

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

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. self.view.backgroundColor = [UIColor whiteColor];
  4. WEAKSELF
  5. KLCodeResignView *codeView = [[KLCodeResignView alloc] initWithCodeBits:4];
  6. codeView.codeResignCompleted = ^(NSString * _Nonnull content) {
  7. //对应位数输入完成时 允许提交按钮有效 允许提交
  8. NSLog(@"%@", content);
  9. weakSelf.submitBtn.enabled = YES;
  10. weakSelf.submitBtn.alpha = 1.0f;
  11. };
  12. codeView.codeResignUnCompleted = ^(NSString * _Nonnull content) {
  13. //对应位数未输入完成时 提交按钮失效 不允许提交
  14. weakSelf.submitBtn.enabled = NO;
  15. weakSelf.submitBtn.alpha = 0.5f;
  16. };
  17. [self.view addSubview:codeView];
  18. [codeView mas_makeConstraints:^(MASConstraintMaker *make) {
  19. make.left.mas_equalTo(weakSelf.view).mas_offset(15.0f);
  20. make.right.mas_equalTo(weakSelf.view).mas_offset(-15.0f);
  21. make.top.mas_equalTo(weakSelf.view).mas_offset(100.0f);
  22. make.height.mas_equalTo(40.0f);
  23. }];
  24. _submitBtn = [UIButton buttonWithType:UIButtonTypeCustom];
  25. _submitBtn.titleLabel.font = FONT(17.0f);
  26. [_submitBtn setTitle:@"提交" forState:UIControlStateNormal];
  27. [_submitBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
  28. [_submitBtn setBackgroundColor:XRGB(3d,9a,e8)];
  29. _submitBtn.enabled = NO;
  30. _submitBtn.alpha = 0.5f;
  31. _submitBtn.layer.cornerRadius = 5.0f;
  32. // [submitBtn addTarget:self action:@selector(submitBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
  33. [self.view addSubview:_submitBtn];
  34. [_submitBtn mas_makeConstraints:^(MASConstraintMaker *make) {
  35. make.left.mas_equalTo(weakSelf.view).mas_offset(20.0f);
  36. make.right.mas_equalTo(weakSelf.view).mas_offset(-20.0f);
  37. make.top.mas_equalTo(weakSelf.view).mas_offset(260.0f);
  38. make.height.mas_equalTo(45.0f);
  39. }];
  40. }

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

  1. #import <UIKit/UIKit.h>
  2. NS_ASSUME_NONNULL_BEGIN
  3. typedef void (^CodeResignCompleted)(NSString *content);
  4. typedef void (^CodeResignUnCompleted)(NSString *content);
  5. @interface KLCodeResignView : UIView
  6. @property (copy, nonatomic) CodeResignCompleted codeResignCompleted;
  7. @property (copy, nonatomic) CodeResignUnCompleted codeResignUnCompleted;
  8. - (instancetype) initWithCodeBits:(NSInteger)codeBits;
  9. @end
KLCodeResignView.h
  1. #import "KLCodeResignView.h"
  2.  
  3. #define WEAKSELF typeof(self) __weak weakSelf = self;
  4. //自定义 验证码展示视图 view,由一个label和一个下划线组成
  5. @interface KLCodeView : UIView
  6. @property (strong, nonatomic) NSString *text;
  7. @property (strong, nonatomic) UILabel *codeLabel;
  8. @property (strong, nonatomic) UIView *lineView;
  9. @end
  10.  
  11.  
  12. @interface KLCodeResignView () <UITextFieldDelegate>
  13. @property (strong, nonatomic) UITextField *contentF; //监听内容输入
  14. @property (strong, nonatomic) NSArray<KLCodeView *> *codeViewsArr;//显示输入内容的codeView数组
  15. @property (assign, nonatomic) NSInteger currIndex;//当前待输入的codeView的下标
  16. @property (assign, nonatomic) NSInteger codeBits;//位数
  17.  
  18. @end
  19.  
  20. @implementation KLCodeResignView
  21. - (instancetype)initWithCodeBits:(NSInteger)codeBits {
  22. self = [super init];
  23. self.backgroundColor = [UIColor whiteColor];
  24. self.codeBits = codeBits;
  25. if (self) {
  26. //验证码默认是4位
  27. if (self.codeBits < 1) {
  28. self.codeBits = 4;
  29. }
  30. WEAKSELF
  31. [self addSubview:self.contentF];
  32. [self.contentF mas_makeConstraints:^(MASConstraintMaker *make) {
  33. make.top.bottom.right.left.mas_equalTo(weakSelf).mas_offset(0.0f);
  34. }];
  35. for(NSInteger i = 0; i < self.codeBits; i++) {
  36. KLCodeView *codeView = self.codeViewsArr[i];
  37. [self addSubview:codeView];
  38. }
  39. }
  40. return self;
  41. }
  42. - (void)layoutSubviews {
  43. [super layoutSubviews];
  44. WEAKSELF
  45. //设定每个数字之间的间距为数字view宽度的一半 总宽度就是 bits + (bits - 1)* 0.5
  46. CGFloat codeViewWidth = self.bounds.size.width/(self.codeBits * 1.5 - 0.5);
  47. for(NSInteger i = 0; i < self.codeBits; i++) {
  48. KLCodeView *codeView = self.codeViewsArr[i];
  49. [codeView mas_updateConstraints:^(MASConstraintMaker *make) {
  50. CGFloat left = codeViewWidth * 1.5 * i;
  51. make.left.mas_equalTo(weakSelf).mas_offset(left);
  52. make.top.bottom.mas_equalTo(weakSelf).mas_offset(0.0f);
  53. make.width.mas_equalTo(codeViewWidth);
  54. }];
  55. }
  56. }
  57. #pragma mark --- UITextField delegate
  58. - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  59. //完成 则收回键盘
  60. if ([string isEqualToString:@"\n"]) {
  61. [textField resignFirstResponder];
  62. return NO;
  63. }
  64. //删除 操作
  65. if ([string isEqualToString:@""]) {
  66. if (self.currIndex == 0) {//待输入的下标为0时 删除时下标不变化,否则下标减1
  67. self.codeViewsArr[self.currIndex].text = string;
  68. } else {
  69. self.codeViewsArr[--self.currIndex].text = string;
  70. if (self.codeResignUnCompleted) {
  71. NSString *content = [textField.text substringToIndex:self.currIndex];
  72. self.codeResignUnCompleted(content);
  73. }
  74. }
  75. return YES;
  76. }
  77. //判断 输入的是否是纯数字,不是纯数字 输入无效
  78. if (![self judgePureInt:string]) {
  79. return NO;
  80. }
  81. //如果输入的内容超过了验证码的长度 则输入无效
  82. if ((textField.text.length + string.length) > self.codeBits) {
  83. return NO;
  84. }
  85. //输入的数字,则当前待输入的下标对应的 view中添加输入的数字,并且下标加1
  86. self.codeViewsArr[self.currIndex++].text = string;
  87. //当当前待输入的下标为codebits时表示已经输入了对应位数的验证码,执行完成操作
  88. if (self.currIndex == self.codeBits && self.codeResignCompleted) {
  89. NSString *content = [NSString stringWithFormat:@"%@%@", textField.text, string];
  90. self.codeResignCompleted(content);
  91. } else {
  92. if (self.codeResignUnCompleted) {
  93. NSString *content = [NSString stringWithFormat:@"%@%@", textField.text, string];
  94. self.codeResignUnCompleted(content);
  95. }
  96. }
  97. return YES;
  98. }
  99. - (UITextField *)contentF {
  100. if (!_contentF) {
  101. _contentF = [[UITextField alloc] init];
  102. //背景颜色和字体颜色都设置为透明的,这样在界面上就看不到
  103. _contentF.backgroundColor = [UIColor clearColor];
  104. _contentF.textColor = [UIColor clearColor];
  105. _contentF.keyboardType = UIKeyboardTypeNumberPad;//数字键盘
  106. _contentF.returnKeyType = UIReturnKeyDone;//完成
  107. _contentF.tintColor = [UIColor clearColor];//设置光标的颜色
  108. _contentF.delegate = self;
  109. }
  110. return _contentF;
  111. }
  112. - (NSArray<KLCodeView *> *)codeViewsArr {
  113. if (!_codeViewsArr) {
  114. NSMutableArray *arr = [NSMutableArray array];
  115. for (NSInteger i = 0; i < self.codeBits; i++) {
  116. KLCodeView *codeView = [[KLCodeView alloc] init];
  117. [arr addObject:codeView];
  118. }
  119. _codeViewsArr = [NSArray arrayWithArray:arr];
  120. }
  121. return _codeViewsArr;
  122. }
  123. //判断一个字符串是都是纯数字
  124. - (BOOL)judgePureInt:(NSString *)content {
  125. NSScanner *scan = [NSScanner scannerWithString:content];
  126. int val;
  127. return [scan scanInt:&val] && [scan isAtEnd];
  128. }
  129. @end
  130.  
  131.  
  132. @implementation UITextField (ForbiddenSelect)
  133. /*
  134. 该函数控制是否允许 选择 全选 剪切 f粘贴等功能,可以针对不同功能进行限制
  135. 返回YES表示允许对应的功能,返回NO则表示不允许对应的功能
  136. 直接返回NO则表示不允许任何编辑
  137. */
  138. - (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
  139. return NO;
  140. }
  141. @end
  142.  
  143.  
  144. //验证码展示视图 的实现
  145. @implementation KLCodeView
  146. - (instancetype)initWithFrame:(CGRect)frame {
  147. self = [super initWithFrame:frame];
  148. if (self) {
  149. WEAKSELF
  150. self.backgroundColor = [UIColor whiteColor];
  151. self.userInteractionEnabled = NO;
  152. //数字编码 label
  153. _codeLabel = [[UILabel alloc] init];
  154. _codeLabel.textColor = [UIColor blueColor];
  155. _codeLabel.font = FONT(25.0f);
  156. _codeLabel.textAlignment = NSTextAlignmentCenter;
  157. [self addSubview:_codeLabel];
  158. [_codeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
  159. make.top.left.right.mas_equalTo(weakSelf).mas_offset(0.0f);
  160. make.bottom.mas_equalTo(weakSelf).mas_offset(-10.0f);
  161. }];
  162. _lineView = [[UIView alloc] init];
  163. _lineView.backgroundColor = [UIColor grayColor];
  164. [self addSubview:_lineView];
  165. [_lineView mas_makeConstraints:^(MASConstraintMaker *make) {
  166. make.bottom.left.right.mas_equalTo(weakSelf).mas_offset(0.0f);
  167. make.height.mas_equalTo(2.0f);
  168. }];
  169. }
  170. return self;
  171. }
  172. - (void)setText:(NSString *)text {
  173. if (text.length > 0) {
  174. self.codeLabel.text = [text substringToIndex:1];
  175. self.lineView.backgroundColor = [UIColor blueColor];
  176. } else {
  177. self.codeLabel.text = @"";
  178. self.lineView.backgroundColor = [UIColor grayColor];
  179. }
  180. }
  181. @end
KLCodeResignView.m

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号