iOS 图文混排
2016-05-29 12:33
399 查看
使用系统自带的NSAttributedString来处理,对于一般的图文混排已经足够了,但是,有一个缺点就是NSAttributedString并不支持gif动画。实际上,使用gif动画还是挺卡的。
思路:
1.通过RegexKitLite 正则,匹配出所有需要特殊处理的字符
2.由于表情图片占用一个字符,使用直接替换范围的方式,会导致后面的表情范围不对。有两种处理方案
方案一:
使用两个数组,分别装特殊字符(文字内容,文字范围,是否为特殊字符,是否为表情)和非特殊字符,按范围排序成一个新数组
循环新数组List,通过判断是否为表情和是否为特殊字符来添加appendAttributedString属性。
方案二:
通过递归的方式,每找到一个表情,先替换掉,再递归找下一个
当前,使用的是方案一,核心代码:
View Code
在使用过程中,我们需要注意一点,计算文本的宽高,通过下面来计算
在计算过程中,我们并没有传字体大小,因而,我们需要给attributedText一开始设定字体大小:
设置行高
附源代码:http://pan.baidu.com/s/1o8Su8H0
目录说明:
HomeViewController ---列表
StatusCell ---列表TableCell
StatusFrame ---列表TableCell高度。由于cell的高度是不固定的,因此我们定义StatusFrame来管理所有的控件尺寸,最后返回总高度
StatusModel ---微博模型,图文混排处理,在这里做核心混排处理,通过添加attributedText属性处理。
--TextPartModel --StatusModel嵌套属性,用于记录RegexKitLite 正则匹配出的字符
--SpecialModel --StatusModel嵌套属性,用于特殊实符点击变色的范围比比较
--UserModel --StatusModel嵌套属性,用户模型
思路:
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 == 0) 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 == 0) 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:15]; 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(0, -3, 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(0, attributedText.length)]; [attributedText addAttribute:@"specials" value:specials range:NSMakeRange(0, 1)]; return attributedText; }
View Code
在使用过程中,我们需要注意一点,计算文本的宽高,通过下面来计算
[status.attributedText boundingRectWithSize:CGSizeMake(maxW, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
在计算过程中,我们并没有传字体大小,因而,我们需要给attributedText一开始设定字体大小:
// 一定要设置字体,保证计算出来的尺寸是正确的 [attributedText addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, attributedText.length)];
设置行高
// 定义行高 NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setLineSpacing:5]; [attributedText addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, 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计步器功能实现之CoreMotion(一)
- iOS动画系列之二--CALayer的contents属性的应用
- IOS入门 SandBox Plist NSUserDefault
- iOS开发- 只能选择iOS Device,无法选择模拟器解决办法
- [基础]iOS 可视化编程(全系列)
- iOS开发真机调试证书导出.p12文件
- IOS中的单例模式
- IOS手势处理
- iOS开发Category(扩展)大全
- BIOS中未启用虚拟化支持系列~~例如:因此无法安装Hyper-V
- iOS开发-即时通信XMPP
- iOS开发-即时通信XMPP
- Android 和 iOS 团队开发中如何快速定位代码修改者等提交信息
- Foundation框架—字典NSDictionary 可变和不可变
- Foundation框架—数组 NSArray -NSMutableArray
- NSString+Check, iOS 判断手机号码格式是否正确
- iOS assign,weak,strong,copy 详解
- Postman 的使用 测试一个POST格式的Web API
- iOS手势识别的详细使用:拖动、缩放、旋转、点击、手势依赖、自定义手势
- iOS 项目中出现的一些问题