iOS 小坑记录:UITextView.attributedText

今天又接了个活,给 UITextView 加上 @username 格式的识别和点击处理,结果当时就踩了个小坑,一小时的活硬是干成一上午。

我的思路是这样:继承 UITextView ,给他加一个扫描 @username 并标记链接的接口。扫描 @username 用正则表达式,注意设置好零宽断言不然 xxx@gmail.com 会被扫进去,然后给 UITextView 的 attributedText 加特效,最后在 UITextViewDelegate::shouldInteractWithURL 里面做点击事件的处理。

写好一测试就发现了问题。这个控件是放在一个 UITableView 里面用,刚打开时是好的,上下滑一滑就坏了,链接的样式漫延到了整个文本框。比如显示的是 something @someone something 这样一行字,动手滑一滑,前后的 something 也会变成链接,样式传染了。

由于是在 UITableView 滑动时出现的,首先怀疑是复用的问题,于是在 [UITableViewCell prepareForReuse] 里面设置 textView.text = "",但是不好使,还是不行。打个断点读出 UITextView 的 attributedText 值,发现样式确实扩散到整个文本了。把所有写 attributedText 的地方都检查了,也没有收获。

无奈之下只好釜底抽薪,在 [UITextView setAttributedText:] 上打符号断点,看看到底是谁在写样式。顺便给断点加一个 Action: p $x2,把后面那个参数打出来。

然后就比较枯燥了,每断一次看一下打出来的东西,有没有乱写的情况。终于发现了:

看底下打出来的东西,整个字符串都是链接的样式。顺着调用栈往上找,一直找到这里:

所以结论就是,修改 UITextView 的 text 属性时,会把 attributedText 属性里面的样式直接拿来并丢掉 Range 信息,然后把样式应用到整体。于是在 prepareForReuse() 里面把文本框的样式清除掉,问题解决。