RKCodeInputView.m 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. //
  2. // RKCodeInputView.m
  3. // YBSoul
  4. //
  5. // Created by YB007 on 2021/3/9.
  6. //
  7. #import "RKCodeInputView.h"
  8. //#import "RKCodeView.h"
  9. @interface RKCodeInputView() <UITextFieldDelegate>
  10. ///输入框
  11. @property (strong, nonatomic) UITextField *textField;
  12. ///格子数组
  13. @property (nonatomic,strong) NSMutableArray <RKCodeView *> *arrayTextFidld;
  14. ///记录上一次的字符串
  15. @property (strong, nonatomic) NSString *lastString;
  16. /////放置小格子
  17. //@property (strong, nonatomic) UIView *contentView;
  18. @end
  19. @implementation RKCodeInputView
  20. - (instancetype)init {
  21. if (self = [super init]) {
  22. [self config];
  23. }
  24. return self;
  25. }
  26. - (instancetype)initWithFrame:(CGRect)frame {
  27. self = [super initWithFrame:frame];
  28. if (self) {
  29. [self config];
  30. }
  31. return self;
  32. }
  33. - (void)config {
  34. _codeCount = 4; //在初始化函数里面, 如果重写了某个属性的setter方法, 那么使用 self.codeCount 会直接调用重写的 setter 方法, 会造成惊喜!
  35. _codeSpace = 20;
  36. //初始化数组
  37. _arrayTextFidld = [NSMutableArray array];
  38. _lastString = @"";
  39. self.backgroundColor = UIColor.blackColor;
  40. //输入框
  41. _textField = [[UITextField alloc] init];
  42. _textField.backgroundColor = [UIColor purpleColor];
  43. _textField.keyboardType = UIKeyboardTypeNumberPad;
  44. _textField.delegate = self;
  45. [self addSubview:_textField];
  46. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textFieldDidChangeValue:) name:UITextFieldTextDidChangeNotification object:_textField];
  47. //放置View
  48. _contentView = [[UIView alloc] init];
  49. _contentView.backgroundColor = UIColor.clearColor;
  50. _contentView.userInteractionEnabled = NO;
  51. [self addSubview:_contentView];
  52. }
  53. - (void)setBecomeFirstRes:(BOOL)becomeFirstRes {
  54. _becomeFirstRes = becomeFirstRes;
  55. if (_becomeFirstRes) {
  56. [_textField becomeFirstResponder];
  57. }
  58. }
  59. - (void)setKeyboardType:(UIKeyboardType)keyboardType {
  60. _keyboardType = keyboardType;
  61. _textField.keyboardType = _keyboardType;
  62. }
  63. - (void)layoutSubviews {
  64. [super layoutSubviews];
  65. //改为外部主动调用
  66. //[self updateSubViews];
  67. }
  68. - (void)updateSubViews {
  69. self.textField.frame = self.bounds;
  70. self.contentView.frame = self.bounds;
  71. /*
  72. 方案1: 直接把原来的都删掉, 重新创建
  73. for (SMSCodeView *v in [self.arrayTextFidld reverseObjectEnumerator]) {
  74. [v removeFromSuperview];
  75. [self.arrayTextFidld removeObject:v];
  76. }
  77. */
  78. //方案2:能用就用,少了再建
  79. if (_arrayTextFidld.count < _codeCount) { //已经存在的子控件比新来的数要小, 那么就创建
  80. NSUInteger c = _codeCount - _arrayTextFidld.count;
  81. for (NSInteger i = 0; i < c; i ++) {
  82. RKCodeView *v = [[RKCodeView alloc] init];
  83. if (_textFont) {
  84. v.textFont = _textFont;
  85. }
  86. if (_textCor) {
  87. v.textCor = _textCor;
  88. }
  89. if (_uiStyle) {
  90. v.uiStyle = _uiStyle;
  91. }
  92. if (_secureTextEntry) {
  93. v.secureTextEntry = _secureTextEntry;
  94. }
  95. [_arrayTextFidld addObject:v];
  96. }
  97. } else if (_arrayTextFidld.count == _codeCount) { //个数相等
  98. return; //如果return,那么就是什么都不做, 如果不return, 那么后续可以更新颜色之类, 或者在转屏的时候重新布局
  99. } else if (_arrayTextFidld.count > _codeCount) { //个数有多余, 那么不用创建新的, 为了尽可能释放内存, 把不用的移除掉,
  100. NSUInteger c = _arrayTextFidld.count - _codeCount;
  101. for (NSInteger i = 0; i < c; i ++) {
  102. [_arrayTextFidld.lastObject removeFromSuperview];
  103. [_arrayTextFidld removeLastObject];
  104. }
  105. }
  106. //可用宽度 / 格子总数
  107. CGFloat w = (self.bounds.size.width - _codeSpace * (_codeCount - 1)) / (_codeCount * 1.0);
  108. //重新布局小格子
  109. for (NSInteger i = 0; i < _arrayTextFidld.count; i ++) {
  110. RKCodeView *t = _arrayTextFidld[i];
  111. [self.contentView addSubview:t];
  112. t.frame = CGRectMake(i * (w + _codeSpace), 0, w, self.bounds.size.height);
  113. [t updateCodeView];
  114. }
  115. }
  116. //已经编辑
  117. - (void)textFieldDidChangeValue:(NSNotification *)notification {
  118. UITextField *sender = (UITextField *)[notification object];
  119. /*
  120. bug: NSUInteger.
  121. sender.text.length 返回值是 NSUInteger,无符号整型, 当两个无符号整型做减法, 如果 6 - 9, 那么不会得到 -3, 而是一串很长的整型数, 也就是计算失误
  122. */
  123. BOOL a = sender.text.length >= self.lastString.length;
  124. BOOL b = sender.text.length - self.lastString.length >= _codeCount;
  125. if (a && b) { //判断为一连串验证码输入, 那么,最后N个,就是来自键盘上的短信验证码,取最后N个
  126. NSLog(@"一连串的输入");
  127. sender.text = [sender.text substringFromIndex:sender.text.length - _codeCount];
  128. }
  129. /*
  130. if (sender.text.length >= _codeCount + 1) { //对于持续输入,只要前面N个就行 sender.text.length >= _codeCount + 1
  131. NSLog(@"持续输入");
  132. sender.text = [sender.text substringToIndex:_codeCount - 1];
  133. }
  134. */
  135. BOOL shouldStop = NO;
  136. if (sender.text.length > _codeCount) {
  137. NSLog(@"持续输入");
  138. shouldStop = YES;
  139. sender.text = [sender.text substringToIndex:_codeCount];
  140. }
  141. if (shouldStop) {
  142. return;
  143. }
  144. //字符串转数组
  145. NSMutableArray <NSString *> *stringArray = [NSMutableArray array];
  146. NSString *temp = nil;
  147. for(int i = 0; i < [sender.text length]; i++) {
  148. temp = [sender.text substringWithRange:NSMakeRange(i,1)];
  149. [stringArray addObject:temp];
  150. }
  151. ///----> 20220602 新增密文
  152. /*
  153. //设置文字
  154. for(int i = 0; i < self.arrayTextFidld.count; i++) {
  155. RKCodeView *SMSCodeView = self.arrayTextFidld[i];
  156. if (i < stringArray.count) {
  157. SMSCodeView.text = stringArray[i];
  158. } else {
  159. SMSCodeView.text = @"";
  160. }
  161. }
  162. */
  163. //新增密文方式解决闪烁问题
  164. int lastIdx = 0;
  165. if (a) {
  166. //NSLog(@"rk_code:++++:%lu===%lu",(unsigned long)self.arrayTextFidld.count,(unsigned long)stringArray.count);
  167. lastIdx = (int)stringArray.count - 1;
  168. RKCodeView *SMSCodeView = self.arrayTextFidld[lastIdx];
  169. SMSCodeView.text = [stringArray lastObject];
  170. }else{
  171. //NSLog(@"rk_code:----%lu===%lu",(unsigned long)self.arrayTextFidld.count,(unsigned long)stringArray.count);
  172. lastIdx = (int)stringArray.count;
  173. RKCodeView *SMSCodeView = self.arrayTextFidld[lastIdx];
  174. SMSCodeView.text = @"";
  175. }
  176. ///<-----
  177. //设置光标
  178. if (stringArray.count == 0) {
  179. for(int i = 0; i < self.arrayTextFidld.count; i++) {
  180. BOOL hide = (i == 0 ? YES : NO);
  181. RKCodeView *SMSCodeView = self.arrayTextFidld[i];
  182. SMSCodeView.showCursor = hide;
  183. }
  184. } else if (stringArray.count == self.arrayTextFidld.count) {
  185. for(int i = 0; i < self.arrayTextFidld.count; i++) {
  186. RKCodeView *SMSCodeView = self.arrayTextFidld[i];
  187. SMSCodeView.showCursor = NO;
  188. }
  189. } else {
  190. for(int i = 0; i < self.arrayTextFidld.count; i++) {
  191. RKCodeView *SMSCodeView = self.arrayTextFidld[i];
  192. if (i == stringArray.count - 1) {
  193. SMSCodeView.showCursor = YES;
  194. } else {
  195. SMSCodeView.showCursor = NO;
  196. }
  197. }
  198. }
  199. if (stringArray.count == self.arrayTextFidld.count) {
  200. [self.textField resignFirstResponder];
  201. if (self.finishEvent) {
  202. self.finishEvent();
  203. }
  204. }
  205. self.lastString = sender.text;
  206. if (self.changeEvent) {
  207. self.changeEvent();
  208. }
  209. }
  210. - (void)textFieldDidBeginEditing:(UITextField *)textField {
  211. //检查上一次的字符串
  212. if (self.lastString.length == 0 || self.lastString.length == 1) {
  213. self.arrayTextFidld.firstObject.showCursor = YES;
  214. } else if (self.lastString.length == self.arrayTextFidld.count) {
  215. self.arrayTextFidld.lastObject.showCursor = YES;
  216. } else {
  217. self.arrayTextFidld[self.lastString.length - 1].showCursor = YES;
  218. }
  219. }
  220. - (NSString *)codeText {
  221. return self.textField.text;
  222. }
  223. - (BOOL)resignFirstResponder {
  224. for(int i = 0; i < self.arrayTextFidld.count; i++) {
  225. RKCodeView *SMSCodeView = self.arrayTextFidld[i];
  226. SMSCodeView.showCursor = NO;
  227. }
  228. [self.textField resignFirstResponder];
  229. return YES;
  230. }
  231. - (BOOL)becomeFirstResponder {
  232. [self.textField becomeFirstResponder];
  233. return YES;
  234. }
  235. ///如果要求可以随时更改输入位数, 那么,
  236. - (void)setCodeCount:(NSInteger)codeCount {
  237. _codeCount = codeCount;
  238. //因为个数改变,清空之前输入的内容
  239. self.lastString = @"";
  240. self.textField.text = @"";
  241. for (NSInteger i = 0; i < _arrayTextFidld.count; i ++) {
  242. RKCodeView *t = _arrayTextFidld[i];
  243. t.text = @"";
  244. if (i == 0) {
  245. t.showCursor = YES;
  246. } else {
  247. t.showCursor = NO;
  248. }
  249. }
  250. [self setNeedsLayout];
  251. [self layoutIfNeeded];
  252. }
  253. /// 清空
  254. -(void)clearText;{
  255. self.lastString = @"";
  256. self.textField.text = @"";
  257. for (NSInteger i = 0; i < _arrayTextFidld.count; i ++) {
  258. RKCodeView *t = _arrayTextFidld[i];
  259. t.text = @"";
  260. if (i == 0) {
  261. t.showCursor = YES;
  262. } else {
  263. t.showCursor = NO;
  264. }
  265. }
  266. [self setNeedsLayout];
  267. [self layoutIfNeeded];
  268. [_textField becomeFirstResponder];
  269. }
  270. /// 取消光标
  271. -(void)cancelCursor {
  272. for (RKCodeView *t in _arrayTextFidld) {
  273. t.showCursor = NO;
  274. }
  275. [_textField resignFirstResponder];
  276. }
  277. @end