写在前面:
iOSUITextField的输入字数限制,网上有很多版本,但是似乎都不能满足我们项目的需求(例:输入框长度限制为10位,在输入过程中进行限制,其中包括输入Emoji),于是自己实现了该功能。
主要思想:
监听UITextField的shouldChangeCharactersInRange方法,获取输入内容string和range,此时获取剩余可输入字符个数(NSInteger remainTextCount = maxTextCount - textField.text.length + range.length;)。
对string进行判断,如果包含Emoji,则获取Emoji的Location和Length,若可以追加在textField.text中,则调用replaceCharactersInRangewithString方法进行替换,最后设定光标位置setSelectedTextRange。
核心代码:
/**
* 输入框内容监听方法
*
* 输入框内容监听方法
*
* @return 是否许可输入内容 返回值:YES:许可 NO:输入禁止
*/
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
// 删除按钮点击
if ([string isEqualToString:@""]) {
return YES;
}
// 最大可输入字符数
NSInteger maxTextCount = 10;
NSMutableAttributedString *textFieldString = [[NSMutableAttributedString alloc] initWithString:textField.text];
// 剩余可输入字符数
NSInteger remainTextCount = maxTextCount - textFieldString.length + range.length;
if (remainTextCount < 0) {
remainTextCount = 0;
}
// 输入内容中包含Emoji
if ([self stringContainsEmoji:string]) {
// 输入内容的长度在可输入字符数的范围内
if (string.length <= remainTextCount) {
[textFieldString replaceCharactersInRange:range withString:string];
textField.text = textFieldString.string;
// 开始位置
UITextPosition *beginning = textField.beginningOfDocument;
// 光标开始位置
UITextPosition *startPosition = [textField positionFromPosition:beginning offset:range.location + string.length];
// 光标结束位置
UITextPosition *endPosition = [textField positionFromPosition:beginning offset:range.location + string.length];
// 光标选择区域
UITextRange *selectionRange = [textField textRangeFromPosition:startPosition toPosition:endPosition];
[textField setSelectedTextRange:selectionRange];
}
// 输入内容的长度超过可输入字符数的范围内
else {
// 获取字符串实际截取Index
[self stringContainsEmoji:string maxCount:remainTextCount resultBlock:^(NSInteger toIndex) {
NSString *changeString = [string substringToIndex:toIndex];
[textFieldString replaceCharactersInRange:range withString:changeString];
textField.text = textFieldString.string;
UITextPosition *beginning = textField.beginningOfDocument;
UITextPosition *startPosition = [textField positionFromPosition:beginning offset:range.location + changeString.length];
UITextPosition *endPosition = [textField positionFromPosition:beginning offset:range.location + changeString.length];
UITextRange *selectionRange = [textField textRangeFromPosition:startPosition toPosition:endPosition];
[textField setSelectedTextRange:selectionRange];
}];
}
return NO;
}
// 输入内容中无Emoji 并且 输入内容的长度超过可输入字符数的范围内
else if (string.length > remainTextCount) {
NSString *changeString = [string substringToIndex:remainTextCount];
[textFieldString replaceCharactersInRange:range withString:changeString];
textField.text = textFieldString.string;
UITextPosition *beginning = textField.beginningOfDocument;
UITextPosition *startPosition = [textField positionFromPosition:beginning offset:range.location + changeString.length];
UITextPosition *endPosition = [textField positionFromPosition:beginning offset:range.location + changeString.length];
UITextRange *selectionRange = [textField textRangeFromPosition:startPosition toPosition:endPosition];
[textField setSelectedTextRange:selectionRange];
return NO;
}
return YES;
}
/**
* 输入字符串中是否包含Emoji
*
* @return 是否存在Emoji 返回值:YES:存在 NO:不存在
*/
- (BOOL)stringContainsEmoji:(NSString *)string {
__block BOOL returnValue = NO;
[string enumerateSubstringsInRange:NSMakeRange(0, [string length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
const unichar high = [substring characterAtIndex: 0];
// Surrogate pair (U+1D000-1F9FF)
if (0xD800 <= high && high <= 0xDBFF) {
const unichar low = [substring characterAtIndex: 1];
const int codepoint = ((high - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
if (0x1D000 <= codepoint && codepoint <= 0x1F9FF){
returnValue = YES;
}
// Not surrogate pair (U+2100-27BF)
} else {
if (0x2100 <= high && high <= 0x27BF){
returnValue = YES;
}
}
}];
return returnValue;
}
/**
* 获取字符串的最大输入Index
*
* @param[in] string 输入字符串
* @param[in] maxCount 最大输入字符数
* @param[out] block 结果Block
*/
- (void)stringContainsEmoji:(NSString *)string maxCount:(NSInteger)maxCount resultBlock:(void (^)(NSInteger))block {
__block BOOL returnValue = NO;
__block NSMutableArray *emojiRangeArr = [NSMutableArray array];
[string enumerateSubstringsInRange:NSMakeRange(0, [string length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
const unichar high = [substring characterAtIndex: 0];
// Surrogate pair (U+1D000-1F9FF)
if (0xD800 <= high && high <= 0xDBFF) {
const unichar low = [substring characterAtIndex: 1];
const int codepoint = ((high - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
if (0x1D000 <= codepoint && codepoint <= 0x1F9FF){
returnValue = YES;
[emojiRangeArr addObject:@[@(substringRange.location), @(substringRange.length)]];
}
// Not surrogate pair (U+2100-27BF)
} else {
if (0x2100 <= high && high <= 0x27BF){
returnValue = YES;
[emojiRangeArr addObject:@[@(substringRange.location), @(substringRange.length)]];
}
}
}];
NSInteger returnToIndex = maxCount;
if (returnValue) {
for (NSArray *subArr in emojiRangeArr) {
NSUInteger location = [[subArr firstObject] integerValue];
NSUInteger length = [[subArr lastObject] integerValue];
if (location < maxCount) {
if (location + length > maxCount) {
returnToIndex = location;
}
}
}
}
block(returnToIndex);
}
写在前面:
代码还可优化,欢迎指正。
网友评论