惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
博客园 - 司徒正美
宝玉的分享
宝玉的分享
阮一峰的网络日志
阮一峰的网络日志
The Cloudflare Blog
月光博客
月光博客
博客园 - 【当耐特】
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
博客园 - 聂微东
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
雷峰网
雷峰网
博客园 - 叶小钗
量子位
IT之家
IT之家

土土哥的Blog

好久没有更新博客=。= 阿里巴巴国际无线技术部 - 招人啦~求iOS、Android、Java 转-阿里巴巴国际无线技术部 - 在这里遇见最好的自己 反编译分析并模拟实现methodSignatureForSelector方法 反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash 有趣的Autolayout示例5-Masonry实现 在对象dealloc的后期执行Task-开源库TTGDeallocTaskHelper 用QuartzCode快速实现一个收藏动画 开源项目-拼图验证控件TTGPuzzleVerify的实现 Swift开源Mac App - BingWallPaper 有趣的Autolayout示例4-Masonry实现 翻译-为什么objc_msgSend必须用汇编实现 API返回结果设计经验与总结 结合访问Out Parameters出现EXC_BAD_ACCESS的例子,反编译汇编解读__autoreleasing 总结一些iOS项目中组织代码的方法 对组件化与模块化的思考与总结 开源项目-TTGTagCollectionView 有趣的Autolayout示例3-Masonry实现 Swift开源项目: TTGEmojiRate的实现 Swift写的库-TTGSnackbar 有趣的Autolayout示例2-Masonry实现 解决iOS项目的版本兼容问题-结合宏、Category和Runtime 用Runtime的手段填充任意NSObject对象的nil属性 有趣的Autolayout示例-Masonry实现 UITextView编辑时插入自定义表情-续-自定义表情图片的大小 RPC框架Thrift例子-PHP调用C++后端程序 GCD使用经验与技巧浅谈 为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权 Enum-枚举的正确使用-Effective-Objective-C-读书笔记-Item-5 @autoreleasepool-内存的分配与释放
UITextView编辑时插入自定义表情-简单的图文混编
土土哥 · 2015-03-07 · via 土土哥的Blog

前言

在iOS开发中,经常需要用UITextView作为编辑文本的输入控件。
但是如何在编辑时插入自定义表情呢?就是像发微博时那样?

本文简单的用NSTextAttachmentNSAttributedString的特性,实现了

  • 在UITextView中编辑文字时插入自定义表情图片
  • 同时可以返回带有表情“替换符”的纯文本字符串。

示例

本文代码工程地址:https://github.com/zekunyan/UITextViewDIYEmojiExample

效果图:

效果图

背景知识

  • NSAttributedString及其子类,用于显示富文本。
  • NSTextAttachment,NSAttributedString的一种样式类,可以在文本中显示图片。
  • NSTextStorage,UITextView中的实际的文本封装。(见参考中的UITextView文档)

表情与其标志

首先需要明确的是,我们的自定义表情一定是有一一对应的“标志”的,如“[/emoji_haha]”。

就是说,为了方便处理,方便在数据库、网络传输中保存、传输带有表情图片的文本,我们必须要为每种表情取特定的“名字”,数据库中储存的、网络传输的文本就只包含这些标志名字就行,在显示的时候做对应的替换。

如:

tutuge.me表情

对应的纯文本就是:

tutuge.me[/emoji_1]

插入并显示表情图片

插入表情很简单,直接实例化NSTextAttachment类,将需要的表情的UIImage实例赋值给NSTextAttachment的image属性,然后用“[NSAttributedString attributedStringWithAttachment:]”方法实例化一个NSAttributedString的对象,插入到UITextView的textStorage对应的位置即可。

如下:

1
2
3
4
5
6
7
8
NSTextAttachment *emojiTextAttachment = [NSTextAttachment new];


emojiTextAttachment.image = emojiImage;


[textView.textStorage insertAttributedString:[NSAttributedString attributedStringWithAttachment:emojiTextAttachment]
atIndex:textView.selectedRange.location];

这样,就能在UITextView当前光标位置插入表情图片了。

获取带有表情标志的文本字符串

难点

NSTextAttachment被插入到NSAttributedString中的时候,就被当成了一个字符处理!!!
就是说,只从UITextView的text中,是找不回文本里面不同的表情所对应的标志的!

解决点

  1. 我们要能遍历出当前文本中所有的表情,也就是NSTextAttachment类。
  2. 我们要能知道遍历出的表情,对应的标志是什么。

遍历所有的NSTextAttachment类属性

遍历,嗯,先看看Apple有没有提供相应的方法,能遍历NSAttributedString(及其子类)的属性的。查阅文档:NSAttributedString Class Reference,可以找到这么一个方法:“- enumerateAttribute:inRange:options:usingBlock:”,其原型如下:

1
2
3
4
- (void)enumerateAttribute:(NSString *)attrName
inRange:(NSRange)enumerationRange
options:(NSAttributedStringEnumerationOptions)opts
usingBlock:(void (^)(id value, NSRange range, BOOL *stop))block

用处:

1
Executes the Block for the specified attribute run in the specified range.

看,就是这个方法~就能遍历出NSTextAttachment对象了~

创建NSTextAttachment的子类

如何绑定NSTextAttachment所表示的表情和与其对应的标志?创建子类嘛~直接在子类中增加属性,保存标志不就行了。
如下:

1
2
3
@interface EmojiTextAttachment : NSTextAttachment
@property(strong, nonatomic) NSString *emojiTag;
@end

所以,这个时候,插入表情的代码应该就是下面这样:

1
2
3
4
5
6
7
8
9
10
11
EmojiTextAttachment *emojiTextAttachment = [EmojiTextAttachment new];


emojiTextAttachment.emojiTag = emojiTag;


emojiTextAttachment.image = emojiImage;


[textView.textStorage insertAttributedString:[NSAttributedString attributedStringWithAttachment:emojiTextAttachment]
atIndex:textView.selectedRange.location];

创建NSAttributedString的Category

最后,就是将这个遍历表情、拼接最终文本字符串的方法设置成NSAttributedString的自定义Category方法,以方便直接调用。
当然,这里面有些细节的处理,如替换表情标志时的字符串偏移量计算等,看代码吧。

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39


@interface NSAttributedString (EmojiExtension)
- (NSString *)getPlainString;
@end




@implementation NSAttributedString (EmojiExtension)

- (NSString *)getPlainString {


NSMutableString *plainString = [NSMutableString stringWithString:self.string];


__block NSUInteger base = 0;


[self enumerateAttribute:NSAttachmentAttributeName inRange:NSMakeRange(0, self.length)
options:0
usingBlock:^(id value, NSRange range, BOOL *stop) {


if (value && [value isKindOfClass:[EmojiTextAttachment class]]) {

[plainString replaceCharactersInRange:NSMakeRange(range.location + base, range.length)
withString:((EmojiTextAttachment *) value).emojiTag];


base += ((EmojiTextAttachment *) value).emojiTag.length - 1;
}
}];

return plainString;
}

@end

使用

直接调用getPlainString方法即可。

总结

其实本文也是来源于最近的项目需求,在网上一直找不到比较好的解决方案,就自己摸索出来一个。至于复杂的图文混合编辑,当然还是Core Text来的强大(自己也在学习中)~

如果有更好地办法,一定要告诉我啊~~~

参考