文档导航
iOS
SDK 版本:  5.X
公告:融云新文档中心已上线,欢迎到新文档中心阅读 iOS IMLibiOS IMKit 的文档。

会话页面

更新时间:2024-02-26 PDF

会话页面

会话页面即应用程序中的聊天页面,主要由消息列表和输入区两部分组成。IMKit 提供基于 UIKit UIViewController 类实现的会话页面类 RCConversationViewController

聊天界面

聊天界面一般由三个部分组成:标题栏、消息列表、输入区域。

提示

IMKit 默认会话页面 RCConversationViewController 未包含标题栏的实现,您需要自行设置会话标题。

conversation conversation conversation

初始化

提示

基于 IMKit 开发时,推荐继承 RCConversationViewController 类,创建自定义的会话页面。在排查或复现与会话页面相关的问题时,可以直接使用 RCConversationViewController 类。

调用 RCConversationViewController 类的初始化方法构建会话页面,设置 conversationTypetargetId 的值来创建一个单聊会话或群聊会话页面。

if (self.navigationController) {
    RCConversationViewController *conversationVC = [[RCConversationViewController alloc] initWithConversationType:conversationType targetId:targetId];
    [self presentViewController:navigationController animated:YES completion:nil];
}
              
已复制
1
2
3
4

参数 类型 说明
conversationType RCConversationType 会话类型
targetId NSString 会话 ID

用法

RCConversationViewController 类中,会话页面主要由三个部分组成:

属性 类型 说明
navigationBar UINavigationBar iOS 系统的导航栏
conversationMessageCollectionView RCBaseCollectionView 消息列表
chatSessionInputBarControl RCChatSessionInputBarControl 输入区域

标题栏

IMKit 的 RCConversationViewController 使用了系统的导航栏,可用于显示会话的标题,例如在群聊时显示群组名称,在一对一聊天中显示另一个用户的名称。IMKit 默认不设置会话标题,应用程序在实现了 IMKit 的信息提供者协议后,可以调用 IMKit 的方法获取存储的用户资料数据,自行通过 UIViewControllertitle 属性设置标题。详情请参见用户信息

开启输入状态功能后,在单聊会话中对方正在输入时,标题栏会被 SDK 修改为对方的输入状态。在搜索时,IMKit 会设置 UINavigationItemtitleView

标题栏组件左侧提供一个按钮,当点击左按钮时,会调用 leftBarButtonItemPressed 方法退出当前页面,应用程序可以重写该方法。标题栏右侧默认无按钮,您可以在自定义页面上添加按钮。您可以参考 SealTalk 源码中 RCDChatViewController.m 中的 rightBarButtonItemClicked 方法。

消息列表

IMKit 的 RCConversationViewController 的消息列表按时间顺序显示所有消息。

消息列表的视图是在各类型消息的展示模板中创建和自定义的,分别使用了 RCTextMessageCellRCFileMessageCellRCImageMessageCell 等继承自 RCMessageCell 的类来显示会话中不同类型的消息。您可以在页面提供的回调方法中自定义列表视图中的每个项目,或者重写 registerCustomCellsAndMessages 方法,在其中调用 registerClass:(Class)cellClass forMessageClass:(Class)messageClass; 方法注册自定义的消息展示模板。

属性 类型 说明
conversationMessageCollectionView RCBaseCollectionView 消息列表
conversationDataRepository NSMutableArray 数据源中存放的元素为聊天页面中消息 Cell 的数据模型,即 RCMessageModel 对象。
提示

要了解与消息列表有关的事件,请转到 页面事件监听

输入组件

RCChatSessionInputBarControl 是 IMKit 的消息输入组件,用户可以在其中键入和发送消息,支持文本、文件、语音、图像、视频等消息类型。输入区域的视图是在 RCChatSessionInputBarControl 中创建和控制的。

IMKit 已在 RCConversationViewController 中提供了部分输入组件相关的事件回调。

定制化

您可以通过修改 IMKit 全局配置和 RCConversationViewController 来自定义聊天页面。

在开始定制化之前,建议您首先继承 SDK 内置会话页面 RCConversationViewController,创建并实现自己的会话页面 View Controller。

#import <RongIMKit/RongIMKit.h>

@interface RCDChatViewController : RCConversationViewController
@property (nonatomic, assign) BOOL needPopToRootView;
@end

              
已复制
1
2
3
4
5
6

提示

会话页面中部分样式与行为受 IMKit 全局配置影响。如需了解全局配置,参见 配置指南

修改用户头像形状与大小

会话页面中显示的用户头像(指 RCMessageCell 上显示的头像)可以通过 IMKit 全局配置修改。

头像显示大小默认值为 40*40。修改方法如下:

RCKitConfigCenter.ui.globalMessagePortraitSize = CGSizeMake(40, 40);
              
已复制
1

头像形状默认为矩形,可修改为圆角显示。您还可以通过 IMKit 全局配置修改圆角曲率。

RCKitConfigCenter.ui.globalMessageAvatarStyle = RC_USER_AVATAR_CYCLE;
              
已复制
1

提示

修改 IMKit 全局配置会影响 IMKit 中所有用户头像的显示。

显示用户昵称

您可以配置 IMKit 会话页面收到的消息是否显示用户昵称。

该功能默认值为 YES,即显示。应用程序可根据不同会话类型,在进入会话页面前设置为 YES 或 NO。

@property (nonatomic, assign) BOOL displayUserNameInCell;
              
已复制
1

隐藏输入区的 Emoji 表情按钮

提示

SDK 从 5.2.3 开始支持禁用内置 Emoji。禁用后,表情面板不再显示 Emoji 标签页。

配置方式详见表情区域

修改默认拉取历史消息数

IMKit 在进入会话页面时和下拉页面时获取历史消息,默认拉取 10 条。优先从本地拉取,本地消息拉取完之后,如果已开通单群聊历史消息云存储功能,IMKit 会再从远端拉取历史消息。您可以修改默认拉取的历史消息数。

请在进入会话页面前设置。

//拉取本地会话。此属性已被弃用,请使用 defaultMessageCount
@property (nonatomic, assign) int defaultLocalHistoryMessageCount;
//拉取远端会话。此属性已被弃用,请使用 defaultMessageCount
@property (nonatomic, assign) int defaultRemoteHistoryMessageCount;

/*!
设置进入会话页面后以及每次拉取时获取消息的条数,默认是 10,defaultMessageCount 传入 1 < count <= 100 之间的数值。
    @discussion 此属性需要在viewDidLoad之前进行设置。
    */
@property (nonatomic, assign) int defaultMessageCount;
              
已复制
1
2
3
4
5
6
7
8
9
10

提示

您可以自助开通单群聊历史消息云存储功能,详见开通单群聊消息云存储服务

修改长按消息菜单中的删除行为

IMKit 会话页面默认已在长按消息弹出的菜单中实现了删除消息的选项。

alt

  • 如果 IMKit 版本 ≧ 5.6.3,默认实现的删除功能会删除本地消息,如果 App Key 已开通单群聊消息云端存储服务,则同步删除远端消息。
  • 如果 IMKit 版本 < 5.6.3,默认实现的删除功能仅删除本地消息,如果 App Key 已开通单群聊消息云端存储服务,不会删除服务端历史消息记录中的该条消息。用户再次获取历史消息、或触发离线消息补偿(卸载重装、换设备登录)时,该消息可能会再次出现在聊天页面中。如需删除远端消息,应用程序可修改删除按钮的默认行为。

如需修改 IMKit 会话页面中删除消息选项的默认行为,方法如下:

  • 如果 SDK 版本 ≧ 5.2.3,可设置 RCConversationViewControllerneedDeleteRemoteMessage 属性。YES 表示需要 IMKit 同步删除本地与远端消息。NO 表示仅从本地消息数据库中删除消息。

    /*!
        默认值为 NO 长按只删除本地消息,设置为 YES 时长按删除消息,会把远端的消息也进行删除
    */
    @property (nonatomic, assign) BOOL needDeleteRemoteMessage;
                  
    已复制
    1
    2
    3
    4

  • 如果 SDK 版本 < 5.2.3,需重写 RCConversationViewControllerdeleteMessage 方法,调用 super 删除本地消息,再调用 deleteRemoteMessage 删除远端消息接口,实现同时删除本地与远端消息。

    /*!
     删除消息并更新UI
    
     @param model 消息Cell的数据模型
     @discussion
     v5.2.3 之前 会话页面只删除本地消息,如果需要删除远端历史消息,需要
        1.重写该方法,并调用 super 删除本地消息
        2.调用删除远端消息接口,删除远端消息
     */
    - (void)deleteMessage:(RCMessageModel *)model;
                  
    已复制
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

提示

如果已有实现无法满足您的需求,可以直接使用 IMLib 提供的能力。具体的核心类、API 与 使用方法,详见 IMLib 文档 删除消息注意:IMLib 中的方法并不提供页面刷新能力,您需要根据业务需求自定义通知机制进行页面刷新。

隐藏消息 Cell 上的用户头像

IMKit SDK 默认显示用户头像。从 5.3.0 版本开始,IMKit SDK 支持隐藏 RCMessageCell 子类消息 Cell 上的用户头像。

重写会话页面的 willDisplayMessageCell 方法,修改 RCMessageCellshowPortrait 属性为 NO,即可隐藏头像。

- (void)willDisplayMessageCell:(RCMessageBaseCell *)cell atIndexPath:(NSIndexPath *)indexPath {
...
     if ([cell isKindOfClass:[RCMessageCell class]]) {
         RCMessageCell *c =  (RCMessageCell *)cell;
   // 隐藏头像
        c.showPortrait = NO;
    }
    ...
    [super willDisplayMessageCell:cell atIndexPath:indexPath];
}
              
已复制
1
2
3
4
5
6
7
8
9
10

自定义消息 Cell 显示

应用程序可以重写 RCConversationViewController 类的 willDisplayMessageCell 方法,在消息 Cell 展示前修改 UI。

例如,SDK 会话页面中文本消息已读的 UI 默认是一个“对勾”图标,如果希望修改为“已读”或“未读”,可以在 Cell 显示的时候,将 SDK 默认的图标移除,在对应位置添加文本“已读”或“未读”。应用程序还需要监听消息发送状态更新的通知,更新 cell。

下面代码仅以修改单聊文本消息 Cell 为例:

  1. 重写 cell 即将显示的方法,修改 UI:

    - (void)willDisplayMessageCell:(RCMessageBaseCell *)cell atIndexPath:(NSIndexPath *)indexPath {
        RCMessageModel *model = [self.conversationDataRepository objectAtIndex:indexPath.row];
        if ([cell isKindOfClass:[RCTextMessageCell class]] && model.conversationType == ConversationType_PRIVATE) {
            if (model.content && (model.sentStatus == SentStatus_READ ||model.sentStatus == SentStatus_SENT) && model.messageDirection == MessageDirection_SEND) {
                for (UIView *view in [((RCMessageCell *)cell).statusContentView subviews]) {
                    [view removeFromSuperview];
                }
                CGRect statusContentViewFrame = ((RCMessageCell *)cell).statusContentView.frame;
                UILabel *hasReadView = [[UILabel alloc] initWithFrame:CGRectMake(statusContentViewFrame.size.width-30,statusContentViewFrame.size.height-16, 30, 16)];
                hasReadView.textAlignment = NSTextAlignmentRight;
                hasReadView.font = [UIFont systemFontOfSize:12];
                hasReadView.textColor = [UIColor blackColor];
                hasReadView.tag = 1001;
                ((RCMessageCell *)cell).statusContentView.hidden = NO;
                if (model.sentStatus == SentStatus_READ) {
                    hasReadView.text = @"已读";
                    [((RCMessageCell *)cell).statusContentView addSubview:hasReadView];
                } else if (model.sentStatus == SentStatus_SENT) {
                    hasReadView.text = @"未读";
                    [((RCMessageCell *)cell).statusContentView addSubview:hasReadView];
                }
            }else{
                UIView *tagView = [((RCMessageCell *)cell).statusContentView viewWithTag:1001];
                if (tagView) {
                    [tagView removeFromSuperview];
                }
            }
        }
    }
                  
    已复制
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

  2. 监听消息发送状态更新的通知,执行刷新 cell 的方法:

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(messageCellUpdateSendingStatusEvent:)
                                                 name:KNotificationMessageBaseCellUpdateSendingStatus
                                               object:nil];
                  
    已复制
    1
    2
    3
    4

    - (void)messageCellUpdateSendingStatusEvent:(NSNotification *)notification {
        RCMessageCellNotificationModel *notifyModel = notification.object;
        if (notifyModel && ![notifyModel.actionName isEqualToString:CONVERSATION_CELL_STATUS_SEND_PROGRESS]) {
            [self reloadMessageCell:notifyModel.messageId];
        }
    }
                  
    已复制
    1
    2
    3
    4
    5
    6

    - (void)reloadMessageCell:(long)messageId {
        __weak typeof(self) __weakself = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            for (int i = 0; i < __weakself.conversationDataRepository.count; i++) {
                RCMessageModel *model = (__weakself.conversationDataRepository)[i];
                if (messageId == model.messageId) {
                    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
                    [__weakself hideCellReceiptView:indexPath withMessageModel:model];
                    break;
                }
            }
        });
    }
                  
    已复制
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    - (void)hideCellReceiptView:(NSIndexPath *)indexPath withMessageModel:(RCMessageModel *)model {
        UICollectionViewCell *__cell = [self.conversationMessageCollectionView cellForItemAtIndexPath:indexPath];
        //如果是空说明被回收了,重新dequeue一个cell,这里用来解决已读人数闪的问题
        if (__cell) {
            [self.conversationMessageCollectionView reloadItemsAtIndexPaths:@[ indexPath ]];
            if ([__cell isKindOfClass:[RCMessageCell class]]) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    ((RCMessageCell *)__cell).statusContentView.hidden = YES;
                });
            }
        }
    }
                  
    已复制
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

定位到指定消息

您可以配置在进入 IMKit 会话页面时定位到指定消息,用于点击消息搜索结果后进入会话页面等场景。

请在进入会话页面前设置需要定位的消息的 sendTime

@property (nonatomic, assign) long long locatedMessageSentTime;
              
已复制
1

其他定制化

您可以在以下文档中继续了解如何自定义会话页面:

提示

您可以直接参考 IMKit 源码中的 RCConversationViewController.h 了解有关 RCConversationViewController 类的更多属性和方法。

文档反馈
意见反馈

您的改进建议

意见反馈

问题类型

联系我们

提交工单

技术支持|集成使用|产品方案


商务咨询

7 x 24 小时

为您解答方案与报价问题

131 6185 6839

文档反馈