UITextView 模拟标签功能

UITextView 模拟标签功能

需求需要实现一个 类似于微博文本中 标签的能力, 一般我们用 YYTextView 配合 YYTextParser 很容易可以写出来, 参考, 但是当我们必需需要用 UITextView 来实现的时候, 就很麻烦了, 因为 YYTextView 不是继承的 YYTextView, 他们是兄弟关系, 强行替换需要修改所有引用该文本的逻辑, 风险较大.
怎么基于 UItextView 实现一套标签能力呢?

UItextView 实现标签需要哪些能力

  1. 文本高亮, 使用 UITextViewattributedText 实现即可

  2. 删除到标签的时候, 第一次自动选中标签, 第二次整体删除标签删除整个标签

  3. 长按系统键盘空格键, 能拖动标签, 光标需要跳过标签, 不能定位到标签内部光标移动 不能移到标签文字内部

  4. 长按文本多选的时候, 光标也要能跳过标签多选时候包含整个标签

  5. 点击标签跳转, 参考 就好, 本次 demo 没实现.

实现

demo
源码 UITextView+WYTagUtils.m

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#import "UITextView+WYTagUtils.h"


UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(16, 100, WY_SCREEN_WIDTH - 32, 200)];
textView.layer.borderColor = UIColor.blueColor.CGColor;
textView.layer.borderWidth = 1;
textView.delegate = self;
textView.returnKeyType = UIReturnKeySend;
textView.enablesReturnKeyAutomatically = YES;
textView.scrollsToTop = NO;
// 指定标签高亮样式
textView.highlightAttrDic = @{
NSForegroundColorAttributeName : [UIColor greenColor],
NSFontAttributeName : [UIFont systemFontOfSize:16],
};
// 指定普通文本样式
textView.normalAttrDic = @{
NSForegroundColorAttributeName : UIColor.grayColor,
NSFontAttributeName : [UIFont systemFontOfSize:16],
};
[self.view addSubview:textView];

按需设置标签内容

1
2
3
4
5
for (NSString *text in @[@"@周杰伦", @"#每日推荐"])
{
// 设置标签内容
[textView tu_appendTagString:WYSAFE_CAST(text, NSString)];
}

主动设置文本内容

1
2
3
textView.text = @"一起听新歌吧 @周杰伦 最伟大的作品 @周杰伦的歌";
// 刷新高亮
[textView tu_refreshText];

还需要实现几个 UITextView 的代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)textViewDidChangeSelection:(UITextView *)textView
{
// 调整光标的位置
[textView tu_adjustCursorPosition];
}
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
// 遇到标签 不能编辑, 直接选中准备删除
if (NO == [textView tu_shouldChangeTextInRange:range replacementText:text])
{
return NO;
}
return YES;
}

- (void)textViewDidChange:(UITextView *)textView
{
// 刷新富文本
[textView tu_refreshText];
}

遇到的坑

中文输入法不能直接刷新 UITextView 的富文本, 第一个输入的字母会被识别成英文, 从第二个输入的字母才算, 十分影响用户体验.

只有系统的中文输入法有问题, 系统中文输入法还在输入, 文本区域实际已经出现了字母”zhou”, 并且有一个选中态, 但是对于第三方中文输入法, 一般在输入框不会提前输入字母, 也就没有这个问题.

解决办法是在输入的时候判定 markedTextRange, 如果有就不替换富文本

1
2
3
4
5
6
7
8
9
10
- (void)tu_refreshText
{
UITextRange *markedRange = [self markedTextRange];
UITextPosition *position = [self positionFromPosition:markedRange.start offset:0];
if (position) // 有中文候选词(系统默认中文输入法才有), 不能刷新富文本
{
return ;
}
// ...高亮逻辑
}
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道