延续之前的文章,本篇关于即时聊天界面的开发技巧。首先需要参考别人的产品,最具有参考性的应该是微信App了,它可以发送文字、表情、语音、图片等,这里讨论的是文字与表情混排的常见消息类型。使用到的是YYText。
YYText对很多iOS开发者并不陌生,因为在处理图文混排问题上,经常会用上它,而且API也算容易上手。所以先看将讨论呈现的效果图:
其实上面主要有两个知识点,一个是是图像的区域拉伸,一个是富文本追加图片,然后使用YYTextLayout去计算文字的layout布局,添加Label的点击和长按事件,监听调用选择、全选、复制、粘贴的气泡。
使用到的图片资源:
实现的代码如下,详细注释了,其实也就是参考YYText给的sample,自己修修改改就行了,另一个对话气泡就没贴出代码了,因为跟下面的代码太过相似重复。
这个只是实现了效果图,但是没有放到UITableView里面去复用,算是个小TODO吧,后面可以加个聊天头像,放到TableView上滚动试试。
- (void)messageFromOther {
//创建支持拉伸的UIImage
UIImage *chatImage = [UIImage imageNamed:@"ReceiverTextNodeBkg"];
UIImage *resizableImage = [chatImage resizableImageWithCapInsets:UIEdgeInsetsMake(30, 28, 85, 28) resizingMode:UIImageResizingModeStretch];
//创建聊天背景的气泡ImageView
UIImageView *chatBg = [[UIImageView alloc] init];
chatBg.image = resizableImage;
[self.view addSubview:chatBg];
//创建YYLabel
YYLabel *chatLabelLeft = [[YYLabel alloc] init];
chatLabelLeft.numberOfLines = 0;
[self.view addSubview:chatLabelLeft];
//添加点击事件
chatLabelLeft.highlightTapAction = ^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) {
NSLog(@"TapAction:\n%@\n%@\n%@\n%@", containerView, text, NSStringFromRange(range), NSStringFromCGRect(rect));
};
//添加长按事件
chatLabelLeft.highlightLongPressAction = ^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) {
NSLog(@"LongPressAction:\n%@\n%@\n%@\n%@", containerView, text, NSStringFromRange(range), NSStringFromCGRect(rect));
};
//聊天气泡背景随Label的layout变化而变化,气泡包裹chatLabelLeft
[chatBg mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.equalTo(chatLabelLeft).offset(-20);
make.trailing.equalTo(chatLabelLeft).offset(20);
make.top.equalTo(chatLabelLeft).offset(-10);
make.bottom.equalTo(chatLabelLeft).offset(20);
}];
//创建富文本
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:@"hello??, 我是小小小鸭子?!我来自马达加斯加?,很高兴认识你。??有什么问题欢迎向我提问,我是一个建筑工人,负责搬砖的。?????????"];
attributedText.yy_font = [UIFont systemFontOfSize:16];
//富文本追加UIImage的Attachment
UIImage *image = [UIImage imageNamed:@"hot_position"];
NSMutableAttributedString *attachment = [NSMutableAttributedString yy_attachmentStringWithContent:image contentMode:UIViewContentModeCenter attachmentSize:image.size alignToFont:chatLabelLeft.font alignment:YYTextVerticalAlignmentCenter];
[attributedText appendAttributedString: attachment];
//计算富文本的layout
CGSize size = CGSizeMake(300, CGFLOAT_MAX);
YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:size text:attributedText];
chatLabelLeft.size = layout.textBoundingSize;
chatLabelLeft.textLayout = layout;
chatLabelLeft.y = 120;
chatLabelLeft.x = 40;
//增加高亮效果
YYTextBorder *highlightBorder = [YYTextBorder borderWithFillColor:[UIColor lightGrayColor] cornerRadius:0];
YYTextHighlight *highlight = [YYTextHighlight new];
[highlight setColor:[UIColor whiteColor]];
[highlight setBackgroundBorder:highlightBorder];
[attributedText yy_setTextHighlight:highlight range:NSMakeRange(0, attributedText.length)];
//富文本绑定到YYLabel上
chatLabelLeft.attributedText = attributedText;
}
对图片拉伸区域的界定是通过UIEdgeInsetsMake(30, 28, 85, 28)实现的,四个数字依次对应图片的上左下右四个像素距离,好像之前写过一篇关于拉伸的文章,不知道是不是在之前的博客上,待会去找找,找到了就移过来...找到了,点我跳转
其实上面的代码还很不完美,因为图文混排的追加不能用这样的方式去创建追加,那如果需要有很多的hot_position等图片需要追加的话,恐怕会成为灾难。采用的是YYText所提供的另一种方法,其实官网也有,我这边偷过来贴上:
// 1. 创建一个解析器
// 内置简单的表情解析
YYTextSimpleEmoticonParser *parser = [YYTextSimpleEmoticonParser new];
NSMutableDictionary *mapper = [NSMutableDictionary new];
mapper[@":smile:"] = [UIImage imageNamed:@"smile.png"];
mapper[@":cool:"] = [UIImage imageNamed:@"cool.png"];
mapper[@":cry:"] = [UIImage imageNamed:@"cry.png"];
mapper[@":wink:"] = [UIImage imageNamed:@"wink.png"];
parser.emoticonMapper = mapper;
// 内置简单的 markdown 解析
YYTextSimpleMarkdownParser *parser = [YYTextSimpleMarkdownParser new];
[parser setColorWithDarkTheme];
// 实现 `YYTextParser` 协议的自定义解析器
MyCustomParser *parser = ...
// 2. 把解析器添加到 YYLabel 或 YYTextView
YYLabel *label = ...
label.textParser = parser;
如果你不想用它赠送的解析器,可以自己写一个解析器,利用正则表达式去匹配特定的字符串,然后读取替换成图像表情。