1.1:融云的分类(简介)
1.1.1:融云 IM 界面组件 - RongCloud IMKit
IMKit 是融云 SDK 的核心特色之一。融云将即时通讯产品中最复杂的会话列表、聊天窗口、消息内容展现、会话设置等功能封装为组件,通过简短的代码,就可以直接将以上界面集成到App 产品中,省去大量的开发调试时间。融云同时支持业内最丰富的自定义界面组件功能,我们可以针对自己界面需求自由设计开发。
1.1.2:融云 IM 通讯能力库 - RongCloud IMLib
IMLib 是不含界面的基础 IM 通讯能力库,封装了通信能力和会话、消息等对象。引用到 App 工程中后,需要自己实现 UI 界面,相对较轻量,适用于对 UI 有较高订制需求的开发者。
1.1.3:融云 Call 界面组件 - RongCloud CallKit
CallKit 是融云音视频通话功能的 UI 界面 SDK。(基于RongCloud CallLib)包含了单人、多人音视频通话的界面的各种场景和功能。可以快速的集成 CallKit 来实现丰富的音视频通话界面,并进行 UI 定制开发。同时开源了 CallKit,您可以根据您的需要去使用。
1.1.4:融云 Call 通讯能力库 - RongCloud CallLib
1.2:融云 SDK的导入
1.2.1:通过 CocoaPods 导入管理依赖库(一般建议选用这个,不用手动管理依赖库,并且升级方便)
1.2.2:手动导入 SDK 并自己管理依赖库
1.3:融云的登录
1.3.1:登录方式: appKey 从融云开发者平台创建应用后获取到的App Key,这个App Key必须和服务端保持一致。不然下面的token验证通不过。
[[RCIM sharedRCIM] initWithAppKey:RONGCLOUNDAPP_KEY]; //初始化融云SDK,在APP初始化的时候必须走这个方法。
1.3.2:登录方法:Token令牌登录方式 注:token是从我们自己服务器获取(属于服务与融云的交互)
[[RCIM sharedRCIM] connectWithToken:loginModel.RongCloudToken success:^(NSString *userId) {
} error:^(RCConnectErrorCode status) {
NSLog(@"登陆的错误码为:%ld", (long)status);
} tokenIncorrect:^{
NSLog(@"token错误");
}];
1.3.3 :登录前所需做的事情(主要是RCIM 这个单例类的一些方法 用来设置一些全局变量)
//全局的导航按钮字体颜色
[RCIM sharedRCIM].globalNavigationBarTintColor = [UIColor blackColor];
//聊天界面中显示的头像大小
'[RCIM sharedRCIM].globalMessagePortraitSize = CGSizeMake(46, 46);
[RCIM sharedRCIM].globalMessageAvatarStyle = RC_USER_AVATAR_CYCLE;
//聊天界面中显示的头像形状,矩形或者圆形
[RCIM sharedRCIM].globalConversationAvatarStyle=RC_USER_AVATAR_CYCLE;
//设置接收消息代理
[RCIM sharedRCIM].receiveMessageDelegate=self;
[RCIM sharedRCIM].userInfoDataSource=self;
//设置IMKit连接状态的监听器
[RCIM sharedRCIM].connectionStatusDelegate=self;
1.4:简单实现聊天功能(不涉及业务逻辑和产品需求)
1.4.1:继承或者启动RCConversationListViewController这个控制器就可以打开聊天列表
//创建试图 直接跳转
WMConversationListViewController *recommendNewVC=[[WMConversationListViewController alloc]init];
//recommendNewVC.xiaoxilx=model.XIAOXILX;
[self.navigationController showViewController:recommendNewVC sender:nil];
//在试图初始化的时候 设置在列表中需要显示的会话类型 (微脉项目只有单聊)
[self setDisplayConversationTypes:@[@(ConversationType_PRIVATE),
@(ConversationType_DISCUSSION),
@(ConversationType_CHATROOM),
@(ConversationType_GROUP),
@(ConversationType_APPSERVICE),
@(ConversationType_SYSTEM)]];
1.4.2:继承或者启动 RCConversationViewController这个页面就可以打开聊天页面
//新建一个聊天会话View Controller对象
RCConversationViewController *chat = [[RCConversationViewController alloc]init];
//设置会话的类型,如单聊、讨论组、群聊、聊天室、客服、公众服务会话等
chat.conversationType = ConversationType_PRIVATE;
//设置会话的目标会话ID。(单聊、客服、公众服务会话为对方的ID,讨论组、群聊、聊天室为会话的ID)
chat.targetId = @"targetIdYouWillChatIn";
//设置聊天会话界面要显示的标题
chat.title = @"想显示的会话标题";
//显示聊天会话界面
[self.navigationController pushViewController:chat animated:YES];
总结:到这里截止,我们就可以实现单纯意义上的实现聊天功能。
2:融云集成进阶
2.1 :遗留问题
在第一个阶段聊天的时候,你会发现在聊天列表界面只能显示出未读消息和未读消息的数目以及时间。聊天人的姓名和头像却是看不见的。
原因是因为:融云本身作为IM实现的工具,不做这些数据的传输和保存(猜测:1,毕竟需要耗流量,能省则省。2,不参与到app逻辑,需求太多满足不了)。所以这两个基本数据的存储,展示和跟新落就理所当然的落到了我们APP自己身上(包括后期产品需要自定义UI数据的展示 ,—后面这个问题涉及到自定义聊天列表了,不在这里讨论了)。
2.2:解决方案
上面问题的根源是头像和姓名这些数据从哪里来? 显然必须用接口从我们自己服务器去获取 ,但是考虑到不能频繁请求接口调相同数据 ,所以APP必须建立数据库,把基本信息存储到本地。为了方便管理和增加可读性,在项目中我创建WMRCDataManager文件来管理 数据存储问题。(其实融云本地是做了自己的数据库,但是不提供给给我们方法。)
单例类的创建
+(WMRCDataManager *) shareManager;
//userInfoDataSource的代理回调
- (void)getUserInfoWithUserId:(NSString*)userId completion:(void (^)(RCUserInfo*))completion;
微脉数据存储机制如图:
2.3:延伸新问题
这样做的弊端:如果对方刷新信息,而前端已经缓存了消息,此时不会再次网络请求,导致数据跟新不及时。
解决方案:根据研究QQ和微信发现他们的数据跟新是在进入聊天页面或者查看他们的信息的时候。所以暂定解决办法是,在每一次进入聊天页面时候获取一下对方最新信息。然后跟新本地数据库数据。(弊端:调用接口的次数还是很多。从性能上讲不合理。不过我们的产品暂时没有这方面的需求,所以没做)
3:聊天列表UI和聊天UI的自定义
前言:每一个产品都有一个自己风格和需求,所以融云的聊天列表的UI和聊天详情的UI基本上满足不了微脉的需求。这里自定义UI的实现就很有必要了。
3.1:聊天列表的UI的自定义
3.1.1:我们项目中聊天列表WMConversationListViewController 是在继承RCConversationListViewController视图的基础上创建的。这里有可能你会问:为什么不自己写一个界面,不集成他们的界面?原因如下:虽然我们的视图展示改变了,但是依然需要使用这个界面的刷新逻辑(相对而言,工作量小,效率高)。
自定义列表cell需要调用下面四个方法
即将加载列表数据源的回调,这个方法很必要是一个转换数据类型的功能,起桥接作用
@param dataSource 即将加载的列表数据源(元素为RCConversationModel对象)
@return 修改后的数据源(元素为RCConversationModel对象)
@discussion 您可以在回调中修改、添加、删除数据源的元素来定制显示的内容,会话列表会根据您返回的修改后的数据源进行显示。
数据源中存放的元素为会话Cell的数据模型,即RCConversationModel对象。
-(NSMutableArray *)willReloadTableData:(NSMutableArray *)dataSource{
for (int i=0; i<dataSource.count ; i++){
RCConversationModel *model = dataSource[i];
if(model.conversationType == ConversationType_PRIVATE){
// 会话Cell数据模型的显示类型 转化为自定义类型
model.conversationModelType = RC_CONVERSATION_MODEL_TYPE_CUSTOMIZATION;
}
}
return dataSource;
}
自定义会话Cell显示时的回调
-(RCConversationBaseCell *)rcConversationListTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//拿到数据源
RCConversationModel *model = self.conversationListDataSource[indexPath.row];
//注册cell
WMRCChatListCell *cell = (WMRCChatListCell *)[[WMRCChatListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"WMRCChatListCell"];
//中间cell上控件的赋值这里省略
return cell;
}
自定义会话Cell显示时的回调
-(CGFloat)rcConversationListTableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
//返回cell高度
return kCellHeight;
}
左滑删除自定义会话时的回调
- (void)rcConversationListTableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
'[[RCIMClient sharedRCIMClient] removeConversation:model.conversationType targetId:model.targetId];
'' [[RCIMClient sharedRCIMClient]clearMessages:ConversationType_PRIVATE targetId:model.targetId];
}
3.1.2:第一个方法很关键。他是一个转换的功能,可以把指定的一类消息转换为自定义消息类型。例如:把单聊类型转化为自定义类型。
3.1.3:然后在cellForRowAtIndexPath方法中直接,创建cell。然后给cell上的控件赋值。(这里的数据从哪里来?)这个时候就开始调用WMRCDataManager 类的getUserInfoWithUserId这个方法从本地获取数据,(这里本地如果没有数据,我会从服务接口去拿,然后缓存到本地,这样就不用么一次都调用接口,给服务端怎成压力了。)
WMRCChatListCell *cell = (WMRCChatListCell *)[[WMRCChatListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"WMRCChatListCell"];
3.1.4:数据展示完了,我们直接调用heightForRowAtIndexPath的方法把cell的高展示出来。
3.1.5:commitEditingStyle用来左滑删除单个会话列表的,这里有两种删除意思:1:从列表中删除这个记录,但是聊天消息并不删除依然在本地存储。
2:列表删除并把聊天记录一并删除。
[[RCIMClient sharedRCIMClient] removeConversation:model.conversationType targetId:model.targetId];
[[RCIMClient sharedRCIMClient]clearMessages:ConversationType_PRIVATE targetId:model.targetId];
自此自定义聊天列表已经按照要求满足我们微脉的需求。
3.2:聊天页面UI的自定义
这一块项目上暂时还未实现。私下已经走通了简单消息的自定义。
3.2.1自定义消息类—WMRCRichMessage 必须继承与RCMessageContent 。这样这一条消息才会储并计入未读消息数。
//声明一个标志
#define WMRCRichMessageTypeIdentifier @"RCD:WMRichMsg"
3.2.2 在融云注册之前先注册消息类型(申明此类的存在)
// 注册自定义测试消息 是消息类型
//RCDTestMessage
[[RCIM sharedRCIM] registerMessageType:[RCDTestMessage class]];
3.2.3自定义cell—WMRCRichMessageCell 必须继承与RCMessageCell 。这样这cell才能展示用户信息和内容的消息。而且这类cell在注册时候必须和RCDTestMessage消息进行绑定。如下:
[self registerClass:[RCDTestMessageCell class] forCellWithReuseIdentifier:RCDTestMessageTypeIdentifier];
3.2.4参照聊天列表页面UI的自定义 实现以下三个方法
注册自定义消息的Cell
@param cellClass 自定义消息的类,该自定义消息需要继承于RCMessageContent
@param identifier 自定义消息Cell的唯一标示符
@discussion 聊天界面在显示时需要通过identifier唯一标示来进行Cell重用,以提高性能。
我们建议您在identifier中添加前缀,请勿使用"rc"前缀的字符串,以免与融云内置消息的Cell冲突。
- (void)registerClass:(Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
自定义消息Cell显示的回调
@param collectionView 当前CollectionView
@param indexPath 该Cell对应的消息Cell数据模型在数据源中的索引值
@return 自定义消息需要显示的Cell
@discussion 自定义消息如果需要显示,则必须先通过RCIM的registerMessageType:注册该自定义消息类型,
并在聊天界面中通过registerClass:forCellWithReuseIdentifier:注册该自定义消息的Cell,否则将此回调将不会被调用。
- (RCMessageBaseCell *)rcConversationCollectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath;
自定义消息Cell显示的回调
@param collectionView 当前CollectionView
@param collectionViewLayout 当前CollectionView Layout
@param indexPath 该Cell对应的消息Cell数据模型在数据源中的索引值
@return 自定义消息Cell需要显示的高度
@discussion 自定义消息如果需要显示,则必须先通过RCIM的registerMessageType:注册该自定义消息类型,
并在聊天界面中通过registerClass:forCellWithReuseIdentifier:注册该自定义消息的Cell,否则将此回调将不会被调用。
- (CGSize)rcConversationCollectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout *)collectionViewLayout
sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
4:总结
第三方接入,本身的难度有限,但是为了满足产品的逻辑去修改第三方的本身逻辑是最蛋疼的事情。解决问题的关键是:看文档,看官方demo,多尝试。