问题描述
TextField中输入身份证号,手机号,银行卡号时每隔几位需要添加空格。当输入错误时需要从末尾或者中间删除,删除之后还要保持当前textfield的中内容保持每隔几位就有一个空格的格式。这篇文章主要是为了解决这个问题
==解决上面的问题主要要解决两个点:==
- [ ] 光标的位置
- [ ] 空格的位置
上面的两种情况又可以分为:
- 从最后一位删除
- 从中间删除
- 一次删除一个和多个
- 从最后一位添加
- 从中间添加
- 一次添加一位和多位
这几种情况都要考虑光标的位置和空格的位置,每次添加和删除都要重新计算.
实现:
首先详细说下👇的这个代理方法:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
range
shouldChangeCharactersInRange :有location 和length 两个参数
location 是即将被替代的内容的位置
length 是即将被替代的内容的长度
string
用来替代在range位置内容的字符串
- [x] 假如:textfield中有内容为1234
三种情况解释下string 和range参数 :
- 光标在最后一位时删除一位 :那么string就是为空 range的location 就是光标前面的内容的下标即为3 length就是1 表示被删除的内容长度为1 ,结果是string占据了range表示的位置 即123,3后面的4被空字符串占据了即被删除了
- 光标在最后一位删除两位:操作,长按textfield选中23,点击删除。可以发现string依旧为空 ,range变为 location=1, length=2 即位置在下标为1 长度为2的字符串被空字符串替代了,还剩1,所谓的23被删除了
- 添加一个:光标在最后一位。输入一个2,此时range:location为1,length为0 即被替代的内容位置在下标为1的地方,长度是空。string为2,那么结果就是一个下标为1,长度为0的字符串被string替代了,即textfield内容添加了一个2,复制到textfield中的内容range 和 string 也是同样的适用
说的这么详细主要为下一步做准备。
UITextInput协议 中的几个属性和方法
设置光标的位置需要下面的两个方法
// 获取以fromPosition为基准偏移offset的光标位置。
- (nullable UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset;
// 创建一个UITextRange
- (nullable UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition;
- [x] 设置光标位置的方法
根据UITextInput协议的两个方法可以得出设置光标的位置的方法
+ (void)setCursorLocation:(UITextField *)textField withOffset:(NSInteger) offset{
// offset 光标要所处的位置
// 生成新的postion
UITextPosition *newPostion = [textField positionFromPosition:textField.beginningOfDocument offset:offset] ;
//设置光标 从一个点到另外一个点如果两个点一样 那么光标就在这个点
textField.selectedTextRange = [textField textRangeFromPosition:newPostion toPosition:newPostion];
}
==注意==:在textField中,有一个属性称之为selectedTextRange,这个属性为UITextRange类型,包含[start,end)两个值,通过实验可以发现,在没有文字被选取时,start 和 end的值一样 代表当前光标的位置;当有区域被选择时,start和end分别是选择的头和尾的光标位置
可以看出 setCursorLocation 方法中很重要的一个参数是偏移量
- [x] 添加空格的方法
// 在指定的位置添加空格
+(NSString*)insertString:(NSString*)string withBlankLocations:(NSArray<NSNumber *>*)locations {
if (!string) {
return nil;
}
NSMutableString* mutableString = [NSMutableString stringWithString:[string stringByReplacingOccurrencesOfString:@" " withString:@""]];
for (NSNumber *location in locations) {
if (mutableString.length > location.integerValue) {
[mutableString insertString:@" " atIndex:location.integerValue];
}
}
return mutableString;
}
上面这个方法是根据传入的空格的位置,遍历整个字符串,在指定的位置为字符串添加一个空格. 这个方法调用的时机就是 textField 中的 text 发生改变时调用,比如说删除或者增加字符串
++那么以下就根据不同的情况来计算偏移量设置光标 和 添加空格++
删除字符串
如何判断是点击了键盘的删除
如上面所说 在
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
这个代理方法中 当string为空时就是删除。range是一个位置表示string的位置 如果string为空时 range.length 表示删除的长度
- 删除一位
如果 range.length == 1
如果string 为空 且 range的location 为当前textField.text的长度减一 即location是textField.text的最后一个字符时 表示在最后一位删除
① 如果在最后一位删除一位不需要设置光标的位置和添加空格
② 如果不是最后一位删除一位则要判断删除的是不是空格。如果是空格则会连续删除两次
// 不是最后一位
NSInteger locationOffset = range.location;
if (range.location < text.length && [text characterAtIndex:range.location] == ' ' && [textField.selectedTextRange isEmpty]) {
[textField deleteBackward]; // 删除空格
locationOffset --;
}
[textField deleteBackward];// 删除空格前面的字符
上面的代码调用了两次[textField deleteBackward] 删除了两次
此时需要修改空格的位置和光标的位置,偏移量 offset 就是 range.location 的值 ,每删除一位 offset 就要减1
- 删除多位
同删除一位的逻辑一样 string为空切 range.length > 1 就表示一次删除多位
① 是否是在最后一位开始删除,如果再最后一位开始删除那么仍然不需要设置光标的位置,但是需要设置空格的位置
② 如果不是最后一位开始删除,则需要计算光标的位置,偏移量仍然是当前 range.location
添加字符串
如何判断是添加?
如上面所说 在
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
这个代理方法中 当string不为空时就是添加。range是一个位置表示string的位置 如果string不为空时 range.length 表示添加的字符串的长度
如果所输入的字符串长度还没有超出限制则直接添加到 textField 中然后在预定的位置添加空格
// 添加到textField 中 这个方法是 UIKeyInput 协议中的方法
[textField insertText:string];
//textField中的字符串发生变化需要重新设置空格
textField.text = [self insertString:textField.text withBlankLocations:blankLocation];
此时要计算偏移量 ,在计算是光标的位置是 range.location + string.length ,但是如果在光标位置出正好有空格则offset需要 +1 如下
NSInteger offset = range.location + string.length;
for (NSNumber *location in blankLocation) {
if (range.location == location.integerValue) {
offset ++;
}
}
[self setCursorLocation:textField withOffset:offset];
通过以上几种情况就可以解决文章开头描述的问题了.
如果解决了您的问题,请点赞支持下哈!