問題描述
TextField中輸入身份證號,手機號,銀行卡號時每隔幾位需要新增空格。當輸入錯誤時需要從末尾或者中間刪除,刪除之後還要保持當前textfield的中內容保持每隔幾位就有一個空格的格式。這篇文章主要是為了解決這個問題
解決上面的問題主要要解決兩個點:
- 游標的位置
- 空格的位置
上面的兩種情況又可以分為:
- 從最後一位刪除
- 從中間刪除
- 一次刪除一個和多個
- 從最後一位新增
- 從中間新增
- 一次新增一位和多位
這幾種情況都要考慮游標的位置和空格的位置,每次新增和刪除都要重新計算.
實現:
首先詳細說下?的這個代理方法:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
複製程式碼
range
shouldChangeCharactersInRange :有location 和length 兩個引數 location 是即將被替代的內容的位置 length 是即將被替代的內容的長度
string
用來替代在range位置內容的字串
假如: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;
複製程式碼
設定游標位置的方法
根據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 方法中很重要的一個引數是偏移量
新增空格的方法
// 在指定的位置新增空格
+(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];
複製程式碼
通過以上幾種情況就可以解決文章開頭描述的問題了.
Demo 在這裡 我是 Demo
轉載請註明出處 https://juejin.im/post/5a527443f265da3e324551ee 謝謝!
如果解決了您的問題,請點贊支援下哈!