最近项目中用到了自定义的手机号验证码输入框,网上搜了一些资料,参考了一些别人实现的思路和代码,并在此基础上自定义了输入框验证码的位数,如四位或者六位,最终做出了如下效果的输入框。
输入框.gif
基本思路:
- 自定义输入框视图底部放置一个
textView
,并设置成透明色,目的是用于点击textView
可以弹出数字键盘,并且可以获取到输入的数字。 - 在
textView
的上面循环(循环的次数根据验证码的位数来决定,如4位或6位)创建一组四个控件,分别是:View
、Layer
、Label
和ShapeLayer
,并且Layer
、Label
和ShapeLayer
要添加到View
上。Layer
是底部放置输入框数字的横线,Label
是展示输入的数字,ShapeLayer
是闪动的光标。
- (void)initSubviews {
CGFloat W = CGRectGetWidth(self.frame);
CGFloat H = CGRectGetHeight(self.frame);
CGFloat Padd = (K_Screen_Width-self.inputNum*K_W)/(self.inputNum+1);
[self addSubview:self.textView];
self.textView.frame = CGRectMake(Padd, 0, W-Padd*2, H);
//默认编辑第一个.
[self beginEdit];
for (int i = 0; i < _inputNum; i ++) {
UIView *subView = [UIView new];
subView.frame = CGRectMake(Padd+(K_W+Padd)*i, 0, K_W, H);
subView.userInteractionEnabled = NO;
[self addSubview:subView];
[CALayer addSubLayerWithFrame:CGRectMake(0, H-2, K_W, 2) backgroundColor:[UIColor lightGrayColor] backView:subView];
//Label
UILabel *label = [[UILabel alloc]init];
label.frame = CGRectMake(0, 0, K_W, H);
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor darkGrayColor];
label.font = [UIFont systemFontOfSize:38];
[subView addSubview:label];
//光标
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(K_W / 2, 15, 2, H - 30)];
CAShapeLayer *line = [CAShapeLayer layer];
line.path = path.CGPath;
line.fillColor = [UIColor darkGrayColor].CGColor;
[subView.layer addSublayer:line];
if (i == 0) {
[line addAnimation:[self opacityAnimation] forKey:@"kOpacityAnimation"];
//高亮颜色
line.hidden = NO;
}else {
line.hidden = YES;
}
//把光标对象和label对象装进数组
[self.lines addObject:line];
[self.labels addObject:label];
}
}
- 把创建的光标对象
ShapeLayer
和Label
对象装进数组,用于后面在输入框输入数字时切换其显示还是隐藏的属性。 - 在
textView
的代理方法- (void)textViewDidChange:(UITextView *)textView;
中去获取输入框输入的内容,并截取对应的数字放到对应创建的Label
上进行显示,而光标则根据当前位置上的Label
上文本内容的有无来设置光标的隐藏和显示,即如果该位置Label
上无数字,则光标显示出来并闪动,反之则隐藏。
#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView {
NSString *verStr = textView.text;
if (verStr.length > _inputNum) {
textView.text = [textView.text substringToIndex:_inputNum];
}
//大于等于最大值时, 结束编辑
if (verStr.length >= _inputNum) {
[self endEdit];
}
if (self.CodeBlock) {
self.CodeBlock(textView.text);
}
for (int i = 0; i < _labels.count; i ++) {
UILabel *bgLabel = _labels[i];
if (i < verStr.length) {
[self changeViewLayerIndex:i linesHidden:YES];
bgLabel.text = [verStr substringWithRange:NSMakeRange(i, 1)];
}else {
[self changeViewLayerIndex:i linesHidden:i == verStr.length ? NO : YES];
//textView的text为空的时候
if (!verStr && verStr.length == 0) {
[self changeViewLayerIndex:0 linesHidden:NO];
}
bgLabel.text = @"";
}
}
}
//光标显示或者隐藏
- (void)changeViewLayerIndex:(NSInteger)index linesHidden:(BOOL)hidden {
CAShapeLayer *line = self.lines[index];
if (hidden) {
[line removeAnimationForKey:@"kOpacityAnimation"];
}else{
[line addAnimation:[self opacityAnimation] forKey:@"kOpacityAnimation"];
}
[UIView animateWithDuration:0.25 animations:^{
line.hidden = hidden;
}];
}
光标闪动动画为:
//闪动动画
- (CABasicAnimation *)opacityAnimation {
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacityAnimation.fromValue = @(1.0);
opacityAnimation.toValue = @(0.0);
opacityAnimation.duration = 0.9;
opacityAnimation.repeatCount = HUGE_VALF;
opacityAnimation.removedOnCompletion = YES;
opacityAnimation.fillMode = kCAFillModeForwards;
opacityAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
return opacityAnimation;
}
- 最后设置键盘的弹出和隐藏。默认进来设置开始编辑,弹出键盘,当输入的数字长度等于输入框的位数时,则结束编辑,隐藏键盘。
//开始编辑
- (void)beginEdit{
[self.textView becomeFirstResponder];
}
//结束编辑
- (void)endEdit{
[self.textView resignFirstResponder];
}
完成的层次图为:
层次图
层级关系为:
层级关系
以上显示的是4位验证码输入框,如果想要6位的输入框,则在调用创建输入框视图的方法- (instancetype)initWithFrame:(CGRect)frame inputType:(NSInteger)inputNum selectCodeBlock:(SelectCodeBlock)CodeBlock;
中将传入参数inputNum设置为6即可。在ViewController
中创建输入框视图如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.codeView];
}
- (CodeInputView *)codeView {
// __weak typeof(self) selfWeak = self;
if (!_codeView) {
_codeView = [[CodeInputView alloc]initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 80) inputType:4 selectCodeBlock:^(NSString * code) {
NSLog(@"code === %@",code);
}];
_codeView.center = self.view.center;
}
return _codeView;
}
Demo地址:自定义验证码输入框(4位或6位)