iOS TTTAttributedLabel 的三个坑

TTTAttributedLabel 真的是个非常常用的第三方库了,很多 app 都用它来给文本加点击事件,比如一段文字中带有能点击的 tag。

iOS TTTAttributedLabel 的三个坑_第1张图片
图为 Keep。图中的绿色标签很有可能是 TTTAttributedLabel 做的。

但是这个库比较老了,虽然貌似一直还有人维护,还是有一些比较坑的地方;而且里面主要用的 CoreText,也存在一些 CoreText 的问题。我的项目从比较早期就一直在用这个库,也踩了一些坑。下面分享比较重大的 3 个坑,供大家参考。

用 text,忘掉 attributedText

给文本加点击功能的同时,我们往往需要改变可点击的文本样式,比如图中用了绿色。要改变文本样式,第一反应可能是用attributedText,设为一个attributedString。不幸的是,由于TTTAttributedLabel作者在创作这个库的时候,attributedText这个属性还没有出生,因此这个库在这方面支持得不太好。

我曾经遇到一个问题,就是用attributedText改过的文本属性在显示中没体现出来。查了之后发现,针对点击区域的文本样式改动最好使用setLinkAttributes:setActiveLinkAttributes:两个 API。形如:

[self.attributedLabel setLinkAttributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:14], NSForegroundColorAttributeName:[UIColor colorWithHex:0x4b4b4b alpha:1.0]}];

而针对全体文本的设置,比如段落格式、行距等,目前用attributedString没有发现什么大问题。但有一次我发现,cell 在重用的时候,之前设的 link 都没有清除。尽管样式上看上去不是 link 的样式,但手指能点,点上去变成了 activeLink 的样式。来回滚动重用次数多了,label 上到处都能点。怎么回事呢?

上 github 查 issues,发现一个神奇的设定:更改TTTAttributedLabeltext属性,不能用:

self.attributedLabel.attributedText = attributedString;

而要用:

self.attributedLabel.text = attributedString;

即使你传入的是带格式的NSAttributedString,而不是普通的NSString~ 否则它不能正常清除之前设置的 link。尽管作者声称在 2013 年就解决了这个问题,然而似乎并没有,还是需要这样写。奇妙吧,没想到吧,违法直觉吧。这是第一个坑。

点击区域过大

第一次用的时候,我还担心会不会 label 上的点击区域过小,用户点不上。事实证明我多虑了。设置了 link 之后,不仅点那排字完全能触发点击事件,甚至点到它下一行还是能触发…… 有时候 link 比较长,一行整体加了 link,结果把整个 cell 都祸害了。

后来怎么改的呢?一番寻找,我在源码里找到了一个- (void)commonInit方法,里面有一行_extendsLinkTouchArea = YES; 是个扩大点击区域的属性,而且默认值为YES。它改成NO,问题就解决了。这是我遇到的第二个坑。

emoji 导致 crash

这并不是TTTAttributedLabel本身的问题,而是 CoreText 的问题。就是我发现多行 label,设定numberOfLines既不为 0 也不为 1 的,文本里出现 emoji 等 unicode 字符的时候会内存暴涨,非常夸张,能一直涨到几百M,直至 crash。如果设了linkAttributes才有这问题,不设没事。

而且这个 crash 并不停 exception 断点,都不知道是哪里出的问题。用了 instrument 里的 allocation 工具,发现是TTTAttributedLabel- (CGSize)sizeThatFits:(CGSize)size方法,一到里面的CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints这个 C 函数就死。 再往里跟,发现是 CoreText 的CTFramesetterCreateFrame这个函数出的问题。首次调用这个方法的时候 width 都是 -1,其他参数也都一样,但只要文本里带 emoji 就会出问题。

看起来是个 CoreText 的 bug,而且貌似是个 iOS 9 新出的 bug,具体原因未知。好在解决方法并不复杂,只要把 label 的 preferredMaxLayoutWidth设成非 0 的数就能消除这个 crash 了。当然为了正确显示,还是要设成正确的值,即文本实际显示的宽度。

另外有人发现了另一个 emoji 导致 crash 的问题,是在设置 link 的 range 时如果 range 的两头正好切在 Unicode 字符的中间就会导致 crash,表现跟我遇到的问题差不多,都是内存暴涨。解决方法是正确计算 Unicode 字符的长度。这个 issue 没有解决我的问题,不过可能有其他人会遇到,所以在此一并列出。

以上就是我使用TTTAttributedLabel这个库时曾经遇到的一些问题。瑕不掩瑜,这个库还是很好用的,至少免去了写 CoreText 那一堆语法的麻烦。有其他问题可以上 github 提 issue,貌似会有人理。祝大家使用愉快。

我参考了:

setAttributedText not reseting links
UILabel Link Hit Detection too large for small labels.
iOS 9.0.2 and iOS 9.1 Crash

你可能感兴趣的:(iOS TTTAttributedLabel 的三个坑)