中山大学数据科学与计算机学院本科生实验报告
(2019年春季学期)
课程名称 | IOS开发 | 任课老师 | 郑贵锋 |
---|---|---|---|
年级 | 16 | 专业(方向) | 软件工程(计算机应用方向) |
学号 | 16340132 | 姓名 | 梁颖霖 |
电话 | 13680473185 | dic0k@qq.com | |
开始日期 | 2019/6/9 | 完成日期 | 2019/6/12 |
一、实验题目
IM聊天工具
二、实现内容
- IM聊天记录显示图片文字混排
- 实现好友间发送图片
三、实验结果
IM聊天记录显示图片文字混排
由于在实现了文字纯文本的好友聊天功能后,我们添加了发送图片这一功能,可以在好友记录中显示出来。这一来需要我改变之前显示聊天记录的TableView的显示,经过一些搜索,找到了实现这一需求叫做实现图文混排。有以下一些思路来实现图文混排:
- UITextView结合NSAttributeString实现图文混排编辑,这个方案可以在网上找到对应的开源代码,比如 SimpleWord 的实现就是使用这种方式,不过缺点是图片不能有交互,比如说在图片上添加进度条,添加上传失败提示,图片点击事件处理等等都不行,如果没有这种需求那么可以选择这种方案。
- 使用WebView通过js和原生的交互实现,比如 WordPress-Editor 、RichTextDemo ,主要的问题就是性能不够好,还有需要你懂得前端知识才能上手。
- 使用CoreText或者TextKit,这种也有实现方案的开源代码,比如说这个 YYText ,这个很有名气,不过他使用的图片插入编辑图片的位置是固定的,文字是围绕着图片,所以这种不符合我的要求,如果要使用这种方案,那修改的地方有很多,并且CoreText/TextKit使用是有一定的门槛的。
- 使用UITableView结合UITextView的假实现,主要的思路是每个Cell是一个文字输入的UITextView或者是用于显示图片使用的UITextView,图片显示之所以是选择UITextView是因为图片位置需要有输入光标,所以使用UITextView结合NSAttributeString的方式正好可以实现这个功能。图片和文字混排也就是显示图片的Cell和显示文字的Cell混排就可以实现了,主要的工作量是处理光标位置输入以及处理光标位置删除。
而我这里刚好也适用的是第四种方法,仅需改变UITableView里面的TextView变成富文本的形式,即使用UITextView结合NSAttributeString的方式正好可以实现这个功能。
以上参见博客
除去这个方法,这里还有使用TextKit
实现图文混排
- 拼接string
/*!
* 插入表情
*
* @param rangeArray 所要插入表情的位置 每个元素包括(loc)
* @param facesArray 要插入的表情的信息 每个model包括(id imageName)
* @param pStr 原始的字符串
* @param sourceArray 表情包
*
* @return 拼接好的字符串
*/
+ (NSMutableAttributedString *)setupEmojiByRangeArray:(NSArray *)rangeArray facesArray:(NSArray *)facesArray plainStr:(NSAttributedString *)pStr sourceArray:(NSArray *)sourceArray {
NSMutableAttributedString * mutStr = [pStr mutableCopy];
__block int offset = 0;
//每次加过一个表情后 字符串会变长 这个变量会记录已经增加的长度,不然插入的位置会错位的
if(rangeArray.count == facesArray.count) {
// 看看要插入的位置的数量 是不是和要插入的表情数量一致
[facesArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 数组的枚举
for(FaceModel * fm in sourceArray){
// 这个在不在表情包里面
if(fm.id == [obj[@"ID"]intValue]) {
UIImage * image1 = [UIImage imageNamed:fm.imageName];
NSTextAttachment * attachment1 = [[NSTextAttachment alloc] init];
//这就是要插入的对象
attachment1.bounds = CGRectMake(0, -3, 20, 20);
// 插入的表情的大小
attachment1.image = image1;
NSAttributedString * attachStr1 = [NSAttributedString attributedStringWithAttachment:attachment1];
// 把要插入的对象转为string
NSDictionary * dic = [rangeArray objectAtIndex:idx];
// 看看插入哪个位置
[mutStr insertAttributedString:attachStr1 atIndex:offset+[dic[@"loc"]intValue]];
offset += (int)attachStr1.length;
// 插入结束要计算一下偏移量
}
}
}];
}
// //添加链接
// NSURL * url = [NSURL URLWithString:@"http://www.baidu.com"];
// [mutStr addAttribute:NSLinkAttributeName value:url range:NSMakeRange(loc, length)];
// mainText.attributedText = [mutStr copy];
return mutts;// 返回处理好的字符串
}
这个是IM的必要需求了。通过NSTextAttachment的使用,我们可以插入表情的image了。因为我这个是批量处理,所以我插入规格一致的表情。同理,我们可以插入一张图片,这就实现了图文混排了。
- 计算NSMutableAttributedString的Rect
// 字体大小
UIFont *font =[UIFont systemFontOfSize:14];
// 段落设置
NSMutableParagraphStyle *p = [[NSMutableParagraphStyle alloc]init];
p.lineBreakMode = NSLineBreakByCharWrapping;
p.lineSpacing = 0.1f;
p.paragraphSpacing = 0.1f;
p.alignment = NSTextAlignmentLeft;
// 设置NSAttributedString 的样式
NSAttributedString * str = [[NSAttributedString alloc]initWithString:rString attributes:@{NSFontAttributeName:font,NSParagraphStyleAttributeName:p}];
// 计算Rect
// w代表你想要的宽度 h代表你的字符串最大支持的高度
CGRect rect = [sstr boundingRectWithSize:CGSizeMake(w,h) options:NSStringDrawingUsesFontLeading|NSStringDrawingUsesLineFragmentOrigin context:nil];
最后,依靠计算出来的rect 我们就知道要怎么设置cell的高度了。再把这个string放到一个textView里面,textvView放到cell上,大功告成。
使用UITableView的实现细节
- Cell中添加UITextView,文字输入换行或者超过一行Cell高度自动伸缩处理
- Cell中添加UITextView显示图片的处理
- 光标处删除和添加图片的处理,换行的处理
前面第一点已经由我的组员完成了,所以我这里仅需要关注如何显示图片即可。
NSAttributedString
结合NSTextAttachment就行了
/**
显示图片的属性文字
*/
- (NSAttributedString*)attrStringWithContainerWidth:(NSInteger)containerWidth {
if (!_attrString) {
CGFloat showImageWidth = containerWidth - MMEditConfig.editAreaLeftPadding - MMEditConfig.editAreaRightPadding - MMEditConfig.imageDeltaWidth;
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
CGRect rect = CGRectZero;
rect.size.width = showImageWidth;
rect.size.height = showImageWidth * self.image.size.height / self.image.size.width;
textAttachment.bounds = rect;
textAttachment.image = self.image;
NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:textAttachment];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@""];
[attributedString insertAttributedString:attachmentString atIndex:0];
_attrString = attributedString;
// 设置Size
CGRect tmpImageFrame = rect;
tmpImageFrame.size.height += MMEditConfig.editAreaTopPadding + MMEditConfig.editAreaBottomPadding;
_imageFrame = tmpImageFrame;
}
return _attrString;
}
图片的获取
关于发送的图片显示是通过拉取用户聊天记录获得的,这与之前的访问服务器拉取文字记录类似,只不过现在多了图片的url而已。而对于url的处理是根据上周使用的SDWebImage来获取并且显示,这里就不重复展示。
四、实验思考及感想
本周做的东西不算太多,由于端午假期没能到实验室进行实训。再一方面,我们小组已经基本完成了需求,开始在做一些扩展的功能,我负责的就是发送图片这一功能。基本的文字发送显示架构已经可以使用,更改成图片的显示无非就是把处理文字的思路转变到处理图片上。所需要的工作包括向服务器发送图片,从服务器获取图片,将图片显示在聊天记录页面上。这周我完成的是后两步的demo功能,待下周周末到实验室再将这个demo改造到我们的IM上,难度应该不大。