使用NSAttributedString富文本踩到的坑

故事是这样子的:现在有个列表,要实现搜索关键字高亮的效果,我们都知道,使用iOS的富文本可以很方便实现该效果。于是,我新建一个UITableViewCell,并在cell上添加一个UILabel,考虑到这个cell可能在项目中其他地方可以复用(在其他地方可能只是简单展示,不需要使用富文本),于是我这样子写:
【使用NSAttributedString富文本踩到的坑】在init方法中添加一个label并给基本属性font、textColor赋值:

_nameLabel = [[UILabel alloc]init]; _nameLabel.font = [UIFont systemFontOfSize:16]; _nameLabel.textColor = [UIColor blackColor]; [self.contentView addSubview:_nameLabel];

写一个刷新方法:
- (void)reloadWithContent:(NSString *)content keywords:(NSString *)keywords { NSDictionary *attributes = @{NSFontAttributeName:_nameLabel.font, NSForegroundColorAttributeName:_nameLabel.textColor, }; NSMutableAttributedString *attributedStr = [[NSMutableAttributedString alloc]initWithString:content attributes:attributes]; NSRange keyRange = [content rangeOfString:keywords]; if(keyRange.location != NSNotFound){ [attributedStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:keyRange]; [attributedStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:20] range:keyRange]; } _nameLabel.attributedText = attributedStr; }

这里可以看到,我为了方便在以后UI调整或需求变更的时候实现”一个属性的修改,只修改一个地方即可“,比如,如果某天UI要求修改默认字体颜色,我只需要在init方法中修改textColor属性即可。我这里在创建富文本的时候,给富文本设置默认的字体和颜色时,font和textColor直接从_nameLabel的属性中获取。
那么,请思考一下,我上面这种写法有什么毛病没有?为什么?
对富文本比较熟悉并且细心的你会发现这种写法会有问题,问题就在上面的偷懒写法那里,我们先看看demo的效果图:
使用NSAttributedString富文本踩到的坑
文章图片
iOS富文本.gif 你会发现,刚开始状态是对的,当你上下滑动的时候,cell进行复用了之后,cell的字体和颜色都变成高亮的状态了。这是为什么呢?当然是cell重用和设置富文本的问题。我们可以看到UILabel设置富文本的官方文档是这样说的:
This property is nil by default. Assigning a new value to this property also replaces the value of the text property with the same string data, although without any formatting information. In addition, assigning a new a value updates the values in the font, textColor, and other style-related properties so that they reflect the style information starting at location 0 in the attributed string.

看到这里,一切都明白了,在给UILabel的attributedText属性赋值的时候,系统会自动用富文本字符串起始0位置处的子字符串的样式来应用到整个UILabel的样式。也就是说设置attributedText之后,UILabel的font和textColor就已经被系统自动修改成0位置处字符的font和textColor了,所以才会导致上面看到的效果。
问题找到了,那么针对这种情况我们怎么处理呢?在这里就不能再偷懒了,要么在cell的prepareForReuse方法中再次给UILabel的默认状态赋上默认值,要么在新建富文本的时候不要直接从label的属性中获取,老老实实新建跟默认状态值一样的UIFont和UIColor。唯一麻烦的就是以后需求改动之后,要记得同时修改这几个地方了。
同样的原理,给UITextField、UITextView、UIButton等设置富文本时也需要注意这一点。设置富文本之后,font和textColor就会被富文本第一个字符的font和textColor覆盖掉。

    推荐阅读