【iOS 开发】使用环信实现聊天遇到的一些坑

使用环信实现聊天的过程遇到了一些坑,记录一下避免自己和其他人踩坑或者尽快出坑。

问题1 聊天页面环信工具栏向上偏移

问题描述:

在聊天页面,点击输入框弹起键盘,点击IQKeyboardManager带的完成按钮或者点击空白页面收起键盘,反复操作多次后,会出现页面向上偏移,环信UI的工具栏移动到了页面最上边。

解决方案:

在聊天页面禁用IQKeyboardManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
IQKeyboardManager *keyboardManager = [IQKeyboardManager sharedManager];
keyboardManager.enable = NO;
keyboardManager.enableAutoToolbar = NO;
}

- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
IQKeyboardManager *keyboardManager = [IQKeyboardManager sharedManager];
keyboardManager.enable = YES;
keyboardManager.enableAutoToolbar = YES;
}

问题2 保存环信昵称头像到数据库失败

问题描述:

环信服务器不存储用户的昵称头像等用户数据。需要客户端自己来存储并展示在界面上。有几个时机是需要插入或更新一条包含昵称、头像的用户数据到数据库里,比如查看用户详情,在某个页面收到环信消息。我们服务器提供的查询用户信息接口返回的昵称和头像,之前是没有问题的,有一次出现了从用户信息界面进入聊天页面不显示昵称的问题。

问题原因:

经排查,接口返回的用户头像字段的值是null,由于客户端之前没有做空处理,导致插入或更新一条数据失败。

1
2
DB Query: INSERT or REPLACE INTO t_huanxin (hxId,nickname,avatar) VALUES (?,?,?)
Unknown error finalizing or resetting statement (19: NOT NULL constraint failed: t_huanxin.avatar)

解决方案:

存储用户信息到本地数据库时增加安全判断。如果为空,就存入空字符串。避免插入或更新字段为null导致操作失败。

1
2
NSString *nickname = dic[@"nickname"]?:@"";
NSString *avatar = dic[@"avatar"]?:@"";

问题3 聊天页面发送首条消息后下拉刷新出现两条重复消息

问题描述:

使用环信Demo发送消息,首条消息发送后,下拉刷新,单聊页面会有两条重复的消息。
操作步骤:1.点击单聊页面右上角的清空按钮,清空某个用户的消息。2.返回会话列表。3.首次进入该用户的单聊页面。4.发送首条消息。5.页面下拉刷新。6.出现了两条一摸一样的消息。

解决方案:

EMChatViewControllertableViewDidTriggerHeaderRefresh方法中增加如下判断。

1
2
3
4
5
6
7
8
9
10
- (void)tableViewDidTriggerHeaderRefresh
{
//解决首条消息发送后下拉刷新出现两条重复消息的BUG 开始
if (self.dataArray.count && self.moreMsgId == nil) {
[self tableViewDidFinishTriggerHeader:YES reload:NO];
return;
}
//解决首条消息发送后下拉刷新出现两条重复消息的BUG 结束
other code
}

问题4 聊天页面发送语音,点击“按住录音”,发送了0秒的聊天记录

问题描述:

iOS环信Demo聊天页面,发送语音,点击“按住录音”,发送了0秒的聊天记录。

解决方案:

EMChatViewControllerchatBarRecordAudioViewStopRecord:timeLength:方法中增加对录音时长的判断。

1
2
3
4
5
6
7
8
9
10
11
12
- (void)chatBarRecordAudioViewStopRecord:(NSString *)aPath
timeLength:(NSInteger)aTimeLength
{
EMVoiceMessageBody *body = [[EMVoiceMessageBody alloc] initWithLocalPath:aPath displayName:@"audio"];
body.duration = (int)aTimeLength;
if (body.duration == 0) {
NSLog(@"录音时长为0");
NSLog(@"录制时间过短,不能发送");
return;
}
[self _sendMessageWithBody:body ext:nil isUpload:YES];
}

问题5 聊天页面点击查看图片,图片右上角按钮显示Done

问题描述:

聊天页面,点击查看图片,图片右上角的按钮显示Done,而不是”完成“。

解决方案:

直接全局搜索Done,把环信UI里的Done,替换成”完成“即可。

问题6 如何实现推送消息内容自定义

问题描述:

不想使用环信默认的推送内容:”您有一条新消息”,或”xxx:消息内容”。想实现自定义的消息内容。

解决方案:

发送消息扩展里添加字段em_apns_ext

1
2
3
4
5
{
nickname:"王小二",
avatar:"http://www.baidu.com",
em_apns_ext:{"em_push_content":"自定义推送内容"}
}

参考链接:环信 APNs 内容解析文档

问题7 头像链接与路由短链冲突

问题描述:

项目里点击推送通知到落地页采用了路由模式,有一个场景是需要我们自己服务器发推送点击通知栏跳转到聊天页面。路由短链类似:page://chat?from=hxId_10086,由于某些原因,需要带上昵称和头像,加上头像链接后,短链变成了page://chat?from=hxId_10086&avatar=http://www.baidu.com&nickname=王小二iOS 解析短链的方法是先把短链字符串转成了URL,加了avatar参数之后,转换失败,导致无法跳转。

解决方案:

  • 方案1:
    由于短链中含有URL,其实违反了路由链接设计的初衷,可以把avatar的值用AES加密一下传输,避免出现链接。
  • 方案2:
    不把短链字符串转换成URL,直接处理短链,去解析。

问题8 小程序发的语音消息iOS无法播放

问题描述:

小程序发过来的语音消息iOS无法播放。

问题原因:

AndroidiOS发的语音格式都是AMR,小程序发的语音格式是MP3iOS收到音频后,会先调用_convertAudioFile:方法转换格式,该方法内部实现实现里有一行代码是判断路径是否是MP3文件的,

1
[EMAudioPlayerHelper isMP3File:retPath]

这个判断方法有问题,发过来的是MP3,却判断不是MP3,接着音频被当成AMR去转换WAV ,转换音频格式失败,播放失败。

解决方案:

EMAudioPlayerHelper类中的startPlayerWithPath:model:completion:方法中增加判断,如果含有.mp3,就不转换,直接去播放。不含.mp3,就照旧走_convertAudioFile:转换格式方法。

1
2
3
4
5
6
if ([aPath containsString:@".mp3"]) {
//不转换
}else {
aPath = [self _convertAudioFile:aPath];
}
// aPath = [self _convertAudioFile:aPath];

问题扩展:

iOS发的语音消息,Web/小程序收到后无法播放?
App端发来的文件是AMR格式的,小程序需要下载的时候转成MP3格式去播放。转换方法如下:
参考链接:环信 Web IM 音频消息文档

最近的文章

《大话数据结构》三

第4章 栈与队列栈是限定仅在表尾进行插入和删除操作的线性表。队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。 栈的定义 栈(stack)是限定仅在表尾进行插入和删除操作的线性表。 我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为 …

学习笔记 阅读全文
更早的文章

《大话数据结构》二

第3章 线性表线性表:零个或多个数据元素的有限序列。 线性表的定义 线性表(List):零个或多个数据元素的有限序列。 线性表元素的个数 n (n>=0) 定义为线性表的长度,当 n = 0时,称为空表。 线性表的抽象数据类型ADT 线性表(List)Data​ 线性表的数据对象集合为 …

学习笔记 阅读全文