文章标题

重构UITextView遇到一些问题总结

之前在项目中需要做一个嵌入图片的长文本,要求图片和文本一起滚动。这样的需求很常见,比如网易新闻里带图片的新闻。当时的第一想法就是UIImageView+UITextView放在UIScrollView上,UITextView设置为不滚动,根据文本的内容将UITextView的frame.height设置为contentSize.height。
这是一个很简单粗暴的做法,带来的问题就是内存过大,特别是当文本很长的时候。

后来App开始不支持iOS6,于是TextKit来拯救我了。TextKit真的是很强大的一个东西。先插入一张图片看看。
这里写图片描述
下面简单说说TextKit里几个重要的组成部分。
1.NSTextStorage(文本存储)
NSTextStorage是中心对象,它知道所有的文本和属性,相当于MVC中的M模型。NSTextStorage继承自NSMutableAttributedString。所以,文本存储——从文本系统看来——仅仅是一个带有属性的字符串,以及几个扩展。这两者唯一的重大不同点是文本存储包含了一个方法来发送内容改变的通知。

2.NSLayoutManager(布局管理器)

1、布局管理器监听文本存储中文本或属性改变的通知,一旦接收到通知就触发布局进程。
2、从文本存储提供的文本开始,它将所有的字符翻译为字形(Glyph)。
3、一旦字形全部生成,这个管理器向它的NSTextContainer(文本容器)(们)查询文本可用以绘制的区域。
4、然后这些区域被行逐步填充,而行又被字形逐步填充。一旦一行填充完毕,下一行开始填充。
5、对于每一行,布局管理器必须考虑断行行为(放不下的单词必须移到下一行)、连字符、内联的图像附件等等。
6、当布局完成,Layout Manager 将前面几步排版好的文本设给 Text View。

3.NSTextContainer(文本容器)
每个UITextView(文本视图)定义了一个文本可以绘制的区域。为此,每个文本视图都有一个文本容器,它精确地描述了这个可用的区域。

4.UITextView(文本视图)
在 TextKit 中,文本视图有两个功能:
1.它是文本系统用来绘制的视图。文本视图它自己并不会做任何绘制;它仅仅提供一个供其它类绘制的区域。
2.作为视图层级中唯一的组件,第二个功能是处理所有的用户交互。具体来说,文本视图实现 UITextInput 的协议来处理键盘事件,它为用户提供了一种途径来设置一个插入点或选择文本。它并不对文本做任何实际上的改变,仅仅将这些改变请求转发给NSTextStorage(文本存储)。

5.CoreText
并没有直接包含在 TextKit 中,CoreText 是进行实际排版的库。对于布局管理器的每一步,CoreText 被以这样或那样的方式调用。它提供了从字符到字形的翻译,用它们来填充行,以及建议断字点。

最最重要的一点,那就是图文混排。本人才疏学浅,目前找到两种用TextKit做图文混排的方式。

1.附件方式
附件方式就是将图片设为NSTextAttachment,然后 [storage insertAttributedString:[NSAttributedString attributedStringWithAttachment:attachment] atIndex:index]插入到文本存储中。
2.设置exclusionPaths
exclusionPaths是NSTextContainer提供的一个属性,它允许开发者设置一个 NSBezierPath 数组来指定不可填充文本的区域,而这个区域就可以用来放置图片。

两种方式各有优劣,采用exclusionPaths可以动态的设置图片位置,拖动图片实时布局,采用附件方式默认实现长按保存本地,而后者正是项目需求的。

如此一来,遇到再长的文本也不用担心内存吃紧了,因为TextKit中对内容的加载是分块儿的,并不是一次性全部加载,感觉有点类似与UITableView 的复用机制。到此还不失为一次完美的重构。

本来问题到这儿就该结束了,可是后来产品加了需求,要实现对文字的可点击翻译。
大致实现是通过获取用户点击的CGPoint得到当前点击的文字,以当前点击的文字为中心建一个透明View覆在上面,并以此View为基点PopOver一个view来显示释义。

在添加透明View的时候会调起autoLayout,因为之前的UITextView是从xib引进来的,导致的问题是每次点击翻译之后,UITextView都会滚动到最前面。具体原因是UITextView分块儿加载内容,layout之后NSTextStorage的第一块儿加载。

最后的解决方案是,根据内容建立好完整的NSTextStorage之后, 手动生成UITextView。
self.textView = [[UITextView alloc] initWithFrame:textViewFrame
textContainer:textContainer];

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值