開發(fā)過程中會經(jīng)常遇到各種優(yōu)化性的需求喉钢,比如對輸入控件的一些限制性操作。如:限制UITextField的輸入長度良姆。
關(guān)于英文肠虽、數(shù)字、中文玛追、emoji中每個字符所占的長度不同税课。根據(jù)需求的不同,自己進(jìn)行計算豹缀。本文不做贅述伯复。
[需要注意的是:限制的總長度 - 現(xiàn)有的總長度 > 本次輸入長度慨代。那么本次輸入不會成功邢笙。]
本文共提供三類方法來解決這種需求(三個方法用于用戶輸入,一個方法用于代碼賦值)
UITextFieldDelegate
從系統(tǒng)提供的textField的方法里面進(jìn)行查找
在UITextFieldDelegate里面通過// return NO to not change text這條注釋找到了這么一個方法:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
// return NO to not change text
可以看出這個方法是系統(tǒng)提供的控制UITextField的text這個屬性的方法侍匙。而字符限制的輸入本質(zhì)上是更改其輸入的文本信息氮惯。
這個實例方法有三個參數(shù)```textfield```、```range```想暗、```string```妇汗。分別表示當(dāng)前相應(yīng)的textfield,當(dāng)前已經(jīng)顯示的字符被更改的范圍说莫,要更改的內(nèi)容杨箭。以及一個```BOOL```類型的返回值來控制是否執(zhí)行本次更改。
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString )string
{ // kMaxLimit的值為你限制輸入的字符數(shù) */
if (range.location == kMaxLimit && range.length == 0) {
// textField.text = [textField.text substringToIndex:kMaxLimit];
// 當(dāng)返回NO的情況下已經(jīng)不會輸入本次內(nèi)容了储狭』バ觯可以進(jìn)行測試捣郊,下文會給出原因。
return NO;
}
return YES;
}
通過這個方法已經(jīng)能控制鍵入的字符長度了慈参。但是呛牲,用戶這個時候是可以通過粘貼這個功能輸入超過我們限制條件的情況的。如果用戶通過粘貼來輸入驮配,還是能夠放入我們超出我們限制條件的內(nèi)容娘扩。可以通過增加邏輯判斷來解決:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
NSUInteger maxLimit = 9; // 模擬長度為9的情況.
if (range.location == maxLimit && range.length == 0) { // 限制了輸入時候的情況
return NO;
}
if (range.location < maxLimit && (string.length + range.location) > maxLimit) { // 限制了粘貼字符時候的情況
return NO;
}
return YES;
}
這個時候就解決了用戶輸入(鍵入壮锻、粘貼)時的字符長度限制琐旁。
***
###Target Action機(jī)制
因為UITextField繼承于UIControl類,那么同樣可以重載父類方法來實現(xiàn)這種需求猜绣。需要注意的是最后一個參數(shù)旋膳,在枚舉值中選取的是`UIControlEventEditingChanged`這種情況。
[textFieldObject addTarget:self action:@selector(textFieldCharacterChange:) forControlEvents:UIControlEventEditingChanged];
- (void)textFieldCharacterChange:(UITextField *)textField
{
if (textField.text.length > 11) {
textField.text = [textField.text substringToIndex:11];
}
}
這樣一個判斷就可以在用戶操作的時候( 鍵入途事、粘貼) 進(jìn)行字符長度限制了验懊。
***
###觀察者模式
在TextField的API里面,系統(tǒng)提供了幾種監(jiān)聽的屬性.根據(jù)時機(jī)與作用選取第三個尸变。`UITextFieldTextDidChangeNotification`
>`UIKIT_EXTERN NSNotificationName const UITextFieldTextDidBeginEditingNotification;`
>`UIKIT_EXTERN NSNotificationName const UITextFieldTextDidEndEditingNotification;`
>`UIKIT_EXTERN NSNotificationName const UITextFieldTextDidChangeNotification;`
代碼如下:
if ([self respondsToSelector:@selector(test:)]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test:) name:UITextFieldTextDidChangeNotification object:nil];
}
(void)test:(NSNotification *)notification {
UITextField *tf = notification.object;
if (tf.text.length > 10) {
tf.text = [tf.text substringToIndex:10];
}
}(void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
}
這個時候做測試义图,會發(fā)現(xiàn)能監(jiān)聽到響應(yīng)用戶的操作,但是同樣監(jiān)聽不到代碼的賦值操作召烂。
***
如果需要先對UITextFIeld賦值(比如用戶修改個人信息的時候)碱工,如果因為某些原因,字符長度超出了預(yù)期奏夫,那么就容易引起用戶誤解怕篷。代碼賦值可以使用KVO進(jìn)行監(jiān)聽,代碼如下:
[_textField addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
UITextField *tf = object;
if (tf.text.length > 10) {
tf.text = [tf.text substringToIndex:10];
}
}(void)dealloc {
[_textField removeObserver:self forKeyPath:@"text"];
}
這時候會發(fā)現(xiàn)酗昼,此時的監(jiān)聽只能監(jiān)聽到代碼對text進(jìn)行的賦值操作廊谓,不能監(jiān)聽到用戶輸入的操作。剛好和上面的方法在作用區(qū)域上互補(bǔ)麻削。
***
關(guān)于上述的方法:
一蒸痹、UITextFieldDelegate里提供的方法,能夠處理掉用戶鍵入的操作呛哟。但是當(dāng)用戶采用粘貼的形式輸入內(nèi)容的時候叠荠,會出現(xiàn)不可預(yù)知的bug。API里最適合處理這個需求的方法扫责,但是需要大量的邏輯判斷來滿足需求榛鼎。
二、通過對UITextField的父類逐級上查,發(fā)現(xiàn)其可以使用TargetAction機(jī)制進(jìn)行通信與監(jiān)測者娱。這個方法使用簡單蜘渣,邏輯上易懂。(個人喜歡用這一種)
三肺然、觀察者模式蔫缸。`UITextFieldTextDidChangeNotification `是對用戶操作的相應(yīng)于監(jiān)聽;`KVO`是對代碼賦值時的監(jiān)聽际起。
***
以上內(nèi)容為如有紕漏拾碌,還請指出交流。