近期项目中要接入聊天,相对于直播来说这只是一个小需求,但是中间也有一些坑以及挣扎了好久的半屏聊天的实现。
1.首先先说下实现正常的聊天,讲真,融云的文档写的是真不怎么样,模棱两可,首先看下他的
- (void)getUserInfoWithUserId:(NSString *)userId completion:(void(^)(RCUserInfo* userInfo))completion
方法,按照文档上写的是两个用户,但是写法是一样的,给人一种错觉就是if else 里面只要写一样的就行了,我在后面的操作中也把自己的以及我要聊天的人的信息上传上去了,但是经实践是不对的,而且这个方法是只用实现一次的,也就是在appdelegate里面登录成功后实现一次这个方法就行了,不用每次给谁聊天都掉一下这个方法,正确的写法是if里面给融云上传自己的信息,在else里面请求一下自己服务器userid的数据,这个userid就是你要聊天对象的id再把请求的头像、昵称赋值给
RCUserInfo *user = [[RCUserInfo alloc]init];
user.userId = userInfo.member_id;
user.name = userInfo.basic_info.nickname;
user.portraitUri = imageUrl;
针对这一块我是写了一个单例,RongYunUserInfo,因为融云的token是有期限的,所以在登录成功之后需要校验rongyun_token是否为空,如果为空,再去服务器请求一下token
2.搞定了头像昵称的显示,列表显示正常了,但是从会话列表跳转到聊天列表,聊天记录不显示了,必须手动刷一次才能正常显示,各种检查代码,都是没问题的,最后没招,只能提工单了,结果答案是在设置所有RCConversationListViewController属性之前必须先设置
chatRoomScene.targetId = model.targetId;
chatRoomScene.conversationType = model.conversationType;
真蛋疼。设置完成之后,好了,聊天的正常事项我们都完成了,下面就是半屏聊天了。
3.实现半屏可能这个方法不是最好的,有更好的方法请告知,先说下我最初的实现方法,也给大家规避一些不好的实现方法,因为,我们是有两个聊天
,外面的完成界面的聊天,里面要实现一个高度只有290的mini聊天界面,但是界面是一样的,
(1)因为最初的聊天、消息是在导航栏上面设置的,正常的VC是不能设置坐标的,后来想到了用特殊的转场动画来实现,因为转场动画是可以设置toVC.view = 290的坐标实现一个mini的VC,这样既保留了导航栏的会话列表还能push到聊天界面,想想都很美好,说干就干了,找了一个简书上一个大神写的VC转场动画,基本都实现了,很开心,但是马丹,我从聊天界面跳转回会话列表的时候,会话列表又变成全屏的了,无论怎么去设置从转场进来的containView的坐标都不行,甚至用了最low的方法,从新拿到做转场动画的主VC的superView来设置也不行,至此,这个方法宣布GG,而且这个方法还有一个致命的问题,当然这个问题不是我们的原因,是融云不支持我们去修改工具栏RCChatSessionInputBarControl的坐标,用转场动画toVC是正常的,从toVC push的聊天界面的样式也是符合要求的minivC,但是他的坐标是按照全屏显示的,虽然导航栏显示的是push过来的,但是RCChatSessionInputBarControl还是在从导航栏开始为起始点屏幕的高度为终止点的位置,聊天的collectionView坐标是可以修改的,但是RCChatSessionInputBarControl不能修改,修改了虽然可以看到,但是不响应点击事件。这样就不能这样做了。
(2)既然要保证RCChatSessionInputBarControl在最下方不能修改,后来想到了用presentViewController来实现,分别present一个回话列表和一个聊天界面,现在有一个问题就是我们需要去更改present出来的VC的透明度,因为present出来vc是吧下面遮盖的,就在网上找到了下设置你要present VC的这三个属性就可以了
chatRoomScene.definesPresentationContext = YES;
chatRoomScene.modalPresentationStyle = UIModalPresentationOverCurrentContext;
chatRoomScene.view.backgroundColor = [UIColor clearColor];
这个地方以及下面的设置都是基于判断是present的是miniVC才这样设置,正常的聊天我们还是按照融云的正常聊天就行了
还是按照上面的方法跳转到聊天界面,因为他是全屏VC我们是可以去修改他的conversationMessageCollectionView的坐标,这样保证了不用修改工具栏的坐标,再自定义一个导航栏就可以了
中间需要去做的就是去监听键盘的弹出以及表情View弹出我们去修改试图的坐标以及自己造的导航栏的坐标动态修改一下试图的坐标就可以了
因为他的列表还是按照全屏显示的,我们再让列表自动滚动到最下方
//滚动到最下面
NSUInteger finalRow = MAX(0, [self.conversationMessageCollectionView numberOfItemsInSection:0] - 1);
if (0 == finalRow) {
return;
}
NSIndexPath *finalIndexPath = [NSIndexPath indexPathForItem:finalRow inSection:0];
[self.conversationMessageCollectionView
scrollToItemAtIndexPath:finalIndexPath
atScrollPosition:UICollectionViewScrollPositionBottom
animated:NO];
其中有一个问题就是返回上一级会话列表的时候数据不会和正常的聊天一样进行数据刷新,这也是唯一的不足,我的做法是在聊天界面disappear的时候发一个通知让会话列表去刷新
[[[[NSNotificationCenter defaultCenter]
rac_addObserverForName:@"RefreshMessageScene" object:nil]
takeUntil:[self rac_willDeallocSignal]]
subscribeNext:^(NSNotification *notification) {
@strongify(self);
/*!
从数据库中重新读取会话列表数据,并刷新会话列表
@warning 从数据库中重新读取并刷新,会比较耗时,请谨慎使用。
*/
[self refreshConversationTableViewIfNeeded];
}];
这个融云官方是不提倡的,但是没别的办法,还望谁看到了有更好办法的给我说一下。
总结,啰里啰嗦说这么多,是希望把自己走的弯路也贴出来给大家一个参考。
更新: 解决同安卓聊天无法正常显示头像昵称的问题
聊天都解决了,但是安卓大兄弟过来说咱们两端聊天的时候,我给你发消息的时候在iOS是正常的,但是你给我发消息的时候在安卓端显示不到你的头像和昵称,其实这个问题很好解决,就是上面提到的我们没法显示头像昵称的问题,因为安卓大兄弟也实现了
- (void)getUserInfoWithUserId:(NSString *)userId completion:(void(^)(RCUserInfo* userInfo))completion
这个方法,但是呢,他里面就是我们之前说的问题请求被聊天人信息时没有实现一次网络请求,所以拿不到我的信息,当时我一直问安卓大兄弟你这个地方请求网络了吗,大兄弟很肯定的告诉我,请求了。直觉告诉我就是这个地方出问题了,我问他呢你是怎么实现的,他说是用携带消息体的方式,安卓大兄弟说他如果用代理自己这边会出问题,那就只好iOS这边改了,这里补充下,融云的消息传递是有两种方式的,一种是实现代理进行网络请求,另一种是携带消息体的方式,就是把用户消息直接包含在消息里面,实现方式,两种方式携带消息体的优先级高,会优先读取消息体,这就是咱们说为啥安卓无法显示iOS信息,因为我用的代理,而安卓用的是携带消息体,我没有携带消息体,他也没有进行网络请求,所以显示就不正常了,携带消息体的写法
[RCIM sharedRCIM].currentUserInfo.userId = @"使用者ID";
[RCIM sharedRCIM].currentUserInfo.name = @"名字";
[RCIM sharedRCIM].currentUserInfo.portraitUri =@"图像";
//设置发送的消息是否携带用户信息
[RCIM sharedRCIM].enableMessageAttachUserInfo = YES;
这里面有个坑,因为两种实现方式,我以为实现这种方式之后就不需要再去实现代理了,问工单客服,客服也说不用再实现代理
然后我按照他说的做了,结果就会出现这个叼样子,只会显示User<>
后来再想了下,所有的操作我都没有对用户信息进行保存,这肯定不是正确做法,
然后我又在原来的实现代理的基础上,在前面加上了携带消息体,至此,所有完结,两端显示正常,那么这就有问题了,就按照我之前说的,是两种实现方式,两者都存在很明显是不合理的,然后就又去和安卓大兄弟讨论一番,把我的想法和处理逻辑给他说了下,发现他那边在实现的代理的时候是进行网络请求,但是没有将用户信息请求下来之后再去返回给融云,所以导致了他说的他那边自己显示会有异常,然后改了下就完美解决了,咱的代码再撤销回去。。。。
2018.6.15更新
近期发现,一个用户在没有登录的情况下,收到不同的人给发的聊天消息,或者自己同时给几个人发聊天消息,这时候登录回到聊天界面,会出现几个人使用一个人的聊天头像和昵称,根据数据显示聊天列表的数据源是对的,但是在getuserinfo的代理方法中会出现给的userid和列表数据不一致的情况(比如列表中有9条数据,但是代理方法中只会给6条左右的数据,导致无法比对数据重复的原因,是新包,无缓存情况下)给融云扯皮了两天,最后一点点排查,找到原因是在- (void)getUserInfoWithUserId:(NSString )userId completion:(void(^)(RCUserInfo userInfo))completion中调取自己服务器的userInfo后,在返回completion(userInfo)返回的时候,同时要调用 [[RCIM sharedRCIM] refreshUserInfoCache:user withUserId:x.member_id];刷新缓存的userInfo。。。