使用系统自带的NSAttributedString来处理,对于一般的图文混排已经足够了,但是,有一个缺点就是NSAttributedString并不支持gif动画。实际上,使用gif动画还是挺卡的。

思路:

1.通过RegexKitLite 正则,匹配出所有需要特殊处理的字符

2.由于表情图片占用一个字符,使用直接替换范围的方式,会导致后面的表情范围不对。有两种处理方案

方案一:

  • 使用两个数组,分别装特殊字符(文字内容,文字范围,是否为特殊字符,是否为表情)和非特殊字符,按范围排序成一个新数组
  • 循环新数组List,通过判断是否为表情和是否为特殊字符来添加appendAttributedString属性。

方案二:

通过递归的方式,每找到一个表情,先替换掉,再递归找下一个

当前,使用的是方案一,核心代码:

/**
* 普通文字 --> 属性文字
*
* @param text 普通文字
*
* @return 属性文字
*/
- (NSAttributedString *)attributedTextWithText:(NSString *)text
{
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] init]; // 表情的规则
NSString *emotionPattern = @"\\[[0-9a-zA-Z\\u4e00-\\u9fa5]+\\]";
// @的规则
NSString *atPattern = @"@[0-9a-zA-Z\\u4e00-\\u9fa5-_]+";
// #话题#的规则
NSString *topicPattern = @"#[0-9a-zA-Z\\u4e00-\\u9fa5]+#";
// url链接的规则
NSString *urlPattern = @"\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^[:punct:]\\s]|/)))";
NSString *pattern = [NSString stringWithFormat:@"%@|%@|%@|%@", emotionPattern, atPattern, topicPattern, urlPattern]; // 遍历所有的特殊字符串
NSMutableArray *parts = [NSMutableArray array];
[text enumerateStringsMatchedByRegex:pattern usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
if ((*capturedRanges).length == ) return; TextPartModel *part = [[TextPartModel alloc] init];
part.special = YES;
part.text = *capturedStrings;
part.emotion = [part.text hasPrefix:@"["] && [part.text hasSuffix:@"]"];
part.range = *capturedRanges;
[parts addObject:part];
}]; // 遍历所有的非特殊字符
[text enumerateStringsSeparatedByRegex:pattern usingBlock:^(NSInteger captureCount, NSString *const __unsafe_unretained *capturedStrings, const NSRange *capturedRanges, volatile BOOL *const stop) {
if ((*capturedRanges).length == ) return; TextPartModel *part = [[TextPartModel alloc] init];
part.text = *capturedStrings;
part.range = *capturedRanges;
[parts addObject:part];
}]; // 排序
// 系统是按照从小 -> 大的顺序排列对象
[parts sortUsingComparator:^NSComparisonResult(TextPartModel *part1, TextPartModel *part2) {
// NSOrderedAscending = -1L, NSOrderedSame, NSOrderedDescending
// 返回NSOrderedSame:两个一样大
// NSOrderedAscending(升序):part2>part1
// NSOrderedDescending(降序):part1>part2
if (part1.range.location > part2.range.location) {
// part1>part2
// part1放后面, part2放前面
return NSOrderedDescending;
}
// part1<part2
// part1放前面, part2放后面
return NSOrderedAscending;
}]; UIFont *font = [UIFont systemFontOfSize:];
NSMutableArray *specials = [NSMutableArray array];
// 按顺序拼接每一段文字
for (TextPartModel *part in parts) {
// 等会需要拼接的子串
NSAttributedString *substr = nil;
if (part.isEmotion) { // 表情
NSTextAttachment *attch = [[NSTextAttachment alloc] init];
NSString *name = [EmoticonTool emoticonWithChs:part.text].png;
if (name) { // 能找到对应的图片
attch.bounds = CGRectMake(, -, font.lineHeight, font.lineHeight);
attch.image = [UIImage imageNamed:name];
substr = [NSAttributedString attributedStringWithAttachment:attch];
} else { // 表情图片不存在
substr = [[NSAttributedString alloc] initWithString:part.text];
}
} else if (part.special) { // 非表情的特殊文字
substr =[[NSAttributedString alloc] initWithString:part.text attributes:@{NSForegroundColorAttributeName : [UIColor redColor]
}];
SpecialModel *s = [[SpecialModel alloc] init];
s.text = part.text;
NSUInteger loc = attributedText.length;
NSUInteger len = part.text.length;
s.range = NSMakeRange(loc, len);
[specials addObject:s];
} else { // 非特殊文字
substr = [[NSAttributedString alloc] initWithString:part.text];
}
[attributedText appendAttributedString:substr];
} // 一定要设置字体,保证计算出来的尺寸是正确的
[attributedText addAttribute:NSFontAttributeName value:font range:NSMakeRange(, attributedText.length)];
[attributedText addAttribute:@"specials" value:specials range:NSMakeRange(, )]; return attributedText;
}

在使用过程中,我们需要注意一点,计算文本的宽高,通过下面来计算

 [status.attributedText boundingRectWithSize:CGSizeMake(maxW, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;

在计算过程中,我们并没有传字体大小,因而,我们需要给attributedText一开始设定字体大小:

// 一定要设置字体,保证计算出来的尺寸是正确的
[attributedText addAttribute:NSFontAttributeName value:font range:NSMakeRange(, attributedText.length)];

设置行高

    // 定义行高
NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineSpacing:];
[attributedText addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(, attributedText.length)];

附源代码:http://pan.baidu.com/s/1o8Su8H0

目录说明:

HomeViewController  ---列表

StatusCell       ---列表TableCell

StatusFrame       ---列表TableCell高度。由于cell的高度是不固定的,因此我们定义StatusFrame来管理所有的控件尺寸,最后返回总高度

StatusModel      ---微博模型,图文混排处理,在这里做核心混排处理,通过添加attributedText属性处理。

  --TextPartModel  --StatusModel嵌套属性,用于记录RegexKitLite 正则匹配出的字符

  --SpecialModel   --StatusModel嵌套属性,用于特殊实符点击变色的范围比比较

  --UserModel     --StatusModel嵌套属性,用户模型

iOS 图文混排的更多相关文章

  1. ios图文混排

    图文混排的形式 1. 富文本形式 2. core Text(文字排版) 3. TextKit 4. UIWebView 一.富文本 我们可以采用attributeString来进行图文混排.例如一个文 ...

  2. iOS 图文混排 链接 可点击

    对于这个话题 我想到 1 第一个解决方法就是使用 webView 比较经典 把所有复杂工作都交给控件本身去处理了,  但是好像好多需要自定义的地方 没法从 webView获得响应回调 :(估计也可以实 ...

  3. iOS图文混排的几种方式

    最近优化升级了之前做的一个项目,现在这一期已接近尾声了,今天可以腾出些时间总结一下最近项目中用的比较多的图片文字混排显示的内容.现在遇到比较多的图文混排的基本有三种:一种是在标签中显示 价格符号+价格 ...

  4. iOS 图文混排 (Swift版)

    // 0> 图片附件 let attachment = NSTextAttachment() attachment.image = #imageLiteral(resourceName: &qu ...

  5. 【iOS】使用CoreText实现图文混排

    iOS没有现成的支持图文混排的控件,而要用多个基础控件组合拼成图文混排这样复杂的排版,是件很苦逼的事情.对此的解决方案有使用CoreText进行绘制,或者使用TextKit.本文主要讲解对于CoreT ...

  6. 高性能图文混排框架,构架顺滑的iOS应用-b

    About GallopGallop是一个功能强大.性能优秀的图文混排框架. Features主要用于解决以下需求: 滚动列表的性能优化.Gallop使用异步绘制.视图层级合并.观察mainRunlo ...

  7. ios开发--图文混排(富文本)

    最近准备接一个编辑类的app,所以就查了下相关的功能,并自己试验了下: /** iOS 6之前:CoreText,纯C语言,极其蛋疼 iOS 6开始:NSAttributedString,简单易用 i ...

  8. IOS开发UI篇--一个支持图文混排的ActionSheet

    一.简单介绍 UIActionSheet是IOS提供给我们开发人员的底部弹出菜单控件.一般用于菜单选择.操作确认.删除确认等功能.IOS官方提供的下面方式对UIActionView进行实例化: - ( ...

  9. iOS火焰动画效果、图文混排框架、StackView效果、偏好设置、底部手势等源码

    iOS精选源码 高性能图文混排框架,构架顺滑的iOS应用. 使用OpenGLE覆盖阿尔法通道视频动画播放器视图. 可选最大日期截至当日日期的日期轮选器ChooseDatePicker 简单轻量的图片浏 ...

随机推荐

  1. 如何用Eclipse+maven创建servlet 3.0 web 项目

    用eclipse + maven, 选择 maven-archetype-webapp,默认的servlet版本是2.3. 目前servlet版本都已经是3.X. 那有什么办法可以创建servlet ...

  2. 基于cdh5.10.x hadoop版本的apache源码编译安装spark

    参考文档:http://spark.apache.org/docs/1.6.0/building-spark.html spark安装需要选择源码编译方式进行安装部署,cdh5.10.0提供默认的二进 ...

  3. book_lsit

    @ 学习go的轮廓.核心.难点  必看 <代码的未来> 3.2 Go ~page 123~ @ 学习内存架构.内存管理的入门.原理    必看 <操作系统之哲学原理>邹恒明 @ ...

  4. bc https://en.wikipedia.org/wiki/Gossip_protocol

    分布式容错性:分布式网络极其鲁棒,能够容忍部分节点的异常状态: 不可篡改性:一致提交后的数据会一直存在,不可被销毁或修改: 隐私保护性:密码学保证了数据隐私,即便数据泄露,也无法解析. 随之带来的业务 ...

  5. LeetCode 226 Invert Binary Tree 解题报告

    题目要求 Invert a binary tree. 题目分析及思路 给定一棵二叉树,要求每一层的结点逆序.可以使用递归的思想将左右子树互换. python代码 # Definition for a ...

  6. 《Linux 文本处理》- awk 分析 nginx 日志

    废话不多说,简单了解一下 awk - 强大的文本分析工具,也就是分析日志 最常用的就是分析日志了吧,做统计什么,这里也拿 nginx 日志来做分析 1:统计出现次数 - 比如状态码出现次数 - 先直接 ...

  7. pyhton 爬虫爬去吾爱精品软件的信息并写入excel

    2018的最后一天了,感觉今年有得有失,这里就不再浪费时间了,愿2019万事如意 之前的爬虫信息下载后只写入txt文档,想到了以后工作加入有特殊需求,趁放假有时间将这写数据写入excel表格 以吾爱精 ...

  8. VS2017上在线和离线安装Qt插件(在线安装)

    版权声明:如需转载,请告知博主并声明出处 https://blog.csdn.net/CLinuxF/article/details/88816436文章目录前言在线安装离线安装(推荐)前言很多朋友喜 ...

  9. ab压力测试工具的使用

    一.下载稳定版2.2.31 http://httpd.apache.org/ 二.2.2.*和2.4.*区别? httpd-2.2.x(prefork)    httpd-2.4.x(event) 编 ...

  10. pandas基础

    1.相关库导入 2.创建数据结构 pandas 有两个重要的数据结构: Series 和 DataFrame 创建Series数组,代表一行或一列 创建DataFrame ,代表二维数组 第一种方式: ...