富文本
图文混排
图文混排的几种方案
* coreText, 非常蛋疼, 纯 C
* NSAttributeString, 属性文本, 付文本, 里面提供了很多设置文字各种属性的接口, 使用简单
* TextKit, 使用简单, 封装好, 好用
NSAttributeString: 属性文本
* 对文字操作, 只需要将对应的属性设置给对应的文字(一般通过 range)
* 创建可变属性文本, 之后就可以对这文本设置一些属性
* 看接口有一些常用的属性: 颜色, 大小, 背景色, 下划线,
通过附件简单添加图片:
* 通过 attachment 属性给属性文本添加附件, 附件可以显示出来, 和文本排在一起
* attach.Image 可以设置图片.
* 通过 attach.bounds 可以设置附件大小, 要和文字一样就设成文字大小即可
UIWebView 显示图文混排
利用 HTML 的标签来给 HTML 文本设置属性
* HTML 本来就是标签语言, 通过标签元素来给其中的内容设置属性,
* 但是 webView 有缺点, 会有内存泄露, 但是通过这个排版代码量少, 不过需要知道 html 语言, CSS, 等网页相关的知识,设计连接简单
富文本的简单应用
* 发微博控制器的标题, 换行, 大小不一样,
直接用不好用, 可以设置 titleView = Label, 在用 Label 设置富文本
* 将表情点击后添加到文本框,
当表情点击后通过通知传递给控制器, 拿到 emotion 模型创建好 image, 创建 attach -> attributeString -> 添加到文本框拼接
最好在从 textView 抽取一个子类, 将传入 emotion 后拼接上去的方法封装起来.
文本和富文本的 font 控制方式不同, 要统一起来
对文字输入的判断, 代码直接设置的不会有通知调用, 需要在设置表情时自己调用, 并检测富文本的长度, 它包括的范围广, TextView 中的都包括
在拼接表情时, 还需要判断是 emoji 还是图片, 处理方式不同,
取出当前富文本, 拼接表情, 设置回 TextView 的富文本
* 表情中的删除功能
textView 自带有 deleteBackspace, 调用删除功能删除
* 插入表情到文字中间
需要获取光标, 虽然有 insertText: 但是只能插入 Text, 不是富文本. 但是我们发送给新浪的数据又是字符串的, 所以我们应该是插入文本, 解析字符串中的 chs, 来显示成图片,
先拿到当前的文本, 要通过正则表达式来匹配出其中的 chs, 不然用 range 搜范围会很麻烦
正则表达式:
> 有一定的字符串表达式, 根据规律来才能匹配一定格式的字符串
> 作用: 匹配
> 创建正则表达式 -> 设定匹配规则 -> 拿去匹配字符串 -> 返回数组, 数组.count 有值就是成功, 失败会=nil
系统自带的正则方法封装用用, 但是想抽出匹配的字段会比较麻烦, 推荐用老外的第三方.需要导入 libicu,,,,
封装了很多简单调用的方法, 可以容易获取到匹配的字符串, 还能获取根据匹配规则来分割的字符串
attributeString 有获取当前光标选中范围的方法, 还可以插入到某个范围, 完了后把富文本传回给 textVIew 的富文本属性
发送微博时发送的是字符串, 不是附件图片, 所以应该把文本框中的富文本中的附件图片转成对应的描述文字, 再发送
* 用富文本自带的方法遍历其自身中的每个区域, 会用字典传出, 并且带有范围,
* 用一个普通可变字符串拼接其所有转换的字符串返回
* 取出附件字典对应的值, 空则为普通字符串, 直接拼接, 非空就取出附件, (我们重写附件让其带有 emotion 属性) , 拿到 emotion.chs 拼接到字符串
* 或者这个 textView 再设置一个 text 属性, 没词点表情时在这个里面保持纯文本
显示表情在其他 Label 中, 即将里面的表情描述转换成富文本显示出来
* 这么弄的话显示出来的肯定是富文本, 那我们直接展示富文本到 Label 或其他地方上,
* 可以在模型中添加一个富文本属性, 当 cell 拿到模型直接设置富文本上去.
* 当转换到模型时设置 setText方法, 里面将纯文本转换为富文本.
解析文本为富文本
* 用正则遍历匹配字符, 再遍历正则分割的字符串,这样就能拿到原文本中整个文本分割后的纯文本和表情描述.
* 拿到文本的直接拼接进富文本, 插入到rang.loc 位置,长度是 loc+len, 表情的话找到对应的表情,拿到 attach,插入 rang.loc,len=1, 但这个应该有一个问题, 当插入的位置是当前 range 中不存在的时,可能会有数组越界错误
* 或者,保险一点的 将匹配遍历出来的文本和图片描述都保存到一个对象模型 result 中, 在通过对数组进行位置判断的排序, 得出元素按照位置重写排列好的数组, 再遍历数组,是文本的话就拼接富文本, 图片的话要遍历 emotions 模型,看看是那个图片的描述,取出对应的模型,拿到图片弄给 attach
显示@...#...#等关键字高亮
* 这些关键字必然存在存文本里, 所以在之前将 string 分割后, 得到的数组中遍历里面result.string, 当每个元素 string 中存在正则匹配时,转成的富文本中将对应 range 的文字设置属性为蓝色, 或者好像可以设置连接属性
* 其他的关键字匹配也是用这种方式
* 注意: 富文本的文字大小哟给你同样的 font计算出来可能 和纯文本的不太一样, 导致显示在用 font 计算出来的 label 里显示不全
bug: 当收到的 emoji 中有不能识别的 emoji 时, 同时还带有中文紧贴着,如😍✌🏻️✨在 xcode 中和在这里显示不了那个未识别的字符,隐藏了, 调用 boude…计算 rect 时,会报错访问坏内存. 和中文隔空格,或在未识别的 emoji 前或后隔空格,可以正确通过, 本身打印没问题,主要是调用方法上有问题
估计是在 xcode 的编码中的设置问题. 或者是NSString 在转码的时侯和调用 bounds…的时候有点问题
小问题: 长按最近表情, 会有隐藏的表情弹出来: —- 长按后的判断条件加一个非隐藏才做反应
处理关键字点击事件
* 自定义控件来显示文字并处理关键字的点击事件,
* 将显示文本的 textView 作为控件的属性, 里面提供了一个方法来获得某个 range 的范围
* 封装在控件里而不是继承可以不将 textView 的接口暴露出来, 不让别人对其设置
* 尝试下有没有属性可以设置 textView 的内切设置为毫无内切, 即便是0000也没有, 注意是 textView 的 containerInset, 不是 UIView 的 ContentInset
* 设置 textView.userInteracte = NO; 但是可能会有显示不了某些控件属性的问题,需要设置其他的禁止滚动和禁止编辑才可以减少这种情况的发生
* 实现这个自定义控件的触摸方法, 拿到当前触摸点, 判断触摸点与关键字所在的范围是不是相交, 相交的话就高亮并跳转
* 拿到当前点击 View 中关键字所在范围:
> 因为 TextRange 是抽象类, 不能直接用, 我们可以先选中某 range 文字 -> textView.selectedTextRange拿到选中文字的 textRange -> selectedRect...TextRange拿到选中文字的 rect -> 看看是否相交, (有时候会计算出宽 or 高为空的情况, 这种要进行排除)
> 要这样, 首先要知道那些需要选中的文字: 关键字, 拿到关键字:在正则匹配的时候, 多设置一个属性(可以自定义key)来保存关键字的位置和文字, 之后拿到这个属性的时候, 对应的会有这个属性设置文字的 range, 拿到 range 就可以进行上面的步奏
* 设置一个数组属性, 将当前控件中属性文本中的所有链接保存起来, 不要重复遍历寻找, 链接对象要在这里算出 rect, 不能在 status 模型中国年算, 模型中不能根据当前 label 控件算出对应的 rect 值. 在重新设置了属性文本后, 要把数组 nil, 因为里面的链接已经不一样了
* 当点击时, 遍历数组, 看看有没有和其中连接中的 rect 相交的, 找到选中链接 -> 将选中链接中遍历 rect 设置上背景
* 在结束点击抬起手的时候移除背景: 在 touch end 方法中遍历数组, 将其中的所有 rect 设置空背景,或者将选中连接中的 rect 清空或者将设置的背景保存起来, 还可以将背景 view 设置 tag, 移除时遍历 self.subViews 找 tag 来移除,结束点击的时候移除. 触摸取消时也要移除
* 当触摸 end 时, 判断触摸是否还在链接上, 是的话就发送通知告诉外界链接被点击了, 让控制器好做事.
* 控制器监听通知, 链接被点击时, 判断链接是 http 的话就用应用打开直接打开,会跳转到 Safari, 不是的话就用可能是跳转控制器, 以后处理
* 话题和@的链接可以用新浪的接口, 通过控制器展示.
发现和我的事件处理
* 因很多个控制器的界面差不多, 仅有几点控件和数据不同, 可以做一个父控制器, 共同拥有的元素放父控制器里, 再子类化出具体功能的控制器,
* 数据模型也要进行子类化, 父模型决定一样的东西, 子类模型决定模型的类型和特有的东西, 分类处理可以功能清晰, 逻辑清楚
* 模型分类使用子类化, 辅助视图判断模型的类型来做区分显示不同的控件
* 数量显示: 当优先判断 cell 模型的 bageValue, 有就显示, 没有再做类型的判断
* 最后一个退出标红的比较特别, 可以直接用 footerView 放个按钮.
* cell 的背景设置: 一般来说应该提供4种背景, 有四种不同情况的 cell, 第一行, 最后一行, 中间的那些行, 仅有一行
* item模型中的属性可以设置一个类属性, 即这个item要跳转的类
* 还可以设置一个block的属性, 即这个item点击候要进行的操作
bug: url 的匹配不正确, 当 url 后面跟着字词之后不会隔开, 会被误视为 URL 的一部分— 找另一个正则来替代
#bug: 换了 TYAttributed 后, 在显示 headerView 正常, 但是显示cell 的时候会不显示文字, 且cell 的计算高度会递增
目前根据一些测试猜测: 计算高度的时候用的是同一个 cell, 里面的控件可能对高度进行了缓存, 导致没次算的时候都会有上面一些的影响
进一步发现, 还是自己的代码写的不够严谨, 在封装好的 Label 中, 对内部 TYAttributed 的使用是 append 富文本, 如果一致使用同一个, 就会一直拼接上去, 最后 attributedString 会越来越长, 造成计算的高度越来越大.
注意自定义控件的时候会有复用的可能
#bug: 单独抽出这个 cell 及 YCAttributedLabel 来测试时, 产生一样不识别, 且都挤在第一行上.
#bug: 想在异步线程完成用自适应的方法计算出高度时,
2016-10-18 17:20:33.133 tableViewtest[40456:2268670] This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
不然异步线程进行自动布局的相关操作