UITextView和UITextField沒(méi)有自帶的輸入字?jǐn)?shù)限制功能膊毁。
如果需要實(shí)現(xiàn)這個(gè)功能,一般的做法有兩種基跑,delegate和監(jiān)控Notification婚温。
先看看UITextViewDelegate,UITextField也是類似的媳否。
@protocol UITextViewDelegate <NSObject, UIScrollViewDelegate>
@optional
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
- (BOOL)textViewShouldEndEditing:(UITextView *)textView;
- (void)textViewDidBeginEditing:(UITextView *)textView;
- (void)textViewDidEndEditing:(UITextView *)textView;
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
- (void)textViewDidChange:(UITextView *)textView;
- (void)textViewDidChangeSelection:(UITextView *)textView;
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction NS_AVAILABLE_IOS(10_0);
- (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction NS_AVAILABLE_IOS(10_0);
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange NS_DEPRECATED_IOS(7_0, 10_0, "Use textView:shouldInteractWithURL:inRange:forInteractionType: instead");
- (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange NS_DEPRECATED_IOS(7_0, 10_0, "Use textView:shouldInteractWithTextAttachment:inRange:forInteractionType: instead");
@end
初步來(lái)看栅螟,會(huì)選用以下這兩個(gè)回調(diào)方法做處理。
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
- (void)textViewDidChange:(UITextView *)textView;
選了第一個(gè)篱竭,以下的代碼力图,在iOS 10上運(yùn)行,沒(méi)問(wèn)題掺逼,so easy~~~
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
if (textView.text.length + text.length > kLimitCount) {
NSLog(@"%@",[NSString stringWithFormat:@"最高支持輸入%d個(gè)字",kLimitCount]);
if (textView.text.length > 0) {
[self.textViewTipsLabel setHidden:YES];
} else {
[self.textViewTipsLabel setHidden:NO];
}
return NO;
}
return YES;
}
但是too young too sample吃媒,在iOS 9上出問(wèn)題了,如果使用iOS 9輸入中文吕喘,在選擇中文時(shí)赘那,不會(huì)調(diào)用shouldChangeTextInRange的回調(diào)。
呵呵~~~ 這個(gè)應(yīng)該是apple的問(wèn)題吧兽泄,有問(wèn)題就要解決啊漓概,所以同時(shí)需要在textViewDidChange中也需要做處理。
- (void)textViewDidChange:(UITextView *)textView {
if (textView.text.length > kLimitCount) {
// 超出限制
textView.text = [textView.text substringToIndex:kLimitCount];
NSLog(@"%@",[NSString stringWithFormat:@"最高支持輸入%d個(gè)字",kLimitCount]);
}
if (textView.text.length > 0) {
[self.textViewTipsLabel setHidden:YES];
} else {
[self.textViewTipsLabel setHidden:NO];
}
}
textViewTipsLabel是一個(gè)提示的label病梢,因?yàn)閁ITextView沒(méi)有placeholder胃珍。
OK,大概的解決方案就完整了蜓陌,如果工程中大量用到UITextView UITextField 輸入字?jǐn)?shù)限制觅彰,可以寫一個(gè)分類實(shí)現(xiàn),(不推薦使用繼承的方式)
分類中監(jiān)控自己的delegate顯然不是很好(自己回調(diào)自己钮热,然后又要回調(diào)出去)填抬。所以考慮用NSNotificationCenter。UITextView相關(guān)的Notification隧期,有以下幾個(gè)飒责,
UIKIT_EXTERN NSNotificationName const UITextViewTextDidBeginEditingNotification;
UIKIT_EXTERN NSNotificationName const UITextViewTextDidChangeNotification;
UIKIT_EXTERN NSNotificationName const UITextViewTextDidEndEditingNotification;
UITextView的分類UITextView+InputLimit只能監(jiān)控UITextViewTextDidChangeNotification了。
同時(shí)分類中增加兩個(gè)屬性(分類是可以增加屬性的仆潮,具體的方法網(wǎng)上很多)宏蛉。
inputLimit_placeholderLabel:占位的label,做提示用性置,可以不顯示拾并。
inputLimit_limitCount:現(xiàn)在字符長(zhǎng)度,方便使用。
外部類只需要關(guān)系這兩個(gè)屬性就好了嗅义,在設(shè)置屬性時(shí)或做相應(yīng)的處理屏歹,
以下是部分關(guān)鍵代碼:
- (void)setInputLimit_placeholderLabel:(UILabel *)inputLimit_placeholderLabel {
if (!inputLimit_placeholderLabel && inputLimit_placeholderLabel != self.inputLimit_placeholderLabel) {
// 移除舊的placeholderLabel
[self.inputLimit_placeholderLabel removeFromSuperview];
// 添加新的
[self addSubview:inputLimit_placeholderLabel];
objc_setAssociatedObject(self, PlaceholderLabelKey, inputLimit_placeholderLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
- (void)setInputLimit_limitCount:(NSUInteger)inputLimit_limitCount {
if (inputLimit_limitCount == self.inputLimit_limitCount) {
return;
}
if (self.inputLimit_limitCount == 0 && inputLimit_limitCount > 0) {
// 從0到不是0,需要注冊(cè)通知
[self addInputLimit];
}
if (inputLimit_limitCount == 0) {
// limitCount為0之碗,不需要監(jiān)控了
[self removeInputLimit];
}
objc_setAssociatedObject(self, LimitCountKey, @(inputLimit_limitCount), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(void)textViewDidChange:(NSNotification *)notification {
#if DEBUG
NSCAssert(self.inputLimit_limitCount > 0, @"監(jiān)控了UITextViewTextDidChangeNotification的消息蝙眶,但是沒(méi)有設(shè)置limitCount的大小");
#endif
UITextView *textView = (UITextView *)notification.object;
// 不是當(dāng)前textView和沒(méi)有限制limitCount時(shí),不做處理
if (textView == self && self.inputLimit_limitCount != 0) {
if (textView.text.length > self.inputLimit_limitCount) {
// 超出限制
textView.text = [textView.text substringToIndex:self.inputLimit_limitCount];
// 發(fā)送消息
[[NSNotificationCenter defaultCenter] postNotificationName:UITextViewInputLimitBeyondNotification object:self];
#if DEBUG
NSLog(@"%@",[NSString stringWithFormat:@"最高支持輸入%lu個(gè)字",(unsigned long)self.inputLimit_limitCount]);
#endif
}
if (textView.text.length > 0) {
[self.inputLimit_placeholderLabel setHidden:YES];
} else {
[self.inputLimit_placeholderLabel setHidden:NO];
}
}
}
好继控,到這里械馆,基本已經(jīng)完事了。這里有一點(diǎn)一開始很困惑就是分類中在設(shè)置字符長(zhǎng)度注冊(cè)(addObserver)了NSNotificationCenter武通,必須有個(gè)地方需要移除(removeObserver)霹崎。如果不移除,UITextView釋放時(shí)候冶忱,NSNotificationCenter發(fā)送通知會(huì)不會(huì)造成崩潰尾菇。
有驗(yàn)證過(guò)在ARC的環(huán)境下(現(xiàn)在都是ARC的代碼了吧。囚枪。派诬。)不需要移除。原因是UITextView本身的dealloc链沼,
會(huì)調(diào)用removeObserver的方法默赂。具體的驗(yàn)證這里不細(xì)講了。
如果要移除可以考慮重寫分類中的dealloc括勺,這樣比較麻煩需要在load函數(shù)中把方法替換缆八。