因为项目中有类似微博的话题和@功能,所以我们来说说类似于新浪微博话题功能的实现,当文字是”#话题#”这种格式时,该文字字体的颜色得变成蓝色。刚拿到这个内容猜测的时候应该用 UITextView 或 UITextField 去做,了解了思路就去百度了下,发现真的很少有这样的案例,得知 YYKIt 大神的 demo 中有这样的 demo ,于是就去下载看看, 这个 YYkit 的链接GitHub - ibireme/YYKit: A collection of iOS components.
这个 demo 中有发布微博界面.
于是想偷懒把整个 copy 过来一下,不用重复造轮子
当我以为能用的时候我发现,这个删除是默认不能整体删除的, YY 大神都是用正则表达式去匹配内容,然后进行绑定和变色
于是我就想在这个基础上看能不能改整体删除,改了半天还是不行,就在github 上搜索, 找了好久都是标题党,基本上都是模仿界面,根本没有实质性的内容,于是就在谷歌上面搜索,找了好久还是不多
看了一下,发现新浪微博#话题#和@功能做的并不好,跟上面的情况一样,仔细发现,新浪微博#话题#和@功能虽然能变声并不能整体删除,这个我是测试过的,删除#再输入话题会出现正则匹配不正确的现象,@某人删除文字时候点击某人会出现查无此人显现, 不知道新浪微博测试和开放人员知不知道,YY 大神的 Demo 也是如如此,今天头条的发布是整体删除,给人感觉很好
于是就谷歌,最后终于找了一篇,
具体实现
在实现过程中,我以AttributedString的颜色值为基准,用几个正则为查找工具,结合UITextView的三个代理方法。
/// Prior to replacing text, this method is called to give your delegate a chance to accept or reject the edits. If you do not implement this method, the return value defaults to YES.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
/// The text view calls this method in response to user-initiated changes to the text. This method is not called in response to programmatically initiated changes. Implementation of this method is optional.
- (void)textViewDidChange:(UITextView *)textView;
/// Implementation of this method is optional. You can use the selectedRange property of the text view to get the new selection.
- (void)textViewDidChangeSelection:(UITextView *)textView;
这三个代理方法
shouldChangeTextInRange 代理方法中,第一,实现第一次选中,第二次删除功能;第二,实现插入话题后,需要改变其他字符串的初始颜色,得在这个方法里面做个标志。
textViewDidChange 代理方法中,实现 根据 shouldChangeTextInRange 方法中所得到的标志,设置字符串的初始颜色;
textViewDidChangeSelection 代理方法中,实现让光标不能移动到话题里面;
首先我定义了两个变量,插入了话题以后,继续在后面输入字符的话,字符颜色就跟话题颜色一样了。所以,我得用这两个变量来实现改变输入字符的初始颜色。
/// 改变Range
@property (assign, nonatomic) NSRange changeRange;
/// 是否改变
@property (assign, nonatomic) BOOL isChanged;
哦,对了,我还得用一个变量来记录上次光标所在的位置,因为话题字符串是不让它输入的。
/// 光标位置
@property (assign, nonatomic) NSInteger cursorLocation;
用户从其他界面选择好话题以后,它得插入到textview中啊:
NSString *insertText = [NSString stringWithFormat:@"#%@#", dict[KeyTopicName]];
[self.textView insertText:insertText];
NSMutableAttributedString *tmpAString = [[NSMutableAttributedString alloc] initWithAttributedString:self.textView.attributedText];
[tmpAString setAttributes:@{ NSForegroundColorAttributeName: TopicColor, NSFontAttributeName: DefaultSizeFont } range:NSMakeRange(self.textView.selectedRange.location - insertText.length, insertText.length)];
self.textView.attributedText = tmpAString;
然后我还得找到将用户所选择插入的话题位置啊。
/**
* 得到话题Range数组
*
* @return return value description
*/
- (NSArray *)getTopicRangeArray:(NSAttributedString *)attributedString {
NSAttributedString *traveAStr = attributedString ?: _textView.attributedText;
__block NSMutableArray *rangeArray = [NSMutableArray array];
static NSRegularExpression *iExpression;
iExpression = iExpression ?: [NSRegularExpression regularExpressionWithPattern:@"#(.*?)#" options:0 error:NULL];
[iExpression enumerateMatchesInString:traveAStr.string
options:0
range:NSMakeRange(0, traveAStr.string.length)
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange resultRange = result.range;
NSDictionary *attributedDict = [traveAStr attributesAtIndex:resultRange.location effectiveRange:&resultRange];
if ([attributedDict[NSForegroundColorAttributeName] isEqual:TopicColor]) {
[rangeArray addObject:NSStringFromRange(result.range)];
}
}];
return rangeArray;
}
那么,三个UITextView delegate方法里的代码就可以这么玩了:
#pragma mark - UITextView Delegate
- (void)textViewDidChangeSelection:(UITextView *)textView {
NSArray *rangeArray = [self getTopicRangeArray:nil];
BOOL inRange = NO;
for (NSInteger i = 0; i < rangeArray.count; i++) {
NSRange range = NSRangeFromString(rangeArray[i]);
if (textView.selectedRange.location > range.location && textView.selectedRange.location < range.location + range.length) {
inRange = YES;
break;
}
}
if (inRange) {
textView.selectedRange = NSMakeRange(self.cursorLocation, textView.selectedRange.length);
return;
}
self.cursorLocation = textView.selectedRange.location;
}
- (void)textViewDidChange:(UITextView *)textView {
if (_isChanged) {
NSMutableAttributedString *tmpAString = [[NSMutableAttributedString alloc] initWithAttributedString:self.textView.attributedText];
[tmpAString setAttributes:@{ NSForegroundColorAttributeName: [UIColor blackColor], NSFontAttributeName: DefaultSizeFont } range:_changeRange];
_textView.attributedText = tmpAString;
_isChanged = NO;
}
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if ([text isEqualToString:@""]) { // 删除
NSArray *rangeArray = [self getTopicRangeArray:nil];
for (NSInteger i = 0; i < rangeArray.count; i++) {
NSRange tmpRange = NSRangeFromString(rangeArray[i]);
if ((range.location + range.length) == (tmpRange.location + tmpRange.length)) {
if ([NSStringFromRange(tmpRange) isEqualToString:NSStringFromRange(textView.selectedRange)]) {
// 第二次点击删除按钮 删除
return YES;
} else {
// 第一次点击删除按钮 选中
textView.selectedRange = tmpRange;
return NO;
}
}
}
} else { // 增加
NSArray *rangeArray = [self getTopicRangeArray:nil];
if ([rangeArray count]) {
for (NSInteger i = 0; i < rangeArray.count; i++) {
NSRange tmpRange = NSRangeFromString(rangeArray[i]);
if ((range.location + range.length) == (tmpRange.location + tmpRange.length) || !range.location) {
_changeRange = NSMakeRange(range.location, text.length);
_isChanged = YES;
return YES;
}
}
} else {
// 话题在第一个删除后 重置text color
if (!range.location) {
_changeRange = NSMakeRange(range.location, text.length);
_isChanged = YES;
return YES;
}
}
}
return YES;
}
好吧,通过以上方法,基本输入、删除操作功能是实现了
最后是上传, 上传是本地先定义一个数组topicArray
然后在插入数据的时候往数组中插入一个对象
删除数据的时候移除 topicArray 中的对象
最后上传 textView的 text 内容 和数组中的对象
显示:
1:YYLabel 显示
2:MLEmojiLabel 显示
我是用了第二种MLEmojiLabel ,YYLabel 没有深入研究,性能比MLEmojiLabel要好,等不忙了再换,
因为我是上传了文本内容和对象给服务器,服务器给我类似的对象然后本地坐比对
,到此,爬坑算是结束了
代码只是提供思路,没有做封装,也没有深入的去优化,希望用到的小伙伴能优化并做的更好,谢谢
最后附上 demo https://github.com/986138497/UITextView-
网友评论