项目中会使用大量的
UITextField
和UITextView
输入控件,而且经常会有需要限制输入字数的情况,比如昵称限制10个字符、意见反馈限制200字符等等。这个时候我们就需要限制字符的个数了,由于中文和英文所占的字节不一样,中文字符占用两个字节”,“英文字符占用一个字节 (具体请看java中的“中文字符”和“英文字符”各占用几个字节?)。
- 所以在OC中也会涉及到中英文所占字节不一样从而导致的限制字符不准确的问题,下面开始上代码。
1、UITextField的字数限制
我这里使用了给UITextField添加Category的方式,创建一个名为LimitTextNum的类别,在.h文件中
添加一个属性@property (nonatomic, assign) NSInteger maxLenght;(关于如何在类别中添加
属性,自行百度,或者看我的代码)。
首先我们知道UITextField有个textFieldEditChanged方法,添加方法[self addTarget:self action:@selector(textFieldEditChanged:) forControlEvents:UIControlEventEditingChanged];
之后每输入一个字符串就回调用一次这个方法。
下面是关键代码:
- (void)textFieldEditChanged:(UITextField *)field{
NSString *toBeString = field.text;
NSString *lang = [[UIApplication sharedApplication]textInputMode].primaryLanguage;
if ([lang isEqualToString:@"zh-Hans"]) {//中文
UITextRange *seleRange = [field markedTextRange];//获取高亮
UITextPosition *position = [field positionFromPosition:seleRange.start offset:0];
if (!position) {
if (toBeString.length > self.maxLenght) {
NSRange rangeIndex = [toBeString rangeOfComposedCharacterSequenceAtIndex:self.maxLenght];
if (rangeIndex.length == 1){
field.text = [toBeString substringToIndex:self.maxLenght];
UIAlertController *alerController = [UIAlertController alertControllerWithTitle:@"提示" message:[NSString stringWithFormat:@"最多只能输入%ld个字符",self.maxLenght] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
[alerController addAction:okAction];
[[self superViewController:self] presentViewController:alerController animated:YES completion:nil];
[field resignFirstResponder];
}else{
NSRange rangeRange = [toBeString rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, self.maxLenght)];
field.text = [toBeString substringWithRange:rangeRange];
}
}
}
}else{
//非中文
if (toBeString.length > self.maxLenght) {
NSRange rangeIndex = [toBeString rangeOfComposedCharacterSequenceAtIndex:self.maxLenght];
if (rangeIndex.length == 1) {
field.text = [toBeString substringToIndex:self.maxLenght];
UIAlertController *alerController = [UIAlertController alertControllerWithTitle:@"提示" message:[NSString stringWithFormat:@"最多只能输入%ld个字符",self.maxLenght] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
[alerController addAction:okAction];
[[self superViewController:self] presentViewController:alerController animated:YES completion:nil];
[field resignFirstResponder];
}else{
NSRange rangeRange = [toBeString rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, self.maxLenght)];
field.text = [toBeString substringWithRange:rangeRange];
}
}
}
}
在使用时只需要在需要限制字符数的文件中#import "UITextField+LimitTextNum.h"
,然后_textField.maxLenght = 5;
限制字符数为5,可以自行设置最大字符数。
2、限制UITextView
的字数以及添加 placeholder
同样我们创建一个名为LimitTextViewNum的类别,在.h文件中添加两个方法
- (BOOL)limitTextViewNum:(UITextView *)textView
text:(NSString *)text
maxLenght:(NSInteger)maxLenght
textInRange:(NSRange)range
appearTextLabel:(UILabel *)label; (限制字符数)
- (void)setTextViewPlaceholde:(NSString*)placeholder;(设置placeholder)
在.m中实现方法
- (BOOL)limitTextViewNum:(UITextView *)textView
text:(NSString *)text
maxLenght:(NSInteger)maxLenght
textInRange:(NSRange)range
appearTextLabel:(UILabel *)label{
if ([text isEqualToString:@"\n"]) {
return NO;
}
//不支持系统表情输入
if ([[textView textInputMode] primaryLanguage] == nil || [[[textView textInputMode] primaryLanguage] isEqualToString:@"emoji"]) {
UIAlertController *alerController = [UIAlertController alertControllerWithTitle:@"提示" message:@"只允许输入文字" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
[alerController addAction:okAction];
[[self superViewController:self] presentViewController:alerController animated:YES completion:nil];
return NO;
}
UITextRange *selectedrange = [textView markedTextRange];
UITextPosition *position = [textView positionFromPosition:selectedrange.start offset:0];
//如果有高亮且当前字数开始位置小于最大限制时允许输入
if (selectedrange && position) {
NSInteger startOffset = [textView offsetFromPosition:textView.beginningOfDocument toPosition:selectedrange.start];
NSInteger endOffset = [textView offsetFromPosition:textView.beginningOfDocument toPosition:selectedrange.end];
NSRange offsetRange = NSMakeRange(startOffset, endOffset - startOffset);
if (offsetRange.location < maxLenght) {
return YES;
}else{
return NO;
}
}
NSString *comcatstr = [textView.text stringByReplacingCharactersInRange:range withString:text];
NSInteger caninputlen = maxLenght - comcatstr.length;
if (caninputlen >= 0) {
return YES;
}else{
NSInteger len = text.length + caninputlen;
NSRange rg = {0,MAX(len, 0)};
if (rg.length > 0) {
NSString *str = @"";
//判断是否只普通的字符或asc码(对于中文和表情返回NO)
BOOL asc = [text canBeConvertedToEncoding:NSASCIIStringEncoding];
if (asc) {
str = [text substringWithRange:rg];//因为是ascii码直接取就可以了不会错
}else{
__block NSInteger idx = 0;
__block NSString *trimString = @"";//截取出的字串
//使用字符串遍历,这个方法能准确知道每个emoji是占一个unicode还是两个
[text enumerateSubstringsInRange:NSMakeRange(0, [text length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
if (idx >= rg.length) {
*stop =YES;//取出所需要就break,提高效率
return ;
}
trimString = [trimString stringByAppendingString:substring];
idx++;
}];
str = trimString;
}
//rang是指从当前光标处进行替换处理
[textView setText:[textView.text stringByReplacingCharactersInRange:range withString:str]];
//既然是超出部分截取了,哪一定是最大限制了。
label.text = [NSString stringWithFormat:@"%d/%ld",0,(long)maxLenght];
}
UIAlertController *alerController = [UIAlertController alertControllerWithTitle:@"提示" message:[NSString stringWithFormat:@"请输入小于%ld字符",(long)maxLenght] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
[alerController addAction:okAction];
[[self superViewController:self] presentViewController:alerController animated:YES completion:nil];
return NO;
}
}
-(void)setTextViewPlaceholde:(NSString*)placeholder{
placeHolderLabel = [[UILabel alloc]initWithFrame:CGRectMake(5, 8,self.bounds.size.width - 10, 0)];
placeHolderLabel.text = placeholder;
placeHolderLabel.numberOfLines = 0;
placeHolderLabel.font = self.font;
placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping;
placeHolderLabel.textColor = [UIColor lightGrayColor];
[self addSubview:placeHolderLabel];
[placeHolderLabel sizeToFit];
[self sendSubviewToBack:placeHolderLabel];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil];
}
在设置textView
的placeholder
时需要注册一个通知监听字符变化,通知的实现方法为
- (void)textChanged:(NSNotification *)notification{
UITextView*textView = (UITextView*)notification.object;
if (textView.text.length == 0) {
placeHolderLabel.hidden = NO;
}else{
placeHolderLabel.hidden = YES;
}
}
还需要在dealloc
移除这个通知
- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
同样在需要使用的地方引入类别的头文件,在textView
的代理方法中实现方法即可
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
return [textView limitTextViewNum:textView text:text maxLenght:_maxNum textInRange:range appearTextLabel:_textViewLb];
}
- 写在最后 ,这些都是在项目中用到的代码,也参考了很多大神的博客,算是比较完整的吧?如果发现任何不对或需要优化的地方,请给我留言,谢谢!
最后附上 demo