Compare commits

...

10 Commits

Author SHA1 Message Date
HouWan
f00a31ecf3 更新Readme,准备完善功能 2020-08-29 15:08:51 +08:00
HouWan
7bff9289f6 无奈
无奈
2018-12-14 15:14:14 +08:00
HouWan
8c76b2651f 光标1
光标1
2018-12-14 15:09:14 +08:00
HouWan
295eec3f77 更新预览图
更新预览图
2018-12-14 15:05:03 +08:00
HouWan
0b89268d9f 预览图
预览图
2018-12-14 15:03:25 +08:00
HouWan
b9d1b3a12f 删除老预览文件
删除老预览文件
2018-12-14 15:02:34 +08:00
HouWan
354ec92a31 光标测试
光标测试
2018-12-14 15:00:09 +08:00
HouWan
1ab7348e27 光标类
光标类
2018-12-14 14:58:01 +08:00
HouWan
e45fb8260e Update README.md
简书地址
2018-11-23 16:32:08 +08:00
HouWan
a7290c68ab Update ViewController.m
删除一个错误代码
2018-09-26 13:12:00 +08:00
8 changed files with 490 additions and 22 deletions

View File

@@ -16,6 +16,7 @@
87BA7284215B3E3B00A6911D /* HWTFCodeBView.m in Sources */ = {isa = PBXBuildFile; fileRef = 87BA727E215B3E3B00A6911D /* HWTFCodeBView.m */; };
87BA7285215B3E3B00A6911D /* HWTFCodeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 87BA727F215B3E3B00A6911D /* HWTFCodeView.m */; };
87BA7286215B3E3B00A6911D /* HWTextCodeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 87BA7280215B3E3B00A6911D /* HWTextCodeView.m */; };
87DCFBBE21C26B2100D4C08D /* HWTFCursorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 87DCFBBD21C26B2100D4C08D /* HWTFCursorView.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -35,6 +36,8 @@
87BA7281215B3E3B00A6911D /* HWTFCodeBView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HWTFCodeBView.h; sourceTree = "<group>"; };
87BA7282215B3E3B00A6911D /* HWTextCodeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HWTextCodeView.h; sourceTree = "<group>"; };
87BA7283215B3E3B00A6911D /* HWTFCodeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HWTFCodeView.h; sourceTree = "<group>"; };
87DCFBBC21C26B2100D4C08D /* HWTFCursorView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HWTFCursorView.h; sourceTree = "<group>"; };
87DCFBBD21C26B2100D4C08D /* HWTFCursorView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HWTFCursorView.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -90,6 +93,8 @@
87BA727E215B3E3B00A6911D /* HWTFCodeBView.m */,
87BA7283215B3E3B00A6911D /* HWTFCodeView.h */,
87BA727F215B3E3B00A6911D /* HWTFCodeView.m */,
87DCFBBC21C26B2100D4C08D /* HWTFCursorView.h */,
87DCFBBD21C26B2100D4C08D /* HWTFCursorView.m */,
);
path = CodeTextView;
sourceTree = "<group>";
@@ -168,6 +173,7 @@
87BA726C215B3DC200A6911D /* ViewController.m in Sources */,
87BA7277215B3DC300A6911D /* main.m in Sources */,
87BA7269215B3DC200A6911D /* AppDelegate.m in Sources */,
87DCFBBE21C26B2100D4C08D /* HWTFCursorView.m in Sources */,
87BA7284215B3E3B00A6911D /* HWTFCodeBView.m in Sources */,
87BA7286215B3E3B00A6911D /* HWTextCodeView.m in Sources */,
);

View File

@@ -0,0 +1,51 @@
//
// HWTFCursorView.h
// CodeTextDemo
//
// Created by 侯万 on 2018/12/13.
// Copyright © 2018 小侯爷. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
基础版 - 下划线 - 有光标
*/
@interface HWTFCursorView : UIView
// ----------------------------Data----------------------------
/// 当前输入的内容
@property (nonatomic, copy, readonly) NSString *code;
// ----------------------------Method----------------------------
- (instancetype)initWithCount:(NSInteger)count margin:(CGFloat)margin;
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
@end
// ------------------------------------------------------------------------
// -----------------------------HWCursorLabel------------------------------
// ------------------------------------------------------------------------
@interface HWCursorLabel : UILabel
@property (nonatomic, weak, readonly) UIView *cursorView;
- (void)startAnimating;
- (void)stopAnimating;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,248 @@
//
// HWTFCursorView.m
// CodeTextDemo
//
// Created by on 2018/12/13.
// Copyright © 2018 . All rights reserved.
//
#import "HWTFCursorView.h"
@interface HWTFCursorView ()
@property (nonatomic, assign) NSInteger itemCount;
@property (nonatomic, assign) CGFloat itemMargin;
@property (nonatomic, weak) UITextField *textField;
@property (nonatomic, weak) UIControl *maskView;
@property (nonatomic, strong) NSMutableArray<HWCursorLabel *> *labels;
@property (nonatomic, strong) NSMutableArray<UIView *> *lines;
@property (nonatomic, weak) HWCursorLabel *currentLabel;
@end
@implementation HWTFCursorView
#pragma mark -
- (instancetype)initWithCount:(NSInteger)count margin:(CGFloat)margin
{
if (self = [super init]) {
self.itemCount = count;
self.itemMargin = margin;
[self configTextField];
}
return self;
}
- (void)configTextField
{
self.backgroundColor = [UIColor whiteColor];
self.labels = @[].mutableCopy;
self.lines = @[].mutableCopy;
UITextField *textField = [[UITextField alloc] init];
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
textField.keyboardType = UIKeyboardTypeNumberPad;
[textField addTarget:self action:@selector(tfEditingChanged:) forControlEvents:(UIControlEventEditingChanged)];
// YES使
// clearsOnBeginEditing secureTextEntry = YES
// textField.secureTextEntry = YES;
[self addSubview:textField];
self.textField = textField;
// textFieldmaskViewtextField
UIButton *maskView = [UIButton new];
maskView.backgroundColor = [UIColor whiteColor];
[maskView addTarget:self action:@selector(clickMaskView) forControlEvents:(UIControlEventTouchUpInside)];
[self addSubview:maskView];
self.maskView = maskView;
for (NSInteger i = 0; i < self.itemCount; i++)
{
HWCursorLabel *label = [HWCursorLabel new];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor darkTextColor];
label.font = [UIFont fontWithName:@"PingFangSC-Regular" size:41.5];
[self addSubview:label];
[self.labels addObject:label];
}
for (NSInteger i = 0; i < self.itemCount; i++)
{
UIView *line = [UIView new];
line.backgroundColor = [UIColor purpleColor];
[self addSubview:line];
[self.lines addObject:line];
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.labels.count != self.itemCount) return;
CGFloat temp = self.bounds.size.width - (self.itemMargin * (self.itemCount - 1));
CGFloat w = temp / self.itemCount;
CGFloat x = 0;
for (NSInteger i = 0; i < self.labels.count; i++)
{
x = i * (w + self.itemMargin);
UILabel *label = self.labels[i];
label.frame = CGRectMake(x, 0, w, self.bounds.size.height);
UIView *line = self.lines[i];
line.frame = CGRectMake(x, self.bounds.size.height - 1, w, 1);
}
self.textField.frame = self.bounds;
self.maskView.frame = self.bounds;
}
#pragma mark -
- (void)tfEditingChanged:(UITextField *)textField
{
if (textField.text.length > self.itemCount) {
textField.text = [textField.text substringWithRange:NSMakeRange(0, self.itemCount)];
}
for (int i = 0; i < self.itemCount; i++)
{
UILabel *label = [self.labels objectAtIndex:i];
if (i < textField.text.length) {
label.text = [textField.text substringWithRange:NSMakeRange(i, 1)];
} else {
label.text = nil;
}
}
[self cursor];
//
if (textField.text.length >= self.itemCount) {
[self.currentLabel stopAnimating];
[textField resignFirstResponder];
}
}
- (void)clickMaskView
{
[self.textField becomeFirstResponder];
[self cursor];
}
- (BOOL)endEditing:(BOOL)force
{
[self.textField endEditing:force];
[self.currentLabel stopAnimating];
return [super endEditing:force];
}
#pragma mark -
- (void)cursor
{
[self.currentLabel stopAnimating];
NSInteger index = self.code.length;
if (index < 0) index = 0;
if (index >= self.labels.count) index = self.labels.count - 1;
HWCursorLabel *label = [self.labels objectAtIndex:index];
[label startAnimating];
self.currentLabel = label;
}
- (NSString *)code
{
return self.textField.text;
}
@end
// ------------------------------------------------------------------------
// -----------------------------HWCursorLabel------------------------------
// ------------------------------------------------------------------------
@implementation HWCursorLabel
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupView];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self setupView];
}
return self;
}
#pragma mark - View
- (void)setupView
{
UIView *cursorView = [[UIView alloc] init];
cursorView.backgroundColor = [UIColor blueColor];
cursorView.alpha = 0;
[self addSubview:cursorView];
_cursorView = cursorView;
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGFloat h = 30;
CGFloat w = 2;
CGFloat x = self.bounds.size.width * 0.5;
CGFloat y = self.bounds.size.height * 0.5;
self.cursorView.frame = CGRectMake(0, 0, w, h);
self.cursorView.center = CGPointMake(x, y);
}
- (void)startAnimating
{
if (self.text.length > 0) return;
CABasicAnimation *oa = [CABasicAnimation animationWithKeyPath:@"opacity"];
oa.fromValue = [NSNumber numberWithFloat:0];
oa.toValue = [NSNumber numberWithFloat:1];
oa.duration = 1;
oa.repeatCount = MAXFLOAT;
oa.removedOnCompletion = NO;
oa.fillMode = kCAFillModeForwards;
oa.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[self.cursorView.layer addAnimation:oa forKey:@"opacity"];
}
- (void)stopAnimating
{
[self.cursorView.layer removeAnimationForKey:@"opacity"];
}
@end

View File

@@ -7,6 +7,13 @@
#import <UIKit/UIKit.h>
// 屏幕宽度高度
#define SW ([UIScreen mainScreen].bounds.size.width)
#define SH ([UIScreen mainScreen].bounds.size.height)
// 随机色
#define HWRandomColor [UIColor colorWithRed:arc4random_uniform(256)/255.0 green:arc4random_uniform(256)/255.0 blue:arc4random_uniform(256)/255.0 alpha:1.0]
@interface ViewController : UIViewController

View File

@@ -7,18 +7,18 @@
#import "ViewController.h"
#import "HWTFCodeView.h"
#import "HWTFCodeBView.h"
#import "HWTextCodeView.h"
// ----------------------------View----------------------------
#import "HWTFCodeView.h" // - 线
#import "HWTFCodeBView.h" // -
#import "HWTextCodeView.h" // - - 线
#import "HWTFCursorView.h" // - 线 -
@interface ViewController ()
@property (nonatomic, weak) HWTFCodeView *code1View;
@property (nonatomic, weak) HWTFCodeBView *code2View;
@property (nonatomic, weak) HWTFCodeView *code1View;
@property (nonatomic, weak) HWTFCodeBView *code2View;
@property (nonatomic, weak) HWTextCodeView *code3View;
@property (nonatomic, weak) HWTFCursorView *code4View;
@end
@@ -28,71 +28,103 @@
{
[super viewDidLoad];
UIScrollView *scrollView = [[UIScrollView alloc] init];
scrollView.contentSize = CGSizeMake(SW, SH * 1.5);
scrollView.layer.borderColor = [HWRandomColor CGColor];
scrollView.layer.borderWidth = 0.5;
scrollView.frame = CGRectMake(0, 0, SW, SH);
[self.view addSubview:scrollView];
UILabel *labx = [UILabel new];
labx.textColor = [UIColor grayColor];
labx.font = [UIFont systemFontOfSize:16];
labx.text = @"😘页面可滑动,防止键盘挡住效果😘";
labx.frame = CGRectMake(30, 45, 320, 15);
[scrollView addSubview:labx];
CGFloat x = 30;
CGFloat w = self.view.bounds.size.width - x * 2;
CGFloat w = SW - x * 2;
CGFloat h = 50;
CGFloat y = 100;
// --------------------------------------------------------------------
UILabel *labA = [UILabel new];
labA.hidden = YES;
labA.textColor = [UIColor orangeColor];
labA.font = [UIFont systemFontOfSize:13];
labA.text = @"基本实现原理 - 下划线";
labA.frame = CGRectMake(x, y, 200, 15);
[self.view addSubview:labA];
[scrollView addSubview:labA];
y = CGRectGetMaxY(labA.frame) + 10;
HWTFCodeView *code1View = [[HWTFCodeView alloc] initWithCount:6 margin:20];
code1View.frame = CGRectMake(x, y, w, h);
[self.view addSubview:code1View];
[scrollView addSubview:code1View];
self.code1View = code1View;
// --------------------------------------------------------------------
y = CGRectGetMaxY(code1View.frame) + 80;
y = CGRectGetMaxY(code1View.frame) + 60;
UILabel *labB = [UILabel new];
labB.textColor = [UIColor orangeColor];
labB.font = [UIFont systemFontOfSize:13];
labB.text = @"基本实现原理 - 方块";
labB.frame = CGRectMake(x, y, 200, 15);
[self.view addSubview:labB];
[scrollView addSubview:labB];
y = CGRectGetMaxY(labB.frame) + 30;
HWTFCodeBView *code2View = [[HWTFCodeBView alloc] initWithCount:6 margin:20];
code2View.frame = CGRectMake(x, y, w, h);
[self.view addSubview:code2View];
[scrollView addSubview:code2View];
self.code2View = code2View;
// --------------------------------------------------------------------
y = CGRectGetMaxY(code2View.frame) + 80;
y = CGRectGetMaxY(code2View.frame) + 60;
UILabel *labC = [UILabel new];
labC.textColor = [UIColor orangeColor];
labC.font = [UIFont systemFontOfSize:13];
labC.text = @"完善版 - 加入动画 - 下划线";
labC.frame = CGRectMake(x, y, 200, 15);
[self.view addSubview:labC];
[scrollView addSubview:labC];
y = CGRectGetMaxY(labC.frame) + 30;
HWTextCodeView *code3View = [[HWTextCodeView alloc] initWithCount:6 margin:20];
code3View.frame = CGRectMake(x, y, w, h);
[self.view addSubview:code3View];
[scrollView addSubview:code3View];
self.code3View = code3View;
// --------------------------------------------------------------------
y = CGRectGetMaxY(code3View.frame) + 60;
UILabel *labD = [UILabel new];
labD.textColor = [UIColor blueColor];
labD.font = [UIFont systemFontOfSize:13];
labD.text = @"基础版 - 下划线 - 有光标";
labD.frame = CGRectMake(x, y, 200, 15);
[scrollView addSubview:labD];
y = CGRectGetMaxY(labD.frame) + 30;
HWTFCursorView *code4View = [[HWTFCursorView alloc] initWithCount:6 margin:20];
code4View.frame = CGRectMake(x, y, w, h);
[scrollView addSubview:code4View];
self.code4View = code4View;
// --------------------------------------------------------------------
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)];
[self.view addGestureRecognizer:tap];
[scrollView addGestureRecognizer:tap];
}
@@ -101,6 +133,7 @@
[self.code1View endEditing:YES];
[self.code2View endEditing:YES];
[self.code3View endEditing:YES];
[self.code4View endEditing:YES];
}

129
README.md
View File

@@ -3,11 +3,134 @@
## iOS验证码输入
> * Xcode 10.0 (10A255) 新建项目并编辑
> * 代码日期2018年09月26日12:30
> * 代码日期2018年12月14日15:00
> * 代码语言Objective-C
> * 加入光标小Demo(2018年12月14日)
![效果图](https://github.com/HouWan/CodeTextDemo/blob/master/wx20180926_130752_2x-qibot.cn.png)
![效果图](https://github.com/HouWan/CodeTextDemo/blob/master/Screen.png)
简书地址:[点我](https://www.jianshu.com/)
简书地址:[点我](https://www.jianshu.com/p/23f7be3677be)
简书地址:[点我](https://www.jianshu.com/p/23f7be3677be)
----
## 博客内容
![img_2299-qibot.cn.png](https://upload-images.jianshu.io/upload_images/855108-f5c9f906ea6467dd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
如图所示现在很多App采用了类似下划线、方块等方式的验证码输入直观美观对于这种效果的实现方式大概有以下几种方式
##### 1.多个`UITextField`组成
这种方式好处是有光标闪烁、但是在处理删除和动画效果时就会显得有点笨拙OFO应该是这样实现的要严格处理好每个`UITextField``FirstResponder`
##### 2.一个`UITextField`组成,使用富文本
这个方式是可行的, 使用富文本设置每个字符的间距,允许编辑富文本,有光标闪烁,缺点应该也是不好处理动画效果。
##### 3.使用`UIView`绘制
这个是我在`GitHub`上看到的一个半成品Demo利用一个`UIView`,使用`Quartz 2D``UIBezierPath`进行绘制文本和下划线并处理输入事件和键盘事件其实整体下来代码也不多300行以内但是需要较好的iOS绘制功底。
##### 4.一个`UITextField`和多个`UILabel`
这个是我接下来介绍的思路这个思路的缺点应该是没有光标闪烁其实也能伪实现看是否必须要有这个需要了。这个思路比较简单方便加入动画纯粹下来就100多行接下来看结构和代码
![wx20180926_114423_2x-qibot.cn.png](https://upload-images.jianshu.io/upload_images/855108-1a2a12e3e3c0df88.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
新建一个`UIView`,初始化方法
```
- (instancetype)initWithCount:(NSInteger)count margin:(CGFloat)margin
{
if (self = [super init]) {
self.itemCount = count; // itemCount是验证码的个数比如6个
self.itemMargin = margin; // itemMargin是每个Label之间的间距
[self configTextField];
}
return self;
}
```
添加内部子控件(演示)
```
- (void)configTextField
{
UITextField *textField = [[UITextField alloc] init];
textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
textField.keyboardType = UIKeyboardTypeNumberPad;
[textField addTarget:self action:@selector(tfEditingChanged:) forControlEvents:(UIControlEventEditingChanged)];
// 小技巧这个属性为YES可以强制使用系统的数字键盘缺点是重新输入时会清空之前的内容
// clearsOnBeginEditing 属性并不适用于 secureTextEntry = YES 时
// textField.secureTextEntry = YES;
[self addSubview:textField];
self.textField = textField;
// 小技巧通过textField上层覆盖一个maskView可以去掉textField的长按事件
UIButton *maskView = [UIButton new];
maskView.backgroundColor = [UIColor whiteColor];
[maskView addTarget:self action:@selector(clickMaskView) forControlEvents:(UIControlEventTouchUpInside)];
[self addSubview:maskView];
self.maskView = maskView;
for (NSInteger i = 0; i < self.itemCount; i++)
{
UILabel *label = [UILabel new];
[self addSubview:label];
[self.labels addObject:label];
}
for (NSInteger i = 0; i < self.itemCount; i++)
{
UIView *line = [UIView new];
[self.lines addObject:line];
}
}
```
这里可能对`maskView `有点费解,`maskView`主要是为了挡住下面的`UITextField `,使用类`UIButton `是为了挡住事件,因为如果使用类`UIView `,会将事件传递到`self`,进而影响到外面隐藏键盘的代码,你可以试试就知道了。
##### 主要处理业务逻辑的代码
```
#pragma mark - 编辑改变
- (void)tfEditingChanged:(UITextField *)textField
{
if (textField.text.length > self.itemCount) {
textField.text = [textField.text substringWithRange:NSMakeRange(0, self.itemCount)];
}
for (int i = 0; i < self.itemCount; i++)
{
UILabel *label = [self.labels objectAtIndex:i];
if (i < textField.text.length) {
label.text = [textField.text substringWithRange:NSMakeRange(i, 1)];
} else {
label.text = nil;
}
}
// 输入完毕后,自动隐藏键盘
if (textField.text.length >= self.itemCount) {
[textField resignFirstResponder];
}
}
```
这里没有使用`UITextField ``delegate`,使用`UIControlEventEditingChanged`足以,但是如果你的需求能输入英文等其他字符,就需要实现`delegate`去限制。
至此,验证码输入的核心代码就***没有了***是不是感觉这么少在此基础上我用Demo实现了3个基本效果如图所示
![Screen.png](https://upload-images.jianshu.io/upload_images/855108-da661909e6280eb2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#### Github代码地址[点我](https://github.com/HouWan/CodeTextDemo)
#####小技巧Tip
当你不想让别人使用某个方法时,除了私有方法办法之外,还可以这么做:
```
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
```
## 有问题反馈
如果大家在使用过程中遇到其他问题,可以留言,我们共同解决。
**PS**:最近我有跳槽的想法,有工作机会的老板,欢迎骚扰哦!北京呦!
**END。**
*我是小侯爷。*
*在魔都艰苦奋斗,白天是上班族,晚上是知识服务工作者。*
*如果读完觉得有收获的话,记得关注和点赞哦。*
*非要打赏的话,我也是不会拒绝的。*

BIN
Screen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB