云信提供的方式有两种 服务器同步信息 和 客户端同步信息,虽然云信推荐使用 服务器同步信息 方式,但是抱歉我们的产品不是这样做的,而是需要我们客户端储存用户信息进行维护资料。
在使用NIMKit中,涉及到会话列表,聊天页面,以及自定义聊天页面的设计,根据云信的设计中,只要有userId(我们项目中定义是叫做chatAccount,以下都称作userId),就能直接获取到用户信息,但是直接获取的话是获取到的云信维护的用户信息([NIMKit sharedKit].provider通过这个对象获取用户信息),但是我们的用户信息不依赖云信而是由我们自己维护,所以在聊天页面和会话列表显示出来的头像和名称有可能与我们自己需要的头像和名字不一致,在刚刚开始使用云信的NIMSDK的时候也确实纠结了一会,后来翻阅云信在guthub上的文档发现了,实际上我们只要有一个类实现了
NIMKitDataProvider
协议以及NIMKitDataProviderImpl
类,并且在Appdelegate中像NIMSDK注册,就能够实现自定义的用户信息的获取了。
-
创建一个对象
PGDataProviderImpl
集成自NIMKitDataProviderImpl
-
重写
NIMKitDataProvider
协议中的方法, -
/** * 上层提供用户信息的接口 * * @param userId 用户ID * @param option 获取选项 * * @return 用户信息 */ - (NIMKitInfo *)infoByUser:(NSString *)userId option:(NIMKitInfoFetchOption *)option; /** * 上层提供群组信息的接口 * * @param teamId 群组ID * @param option 获取选项 * * @return 群组信息 */ - (NIMKitInfo *)infoByTeam:(NSString *)teamId option:(NIMKitInfoFetchOption *)option;
我在早期实现这些方法的方式是
- 先通过userId从我本地的数据库中获取资料
- 获取本地数据库数据成功则返回对应的数据
- 但是一旦本地数据库没有,则返回云信默认的数据(毕竟云信默认的数据是创建云信账号时的原始数据,等于是我们用户的初始数据,)同时向服务端获取最新的数据,且刷新界面。
- 这是我一开始的解决方案,但是体验上感觉不好。
后来修改了数据获取的策略,
- 每条发送的消息中都会包含一条ext数据,这里包含了发送者的头像名称的相关需要的数据。ps.群聊消息则还会包含群聊的资料以及当前发送者在当前群聊中的群昵称以及其他相关资料。
- 接收消息方会将本条消息中的有效数据暂时放在数据库中
- 实现
NIMKitDataProvider
协议 - 先通过userId从我本地的数据库中获取资料
- 获取本地数据库数据成功则返回对应的数据
- 但是一旦本地数据库没有,则优先返回接收的消息中缓存的用户资料,并且同时向自己的服务器获取完整的用户资料数据
- 这样的话,体验上会好很多,不会出现前后显示不一致的情况了
3.在Appdelegate中,云信SDK注册方法之后写上[NIMKit sharedKit].provider = [[PGDataProviderImpl alloc]init];
,以后在重写会话列表,聊天列表和群聊资料时获取到的资料就是我们需要的数据了。
-
自定义消息模型 以及 发送自定义消息
- 创建模型 实现
NIMCustomAttachment
协议,主要是实现- (NSString *)encodeAttachment;
方法,才能使云信sdk将这条自定义的消息进行序列化,如果附带其它的附件消息,实现对应的协议方法即可。 - 发送自定义消息和正常的发送消息没有区别,别忘了
NIMCustomObject
这个对象包含自定义消息模型就好了
- 创建模型 实现
-
接收自定义消息
-
创建对象
PGAttachmentDecoder
并且实现NIMCustomAttachmentCoding
协议,帮助云信实现自定义消息模型的反序列化。即实现- (id<NIMCustomAttachment>)decodeAttachment:(NSString *)content
,content是上面encodeAttachment
序列化出来的json数据。- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... [NIMCustomObject registerCustomDecoder:[[AttachmentDecoder alloc]init]]; ... }
-
-
新建气泡内容 气泡内容类需要继承 NIMSessionMessageContentView ,并使用
- (instancetype)initSessionMessageContentView
作为初始化方法。内容里根据业务需求自行排版。- 至于内部到底怎么写,可以参考云信提供的Demo。
- 整个cell能自定义的部分也只有这个contentView,但是实际可以将这个气泡完全填充整个cell,也就变相的实现了整个cell的自定义
-
新建自定义消息气泡布局配置,配置需要实现
NIMCellLayoutConfig
协议。这里除自定义消息外,其他消息沿用内置配置,所以配置类继承基类NIMCellLayoutConfig
。以下是云信官方提供的demo@interface CellLayoutConfig : NIMCellLayoutConfig<NIMCellLayoutConfig>
@end ```
```
@implementation CellLayoutConfig
- (CGSize)contentSize:(NIMMessageModel *)model cellWidth:(CGFloat)width{
//填入内容大小
if ([self isSupportedCustomModel:model]) {
//先判断是否是需要处理的自定义消息
return CGSizeMake(200, 50);
}
//如果不是自己定义的消息,就走内置处理流程
return [super contentSize:model
cellWidth:width];
}
- (NSString *)cellContent:(NIMMessageModel *)model{
//填入contentView类型
if ([self isSupportedCustomModel:model]) {
//先判断是否是需要处理的自定义消息
return @"ContentView";
}
//如果不是自己定义的消息,就走内置处理流程
return [super cellContent:model];
}
- (UIEdgeInsets)cellInsets:(NIMMessageModel *)model{
//填入气泡距cell的边距,选填
if ([self isSupportedCustomModel:model]) {
//先判断是否是需要处理的自定义消息
return UIEdgeInsetsMake(5, 5, 5, 5);
}
//如果不是自己定义的消息,就走内置处理流程
return [super cellInsets:model];
}
- (UIEdgeInsets)contentViewInsets:(NIMMessageModel *)model{
//填入内容距气泡的边距,选填
if ([self isSupportedCustomModel:model]) {
//先判断是否是需要处理的自定义消息
return UIEdgeInsetsMake(5, 5, 5, 5);
}
//如果不是自己定义的消息,就走内置处理流程
return [super contentViewInsets:model];
}
@end
```
-
将创建好的布局配置类注入到组件中,保证在会话页实例化之前注入即可。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... //注册 NIMKit 自定义排版配置 [[NIMKit sharedKit] registerLayoutConfig:[CellLayoutConfig new]]; ... }