融云近期推出的 Global IM UIKit,支持开拓者高效知足外洋用户交互体验需求,且保留了相称的产品张力授予开拓者更多自由和灵巧性,是实现环球化社交组件的不二之选。
关注【融云环球互联网通信云】理解更多

Global IM UIKit 采取“一键开关、二级重写、三级自定义”来扩充自定义能力,提升产品集成灵巧性,更好支持业务层创新。

一键开关:对付通用的、可列举的交互和 UI 自定义范围,Global IM UIKit 内部已经帮开拓者实现了干系功能和适配,只需一行代码,即可实现多种交互和 UI 的样式切换。

融云phpserverdemo融云 Global IM UIKit灵巧易用的即时通信组件设计思绪和最佳实践 AJAX

二级重写:支持 Module(页面)和 Component(组件)重写,包含 ViewModel、ViewController、View 的继续与重写。
适用于对默认实现办法的极度个性化定制,扩展性极高。

三级自定义:针对通用的、不可列举的自定义范围,供应 API 自定义能力,例如:导航栏的操作项、输入框扩展功能入口等。

本文将以 iOS 端集成为例,分享 Global IM UIKit 中组件的灵巧拆分和高扩展性的设计思路,以及快速集成的实战教程。

紧张功能和核心架构

在即时通讯组件中,紧张的功能可聚合为两大页面:

会话列表:以会话工具为元素,展示最近的会话工具,也是进行会话的紧张入口。
Global IM UIKit 针对会话项目支持左滑编辑和右滑编辑,供应置顶、免打扰、标记已读等操作。

会话:以工具为元素,展示所有工具,也是所有的发起入口。
Global IM UIKit 中常用的类型有:文本、图片、视频、文件、语音、贴纸等,支持回答、转发、复制等常用快捷交互。

除以上两大页面之外,还有很多其他衍生页面,如:多媒体、预览、用户列表、用户信息等。
当然,作为国际化产品,多措辞和主题也是不可少的。

在 Global IM UIKit 中,虽然核心页面构造相对大略,但其社交功能聚合度非常高。
尤其是在会话页面中,的吸收、发送、操作等,不仅仅是数据和 UI 的交互,还涉及的时序、安全、性能等,以及 SDK 的定制化需求。

利用传统的 MVC 框架会撑爆 Controller,给研发和掩护产生很多后患。
以是,融云 Global IM UIKit 采取 MVVM 架构,将数据处理与 UI 渲染和交互合理解耦,降落代码逻辑繁芜度,合营灵巧的组件拆分,提高扩展性。

MVVM 也是一个遍及度高、接管度好的框架,开拓者可以快速上手。
以会话列表和会话的对外接口类为例展示如下图:

UI 组件

Global IM UIKit 的一个页面对应一个 Module,Module 中包含一个或多个 Component,并管理这些 Component 的整体布局。

例如,全体会话页面是一个 Module,其又可以被划分为三个 Component,分别是:顶部导航栏、底部输入组件和中间的滚动区。

在 UI 组件中,PaaS 做事的难点在于:须要考虑与各种业务场景的适配,通过高可重用的组件和高开放性的接口,尽可能多地支持定制化需求。

以“底层精简+转让自由”为产品设计核心思路,Global IM UIKit 将 IM 会话页面 Module 和各组件 Component 的重写自由给到开拓者,开拓者可灵巧自定义而不影响可用性。

为实现这一点,Global IM UIKit 的会话页面需按业务逻辑拆分成独立功能组件。
每个模块可以作为重用单元,有助于解耦,降落系统的繁芜性,同时隔离也有助于提升开拓效率。

在模块中整体采取 MVVM 的设计模式,对付功能较大略的组件依然采取了 MVC 的设计模式。
当组件没有繁芜的交互和业务逻辑时,利用 MVC 会更加简洁明了。

多措辞和主题作为全局性的功能,会渗透到每个组件之中。
在设计组件时,须要为多措辞和主题的一键切换能力做铺垫。

组件拆分

根据列表页面显示,可以划分为导航条(Header)、列表(List)、工具栏(ToolBar)和会话输入框(Input),如下图示:

组件中会根据功能聚合再次拆分为更细粒度的组件,例如:输入框的 Reference、TextInput、Sticker、Recorder 等子组件。

这样,Module 与 Component 的基本组件关系就整理拆分完毕了。
开拓者可以用继续 Module,新增或修正 Component 的方法,实现组件的二级重写能力。

Component 便是 Global IM UIKit 模块化定义中的最小组成单元,也是开拓者可以重写的最小单元。
同时,重写的组件可以在上一级组件中,通过 setter 方法,设置为自定义组件。

组件实现

Global IM UIKit 采取 Objective-C 措辞开拓,所有组件基于原生框架实现。

组件中的颜色和图片,利用系统的动态接口,对应该前设置的主题:

/// 根据主题切换颜色

+ (UIColor )colorWithDynamicProvider:(UIColor (^)(UITraitCollection traitCollection))dynamicProvider;

/// 为 Image 注册不同主题对应的图片,根据主题切换

- (void)registerImage:(UIImage )image withConfiguration:(UIImageConfiguration )configuration;

组件中的笔墨是经由本地化后的:

/// 系统确当地化接口

NSLocalizedStringFromTable(key, tbl, comment)

导航条、列表、工具栏、输入框是 Global IM UIKit 中最核心的几大 Component。

导航条

Header 组件是对系统导航条 UINavigationBar 的自定义,通过定义旁边按钮和标题,在 ViewController 加载时赋值给 UINavigationBar。

@interface RCBaseHeaderView : NSObject

/// 左侧按钮

@property (nonatomic, strong) NSMutableArray<RCBarItem > leftItems;

/// 右侧按钮,默认编辑按钮

@property (nonatomic, strong) NSMutableArray<RCBarItem > rightItems;

/// 标题 baseView

@property (nonatomic, strong) RCBarTitleView titleView;

@end

按钮支持标题、图片和自定义视图显示,点击事宜通过 Action 抛出。

@interface RCBarItem : NSObject

/// 名称

@property (nonatomic, copy, nullable) NSString title;

/// 图标

@property (nonatomic, strong, nullable) UIImage image;

/// 事宜回调,如果自定义视图,这里不会有事宜回调

@property (nonatomic, copy, nullable) RCBarItemAction action;

/// 自定义视图

/// 自定义视图的点击事宜也须要自定义,当点击时,不会触发 action 调用

@property (nonatomic, strong, nullable) UIView customView;

/// 标签

@property (nonatomic, assign) RCBarItemTag tag;

@end

标题是自定义视图,支持标题和副标题两个 Label。

@interface RCBarTitleView : UIView

/// 标题 label

@property (nonatomic, strong) UILabel titleLabel;

/// 状态 label

@property (nonatomic, strong) UILabel statusLabel;

@end

列表

在 List 组件中,利用 UITableView 实现列表展示,支持设置占位视图:

@interface RCBaseListView : UIView

// 列表

@property (nonatomic, strong) UITableView tableView;

// 占位页面

@property (nonatomic, strong) UIView emptyView;

@end

无论会话还是,都须要分页加载,UITableView 的 insert 方法非常契合会话列表的动态加载效果,可以流畅的插入下页数据。

在会话列表中,每个会话对应一个 Cell,会话的展示样式基本同等,在列表中利用 RCChatCell 展示。
开拓者可以注册自定义 Cell:

@interface RCChatListView : RCBaseListView

...

/// 将会话 Cell 更换为继续 RCChatCell 的自定义 Cell

- (void)replaceChatCellWithClass:(Class)cellClass;

@end

在会话页面中,不同的展示样式差异很大,每个 Cell 都会继续于 RCMessageCell 自定义样式。
同样,开拓者也可以注册自定义 Cell:

@interface RCMessageListView : RCBaseListView

...

/// 注册自定义 Cell

/// - Parameters:

/// - cellClass: 自定义 Cell,须要继续 RCBaseMessageCell

/// - messageClass: 体,支持自定义和内置

- (void)registerClass:(Class)cellClass

forMessageClass:(Class)messageClass;

@end

工具栏

工具栏常日用于多选操作,根据业务需求,设置编辑按钮,利用工具栏须要考虑系统的 tabBar。

@interface RCTabBar : UIView

/// 按钮容器,用于支配按钮

@property (nonatomic, strong) UIStackView containerHStackView;

/// 顶部线条

@property (nonatomic, strong) UIView topLineView;

/// 按钮

@property (nonatomic, copy) NSArray<RCBarItem > items;

@end

在会话列表中,编辑栏固定为底部显示,会向上推起会话列表。
如果有系统 tabBar,就会将 tabBar 隐蔽,结束选择后,规复 tabBar。
在会话页面中,工具栏会直接覆盖输入框。

输入框

Input 是一个功能聚合度比较高的组件,包含了文本输入、多媒体选择、录音、引用、贴纸等子组件。

@interface RCInputBar : UIView

/// 引用 view

@property (nonatomic, readonly) RCReferenceView referenceView;

/// 输入框

@property (nonatomic, readonly) RCInputTextView textView;

/// 贴纸 view

@property (nonatomic, readonly) RCStickerBoardView stickerBoardView;

/// 输入框左边 items,默认为 addItem

@property (nonatomic, copy) NSArray<RCBarItem > leftItems;

/// 输入框右边 items,默认为 recordItem,当输入文本时,变为 sendItem

@property (nonatomic, copy) NSArray<RCBarItem > rightItems;

/// 加号按钮扩展 items,默认为:相册、相机、文件

/// 在每次扩展面板展示前设置生效

@property (nonatomic, strong) NSMutableArray<RCBarItem > addExpandItems;

@end

组件定制化

为了与运用中的社交模块平滑衔接,Global IM UIKit 的 UI 组件须要支持灵巧定制和高重用。
开拓者可以通过继续、布局和配置的二级重写和三级自定义办法,定制符合业务场景和体验的功能。

继续

推举开拓者利用继续的办法跳转会话列表和会话页面,在自定义的 ViewController 中,开拓者可以通过重写父方法,实现自定义操作:

@interface TestChatViewController : RCChatViewController

@end

@implementation TestChatViewController

- (void)reloadHeaderView {

[super reloadHeaderView];

// TODO custom

}

@end

也可以继续组件来实现自定义业务逻辑,在页面展示时通过 setter 的方法,设置为自定义的组件:

@interface TestChatHeaderView : RCChatHeaderView

@end

@implementation TestChatHeaderView

- (void)configure:(RCChatModel )model {

[super configure:model];

}

@end

TestChatViewController controller = [[TestChatViewController alloc] initWithChatModel:model];

controller.headerView = [[TestChatHeaderView alloc] init];

重写事宜代理回调,也可以添加自定义操作,如列表的代理事宜:

/// 列表事宜代理

@protocol RCMessageListViewDelegate <NSObject>

...

/// 列表中的 Cell 将要加载

/// - Parameters:

/// - listView: 列表页面

/// - messageModel: 工具

- (void)listView:(RCMessageListView )listView willLoadCell:(UITableViewCell )cell forMessageModel:(RCMessageModel )messageModel;

/// 列表中的 Cell 加载完成

/// - Parameters:

/// - listView: 列表页面

/// - messageModel: 工具

- (void)listView:(RCMessageListView )listView didLoadCell:(UITableViewCell )cell forMessageModel:(RCMessageModel )messageModel;

/// 列表中的 Cell 将要展示

/// - Parameters:

/// - listView: 列表页面

/// - messageModel: 工具

- (void)listView:(RCMessageListView )listView willDisplayCell:(UITableViewCell )cell forMessageModel:(RCMessageModel )messageModel;

/// 更多回调接口,请参考 SDK 接口

...

@end

布局

在各组件中,会利用 StackView 容器承载 View,可以在 StackView 中直接添加更多的 View。

这种办法紧张用在列表的 Cell 中,Cell 利用 Estimate 高度,充分运用 iOS 的 Auto Layout 功能特性,Cell 实际高度会由内容自动撑开。

@interface RCChatCell : RCTableViewCell

/// 会话容器,头像、会话内容

@property (nonatomic, readonly) UIStackView containerStackView;

...

/// 会话内容,在 containerStackView 中

@property (nonatomic, readonly) UIStackView contentStackView;

/// 会话上部分内容,在 containerStackView 中,用户信息、韶光

@property (nonatomic, readonly) UIStackView contentTopStackView;

...

/// 会话下部分内容,在 containerStackView 中,预览、已读

@property (nonatomic, readonly) UIStackView contentBotStackView;

...

@end

开拓者须要熟习 StackView 的根本利用方法,把稳 StackView 的布局方向。

配置

通过配置的办法增编削现有的功能逻辑,紧张适用基于 RCBarItem 的按钮事宜,例如:导航条旁边按钮是可变数组,可以直接修正该数组,并刷新 UI 即可:

@implementation TestChatViewController

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view.

RCBarItem item = [RCBarItem itemWithTitle:nil image:CustomImage action:^(RCBarItem item) {

// TODO custom

}];

[self.headerView.leftItems addObject:item];

[self reloadHeaderView];

}

@end

会话和列表的展示样式是需求最多的自定义组件,开拓者可以通过 List 供应的注册方法,完备自定义 Cell 的展示样式。

支持 Module 和 Component 的二级重写之外,如文章开头所述,融云 Global IM UIKit 亦对某些通用功能支持一键切换修正。

在支持开拓者环球化业务的多样实践中,融云创造有些通用功能也须要跟随业务实现多样化。
例如,单群聊自己的头像和昵称是否展示等。

针对这些功能,开拓者通过重写组件来实现多样化的本钱比较高,融云 Global IM UIKit 直接将此类功能直接封装成开关(与多措辞、主题切换类似),开拓者可根据业务场景一键切换利用。

数据处理

除了用户可以直不雅观看到的 UI 组件外,数据流处理也是 Global IM UIKit 中的主要环节。

数据处理的紧张难点在于:须要将繁芜的业务状态打算放入串行行列步队中异步处理。
通过节流优化,批量处理吸收或同步已读和回执。

紧张的数据处理有吸收、加载、未读数、已读回执、已读同步等,须要考虑的时序、IO 访问的频率、属性的缓存、UI 和数据逻辑的交互。

数据行列步队

的吸收、发送、加载和操作等都是并发事宜,为了能够将这些事宜有序、安全地通报给 UI 组件显示,须要有一个统一可调度的串行行列步队,确保数据和 UI 的之间的相互映射。

在 ViewModel 中,有专门处理数据的串行行列步队 dataQueue,所有事宜都会放入到串行行列步队中处理,ViewModel 会将处理结果通过回调接口,分发给对应组件刷新。

/// ViewModel 根本类

@interface RCBaseViewModel : NSObject

/// 操作数据的线程,所有对数据的操作必须在该线程中处理

@property (nonatomic, readonly) dispatch_queue_t dataQueue;

#pragma mark - Perform -

- (void)asyncPerformBlock:(dispatch_block_t)block;

- (void)syncPerformBlock:(dispatch_block_t)block;

@end

/// 会话数据回调接口

@protocol RCChatViewModelDelegate <NSObject>

...

/// 列表数据刷新

/// - Parameters:

/// - viewModel: ViewModel

/// - messages: 工具数组

- (void)viewModel:(RCChatViewModel )viewModel

didReloadMessages:(NSArray<RCMessageModel > )messages;

/// 插入列表数据

/// - Parameters:

/// - viewModel: ViewModel

/// - messages: 工具数组

/// - indexSet: 工具数组对应的位置

- (void)viewModel:(RCChatViewModel )viewModel

didInsertMessages:(NSArray<RCMessageModel > )messages

atIndexSet:(NSIndexSet )indexSet;

/// 列表数据更新

/// - Parameters:

/// - viewModel: ViewModel

/// - messages: 工具数组

/// - indexSet: 工具数组对应的位置

- (void)viewModel:(RCChatViewModel )viewModel

didUpdateMessages:(NSArray<RCMessageModel > )messages

atIndexSet:(NSIndexSet )indexSet;

/// 更多回调接口,请参考 SDK 接口

...

@end

数据加载

在列表中,为了流畅地滚动预加载体验,ViewModel 中除了 reload 方法外,也供应了加载上一页和下一页的方法。

随列表滚动,当达到加载下一页的条件时,会调用 loadNextPage 方法加载下一页数据。
ViewModel 会通过代理回调的方法,将下一页的数据通报给 List 显示出来。

@interface RCChatListViewModel : RCBaseViewModel

...

/// 加载会话数据

- (void)reloadData;

/// 拉取下一页数据

- (void)loadNextPage;

...

@end

@protocol RCChatListViewModelDelegate <NSObject>

/// 刷新会话列表

/// - Parameter viewModel: ViewModel

/// - Parameter chatModels: 会话数据

- (void)viewModel:(RCChatListViewModel )viewModel chatListDidReload:(NSArray<RCChatModel > )chatModels;

/// 插入会话

/// - Parameter viewModel: ViewModel

/// - Parameter indexSet: 会话 index list

- (void)viewModel:(RCChatListViewModel )viewModel chatListDidInsertAtIndexSet:(NSIndexSet )indexSet;

...

@end

数据节流

虽然数据线程能够确保有序展示到 UI 中,但在吸收时,须要考虑风暴的情形。
即,吸收大量直接刷新 UI,会导致 UI 卡顿,乃至崩溃。

因此,须要添加节流操作,在延时一定韶光后,进行批量处理。

- (void)onReceived:(RCMessage )message left:(int)nLeft object:(id)object {

self.cachedMessages[@(message.messageId)] = message;

if (nLeft == 0) {

__weak typeof(self) weakSelf = self;

[self.throttle scheduleThrottleWithBlock:^{

[weakSelf rc_throttleUpdateChatList];

}];

}

}

在吸收和发送时,还会触发已读回执、未读数清理、已读多端同步。
已读回执和已读同步在接口调用时,须要通过节流汇总,在一定韶光后统一上报。

__weak typeof(self) weakSelf = self;

[self.readReceiptThrottle scheduleThrottleWithBlock:^{

[weakSelf rc_sendReadReceipt];

}];

__weak typeof(self) weakSelf = self;

[self.readSyncThrottle scheduleThrottleWithBlock:^{

[weakSelf rc_syncReadTimeThrottle];

}];

处理

会话支持置顶、免打扰、标记未读和删除,当 ViewModel 中调用接口后,会触发远端和数据库状态更新,加载到内存的数据也须要掩护更新。
UI 的刷新直接依赖于内存数据,内存数据须要确保和本地数据库同等,本地数据库频繁触发 IO 访问。

可以将 ViewModel 中部分数据操作分配到内存中,同时内存根据处理结果和 UI 合营刷新展示。
例如:置顶操作,调用设置接口后,在内存中更新状态和重新排序,并将结果反馈给 List 进行 UI 刷新和动画。

@interface RCChatListViewModel : RCBaseViewModel

...

/// 移除数据,根据 models 批量移除对应会话,同时删除会话中的

/// 数据添加完成后,会触发 viewModel:chatListDidRemoveAtIndexSet: 回调

- (void)deleteChatModels:(NSArray<RCChatModel > )models;

/// 标记已读未读

/// 标记完成后,会触发 viewModel:chatListDidUpdateAtIndexSet: 回调

/// - Parameter model: 会话工具

- (void)toggleRead:(RCChatModel )model;

...

/// 设置/取消置顶

- (void)toggleTop:(RCChatModel )model;

...

/// 设置/取消免打扰

- (void)toggleNotification:(RCChatModel )model;

@end

接入指南

Global IM UIKit SDK 的核心集中于即时通讯的业务功能界面的实现,基于融云 RongCloudIM 供应的高并发、高可用通讯能力,以适应外洋用户利用习气的交互设计完成封装。
开拓者采取 Global IM UIKit SDK,在快速上手文档支持下,“分钟级”接入即可实现单群聊完全功能。

集成准备

创建融云开拓者账号,获取App Key。

开始之前,需创建融云开拓者账号并获取 App Key。
在开拓者后台,系统会自动为新账号创建一个运用。
默认利用海内数据中央,并供应开拓环境。
如果您已经有融云开拓者账号,可以直接创建新运用。

集成 SDK

推举利用 Pod 集成,如需手动集成,请移步官网下载 Framework,将 Framework 直接导入 App 即可。

☑ 在 podfile 中添加如下内容:

pod 'RongCloudGlobal/IMUIKit'

☑ 请在终端中运行以下命令:

pod install

如果涌现找不到干系版本的问题,可先实行 pod repo update,再实行 pod install。

☑ 上一步完成后,CocoaPods 会在您的工程根目录下天生一个 xcworkspace 文件,只需通过 XCode 打开该文件即可加载工程。

实现谈天功能

☑ 初始化

Global IM UIKit SDK 依赖于 IMLibCore 的即时通讯能力,利用前须要对 IMLibCore 进行初始化。
IMLibCore 核心类为 RCCoreClient,初始化时,须要传入生产或开拓环境的 App Key。

#import <RongIMLibCore/RongIMLibCore.h>

NSString appKey = @"Your_AppKey"; // example: bos9p5rlcm2ba

RCInitOption initOption = nil;

[[RCCoreClient sharedCoreClient] initWithAppKey:appKey option:initOption];

☑ 连接

用户 Token 是与用户 ID 对应的身份验证令牌,是运用程序的用户在融云的唯一身份标识。
运用客户端在利用融云即时通讯功能前必须与融云建立 IM 连接,连接时必须传入 Token。

用户可以在开拓者后台快速创建一个用户 John Doe,创建后返回 Token,利用该 Token 连接。

[[RCCoreClient sharedCoreClient] connectWithToken:@"John Doe token" dbOpened:^(RCDBErrorCode code) {

//数据库打开,可以进入到主页面

} success:^(NSString userId) {

//连接成功

} error:^(RCConnectErrorCode errorCode) {

if (status == RC_CONN_TOKEN_INCORRECT) {

//从 APP 做事获取新 token,并重连

} else {

//无法连接到 IM 做事器,请根据相应的缺点码作出对应处理

}

}];

☑ 用户信息

要在 Global IM UIKit 上展示用户、群组的头像、名称等,须要运用层(App)主动向 IMKit SDK 供应用户信息。
为了完全部验 Global IM UIKit 的 UI,我们将直接在用户信息数据库中写入对运用户 ID 的头像、名称信息。

下面设置了本地登任命户 John Doe 和另一个用户 Jane Smith 的头像、昵称。

[RCIMKitClient shared].enableUserInfoPersistence = YES;

RCUserInfo currentUserJohnDoe = [[RCUserInfo alloc] initWithUserId:@"userIdJohnDoe" name:@"John Doe" portrait:userPortraitUri];

[RCIMKitClient shared].currentUserInfo = currentUserJohnDoe;

RCUserInfo JaneSmith = [[RCUserInfo alloc] initWithUserId:@"userIdJaneSmith" name:@"Jane Smith" portrait:@"http://portrait"];

[[RCIMKitClient shared] refreshUserInfoCache:JaneSmith];

☑ 会话列表

Global IM UIKit 已默认供应会话列表页面和会话页面。
会话列表页面展示当前用户参与的所有单聊、群聊、系统会话,在会话页面可进行编辑、查看、回答、发送等活动。

连接成功后即可跳转到默认会话列表界面。
推举继续 SDK 中的 RCChatListViewController,例如 TestChatListViewController 类,示例如下:

@interface TestChatListViewController : RCChatListViewController

@end

初始化自定义的会话列表页面 TestChatListViewController,会话列表支持显示单聊、群聊、系统会话。
会话列表详细利用方法,拜会会话列表页面。

TestChatListViewController listVC = [[TestChatListViewController alloc] init];

[self.navigationController pushViewController:listVC animated:YES];

☑ 会话

为了快速体验,可以从开拓者后台【北极星】开拓者工具箱【IM Server API 调试】页面仿照用户 Jane Smith 向当前登录的用户 John 发送一条文本。

只要供应对方的 userId,融云就可支持跟对方发起谈天。
运用客户端可以通过用户 ID、群聊会话 ID 吸收。

以上便是 Global IM UIKit SDK 的全部集成步骤,“分钟级”接入即可感想熏染最具灵巧性的 IM UI 技能架构,以及与外洋用户利用体验对齐的交互设计。