EaseUI 使用指南


EaseUI 封装了 IM 功能常用的控件(如聊天会话、会话列表、联系人列表)。旨在帮助开发者快速集成环信 SDK。

源码地址:

方法1:

  1. 集成 EaseUI 前的准备工作,首先需要集成环信 iOS SDK
  2. 参考 ChatDemo3.0 导入的方式,直接将EaseUI拖入项目里

将EaseUI拖入项目

方法2:

通过 Cocoapods 来集成 EaseUI:

pod 'EaseUI', :git => 'https://github.com/easemob/easeui-ios-cocoapods.git'

第 1 步:引入相关头文件 #import “EaseUI.h”。

第 2 步:在工程的 AppDelegate 中的以下方法中,调用 EaseUI 对应方法。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[EaseSDKHelper shareHelper] easemobApplication:application
                    didFinishLaunchingWithOptions:launchOptions
                                           appkey:@"douser#istore"
                                     apnsCertName:@"istore_dev"
                                      otherConfig:@{kSDKConfigEnableConsoleLogger:[NSNumber numberWithBool:YES]}];
    return YES;
}

创建聊天会话,传递用户或群 ID 和会话类型(EMConversationType)。

EaseMessageViewController *chatController = [[EaseMessageViewController alloc] initWithConversationChatter:@"8001" conversationType:eConversationTypeChat];

EaseUI 提供现成的聊天会话 ViewController,可以通过继承 EaseMessageViewController 方式(参考 ChatDemo-UI3.0 中 ChatViewController)实现对聊天会话的扩展。

也可以直接使用 EaseMessageViewController,通过 EaseMessageViewControllerDelegate 和 EaseMessageViewControllerDataSource 两个协议实现对 EaseMessageViewController 的扩展。

实现自定义聊天样式

EaseMessageViewControllerDelegate

获取自定义消息 cell,根据 messageModel,用户自己判断是否显示自定义消息 cell,如果返回 nil 会显示默认,如果返回 cell 会显示用户自定义消息 cell。

/*!
 @method
 @brief 获取消息自定义cell
 @discussion 用户根据messageModel判断是否显示自定义cell,返回nil显示默认cell,否则显示用户自定义cell
 @param tableView 当前消息视图的tableView
 @param messageModel 消息模型
 @result 返回用户自定义cell
 */
- (UITableViewCell *)messageViewController:(UITableView *)tableView
                       cellForMessageModel:(id<IMessageModel>)messageModel;

/*!
 @method
 @brief 获取消息cell高度
 @discussion 用户根据messageModel判断,是否自定义显示cell的高度
 @param viewController 当前消息视图
 @param messageModel 消息模型
 @param cellWidth 视图宽度
 @result 返回用户自定义cell
 */
- (CGFloat)messageViewController:(EaseMessageViewController *)viewController
           heightForMessageModel:(id<IMessageModel>)messageModel
                   withCellWidth:(CGFloat)cellWidth;
                   
//具体创建自定义Cell的样例:
- (UITableViewCell *)messageViewController:(UITableView *)tableView cellForMessageModel:(id<IMessageModel>)model
{
    //样例为如果消息是文本消息显示用户自定义cell
    if (model.bodyType == eMessageBodyType_Text) {
        NSString *CellIdentifier = [CustomMessageCell cellIdentifierWithModel:model];
        //CustomMessageCell为用户自定义cell,继承了EaseBaseMessageCell
        CustomMessageCell *cell = (CustomMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[CustomMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier model:model];
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
        }
        cell.model = model;
        return cell;
    }
    return nil;
}

- (CGFloat)messageViewController:(EaseMessageViewController *)viewController
           heightForMessageModel:(id<IMessageModel>)messageModel
                   withCellWidth:(CGFloat)cellWidth
{
    //样例为如果消息是文本消息使用用户自定义cell的高度
    if (messageModel.bodyType == eMessageBodyType_Text) {
        //CustomMessageCell为用户自定义cell,继承了EaseBaseMessageCell
        return [CustomMessageCell cellHeightWithModel:messageModel];
    }
    return 0.f;
}

通过自定义cell展示动态表情的效果图:

通过自定义cell展示动态表情的效果图

选中消息的回调。

/*!
 @method
 @brief 选中消息的回调
 @discussion 用户根据messageModel判断,是否自定义处理消息选中时间,返回YES为自定义处理,返回NO为默认处理
 @param viewController 当前消息视图
 @param messageModel 消息模型
 @result 是否采用自定义处理
 */
- (BOOL)messageViewController:(EaseMessageViewController *)viewController
        didSelectMessageModel:(id<IMessageModel>)messageModel;
        
//选中消息回调的样例:
- (BOOL)messageViewController:(EaseMessageViewController *)viewController
        didSelectMessageModel:(id<IMessageModel>)messageModel
{
    BOOL flag = NO;
    //样例为如果消息是文件消息用户自定义处理选中逻辑
    switch (messageModel.bodyType) {
        case eMessageBodyType_Image:
        case eMessageBodyType_Location:
        case eMessageBodyType_Voice:
        case eMessageBodyType_Video:
            break;
        case eMessageBodyType_File:
        {
            flag = YES;
            NSLog(@"用户自定义实现");
        }
            break;
        default:
            break;
    }
    return flag;
}

用户选中头像的回调。

/*!
 @method
 @brief 点击消息头像
 @discussion 获取用户点击头像回调
 @param viewController 当前消息视图
 @param messageModel 消息模型
 @result
 */
- (void)messageViewController:(EaseMessageViewController *)viewController
    didSelectAvatarMessageModel:(id<IMessageModel>)messageModel;
    
//获取用户点击头像回调的样例:
- (void)messageViewController:(EaseMessageViewController *)viewController
   didSelectAvatarMessageModel:(id<IMessageModel>)messageModel
{
    //UserProfileViewController用户自定义的个人信息视图
    //样例的逻辑是选中消息头像后,进入该消息发送者的个人信息
    UserProfileViewController *userprofile = [[UserProfileViewController alloc] initWithUsername:messageModel.message.from];
    [self.navigationController pushViewController:userprofile animated:YES];
}

录音按钮状态的回调

/*!
 @method
 @brief 底部录音功能按钮状态回调
 @discussion 获取底部录音功能按钮状态回调,根据EaseRecordViewType,用户自定义处理UI的逻辑
 @param viewController 当前消息视图
 @param recordView 录音视图
 @param type 录音按钮当前状态
 @result
 */
- (void)messageViewController:(EaseMessageViewController *)viewController
              didSelectRecordView:(UIView *)recordView
                withEvenType:(EaseRecordViewType)type;
                
//录音按钮状态的回调样例:
- (void)messageViewController:(EaseMessageViewController *)viewController
          didSelectRecordView:(UIView *)recordView
                 withEvenType:(EaseRecordViewType)type
{
    /*
        EaseRecordViewTypeTouchDown,//录音按钮按下
        EaseRecordViewTypeTouchUpInside,//手指在录音按钮内部时离开
        EaseRecordViewTypeTouchUpOutside,//手指在录音按钮外部时离开
        EaseRecordViewTypeDragInside,//手指移动到录音按钮内部
        EaseRecordViewTypeDragOutside,//手指移动到录音按钮外部
    */
    //根据type类型,用户自定义处理UI的逻辑
    switch (type) {
        case EaseRecordViewTypeTouchDown:
        {
            if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
                [(EaseRecordView *)self.recordView  recordButtonTouchDown];
            }
        }
            break;
        case EaseRecordViewTypeTouchUpInside:
        {
            if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
                [(EaseRecordView *)self.recordView recordButtonTouchUpInside];
            }
            [self.recordView removeFromSuperview];
        }
            break;
        case EaseRecordViewTypeTouchUpOutside:
        {
            if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
                [(EaseRecordView *)self.recordView recordButtonTouchUpOutside];
            }
            [self.recordView removeFromSuperview];
        }
            break;
        case EaseRecordViewTypeDragInside:
        {
            if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
                [(EaseRecordView *)self.recordView recordButtonDragInside];
            }
        }
            break;
        case EaseRecordViewTypeDragOutside:
        {
            if ([self.recordView isKindOfClass:[EaseRecordView class]]) {
                [(EaseRecordView *)self.recordView recordButtonDragOutside];
            }
        }
            break;
        default:
            break;
    }
}

EaseMessageViewControllerDataSource

用户判断消息是否允许长按,返回布尔值;如果用户允许长按,此方法为通知用户触发长按手势,返回布尔值,如果返回 NO 默认方式处理,返回 YES 采用用户自定义的处理方式。

/*!
 @method
 @brief 是否允许长按
 @discussion 获取是否允许长按的回调,默认是NO
 @param viewController 当前消息视图
 @param indexPath 长按消息对应的indexPath
 @result
 */
- (BOOL)messageViewController:(EaseMessageViewController *)viewController
   canLongPressRowAtIndexPath:(NSIndexPath *)indexPath;

/*!
 @method
 @brief 触发长按手势
 @discussion 获取触发长按手势的回调,默认是NO
 @param viewController 当前消息视图
 @param indexPath 长按消息对应的indexPath
 @result
 */
- (BOOL)messageViewController:(EaseMessageViewController *)viewController
   didLongPressRowAtIndexPath:(NSIndexPath *)indexPath;
   
//长按收拾回调样例:
- (BOOL)messageViewController:(EaseMessageViewController *)viewController
   canLongPressRowAtIndexPath:(NSIndexPath *)indexPath
{
    //样例给出的逻辑是所有cell都允许长按
    return YES;
}

- (BOOL)messageViewController:(EaseMessageViewController *)viewController
   didLongPressRowAtIndexPath:(NSIndexPath *)indexPath
{
    //样例给出的逻辑是长按cell之后显示menu视图
    id object = [self.dataArray objectAtIndex:indexPath.row];
    if (![object isKindOfClass:[NSString class]]) {
        EaseMessageCell *cell = (EaseMessageCell *)[self.tableView cellForRowAtIndexPath:indexPath];
        [cell becomeFirstResponder];
        self.menuIndexPath = indexPath;
        [self _showMenuViewController:cell.bubbleView andIndexPath:indexPath messageType:cell.model.bodyType];
    }
    return YES;
}

demo3.0实现的消息长按效果演示:

demo3.0实现的消息长按效果演示

将 EMMessage 类型转换为符合 <IMessageModel> 协议的类型,设置用户信息、消息显示用户昵称和头像。

/*!
 @method
 @brief 将EMMessage类型转换为符合<IMessageModel>协议的类型
 @discussion 将EMMessage类型转换为符合<IMessageModel>协议的类型,设置用户信息,消息显示用户昵称和头像
 @param viewController 当前消息视图
 @param EMMessage 聊天消息对象类型
 @result 返回<IMessageModel>协议的类型
 */
- (id<IMessageModel>)messageViewController:(EaseMessageViewController *)viewController
                           modelForMessage:(EMMessage *)message;
                           
//具体样例:
- (id<IMessageModel>)messageViewController:(EaseMessageViewController *)viewController
                           modelForMessage:(EMMessage *)message
{
    //用户可以根据自己的用户体系,根据message设置用户昵称和头像
    id<IMessageModel> model = nil;
    model = [[EaseMessageModel alloc] initWithMessage:message];
    model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];//默认头像
    model.avatarURLPath = @"";//头像网络地址
    model.nickname = @"昵称";//用户昵称
    return model;
}

聊天会话页面头像和昵称的效果演示:

聊天会话页面头像和昵称的效果演示

聊天会话样式自定义

聊天样式的自定义需要在 EaseMessageViewController 中 viewDidload 结束前设置。

//@property中带有UI_APPEARANCE_SELECTOR,都可以通过set的形式设置样式,具体可以参考EaseBaseMessageCell.h,EaseMessageCell.h

[[EaseBaseMessageCell appearance] setSendBubbleBackgroundImage:[[UIImage imageNamed:@"chat_sender_bg"] stretchableImageWithLeftCapWidth:5 topCapHeight:35]];//设置发送气泡
[[EaseBaseMessageCell appearance] setRecvBubbleBackgroundImage:[[UIImage imageNamed:@"chat_receiver_bg"] stretchableImageWithLeftCapWidth:35 topCapHeight:35]];//设置接收气泡

[[EaseBaseMessageCell appearance] setAvatarSize:40.f];//设置头像大小
[[EaseBaseMessageCell appearance] setAvatarCornerRadius:20.f];//设置头像圆角

EaseSDKHelper封装了发送消息的方法。

具体发送消息样例:

/*
 eMessageTypeChat            单聊消息
 eMessageTypeGroupChat       群聊消息
 eMessageTypeChatRoom        聊天室消息
*/

//发送文字消息
EMMessage *message = [EaseSDKHelper sendTextMessage:@"要发送的消息"
                                                   to:@"6001"//接收方
                                          messageType:eMessageTypeChat//消息类型
                                    requireEncryption:NO//是否加密
                                           messageExt:nil];//扩展信息
//发送位置消息
EMMessage *message = [EaseSDKHelper sendLocationMessageWithLatitude:35.1//经度
                                                          longitude:35.1//纬度
                                                            address:"地址"
                                                                 to:@"6001"//接收方
                                                        messageType:eMessageTypeChat//消息类型
                                                  requireEncryption:NO//是否加密
                                                         messageExt:nil];//扩展信息                                         

//发送图片消息      
EMMessage *message = [EaseSDKHelper sendImageMessageWithImage:image//发送的图片
                                                           to:@"6001"//接收方
                                                  messageType:eMessageTypeChat//消息类型
                                            requireEncryption:NO//是否加密
                                                   messageExt:nil//扩展信息
                                                      quality:1.0f//图片质量(0-1)
                                                     progress:nil];//进度回调

//发送音频消息                                           
EMMessage *message = [EaseSDKHelper sendVoiceMessageWithLocalPath:localPath//音频本地地址
                                                         duration:duration//语音的时长,单位是秒
                                                               to:@"6001"//接收方
                                                      messageType:eMessageTypeChat//消息类型
                                                requireEncryption:NO//是否加密
                                                       messageExt:nil//扩展信息
                                                         progress:nil];//进度回调
//发送视频文件消息
EMMessage *message = [EaseSDKHelper sendVideoMessageWithURL:url//发送的视频地址
                                                         to:@"6001"//接收方
                                                messageType:eMessageTypeChat//消息类型
                                          requireEncryption:NO//是否加密
                                                 messageExt:nil//扩展信息
                                                   progress:nil];//进度回调                                                                                                                                                                                                

会话列表初始化

EaseConversationListViewController *chatListVC = [[EaseConversationListViewController alloc] init];

会话列表扩展

EaseConversationListViewControllerDataSource

用户根据conversationModel实现,实现自定义会话中最后一条消息文案的显示内容。

/*!
 @method
 @brief 获取最后一条消息显示的内容
 @discussion 用户根据conversationModel实现,实现自定义会话中最后一条消息文案的显示内容
 @param conversationListViewController 当前会话列表视图
 @param IConversationModel 会话模型
 @result 返回用户最后一条消息显示的内容
 */
- (NSString *)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
      latestMessageTitleForConversationModel:(id<IConversationModel>)conversationModel;


/*!
 @method
 @brief 获取最后一条消息显示的时间
 @discussion 用户可以根据conversationModel,自定义实现会话列表中时间文案的显示内容
 @param conversationListViewController 当前会话列表视图
 @param IConversationModel 会话模型
 @result 返回用户最后一条消息时间的显示文案
 */
- (NSString *)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
       latestMessageTimeForConversationModel:(id<IConversationModel>)conversationModel;
       
//最后一条消息展示内容样例    
- (NSString *)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
      latestMessageTitleForConversationModel:(id<IConversationModel>)conversationModel
{
    //用户获取最后一条message,根据message的messageBodyType展示显示最后一条message对应的文案
    NSString *latestMessageTitle = @"";
    EMMessage *lastMessage = [conversationModel.conversation latestMessage];
    if (lastMessage) {
        id<IEMMessageBody> messageBody = lastMessage.messageBodies.lastObject;
        switch (messageBody.messageBodyType) {
            case eMessageBodyType_Image:{
                latestMessageTitle = NSLocalizedString(@"message.image1", @"[image]");
            } break;
            case eMessageBodyType_Text:{
                // 表情映射
                NSString *didReceiveText = [EaseConvertToCommonEmoticonsHelper
                                            convertToSystemEmoticons:((EMTextMessageBody *)messageBody).text];
                latestMessageTitle = didReceiveText;
            } break;
            case eMessageBodyType_Voice:{
                latestMessageTitle = NSLocalizedString(@"message.voice1", @"[voice]");
            } break;
            case eMessageBodyType_Location: {
                latestMessageTitle = NSLocalizedString(@"message.location1", @"[location]");
            } break;
            case eMessageBodyType_Video: {
                latestMessageTitle = NSLocalizedString(@"message.video1", @"[video]");
            } break;
            case eMessageBodyType_File: {
                latestMessageTitle = NSLocalizedString(@"message.file1", @"[file]");
            } break;
            default: {
            } break;
        }
    }
    
    return latestMessageTitle;
}

//最后一条消息展示时间样例  
- (NSString *)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
       latestMessageTimeForConversationModel:(id<IConversationModel>)conversationModel
{
    //用户获取最后一条message,根据lastMessage中timestamp,自定义时间文案显示(例如:"1分钟前","14:20")
    NSString *latestMessageTime = @"";
    EMMessage *lastMessage = [conversationModel.conversation latestMessage];;
    if (lastMessage) {
        latestMessageTime = [NSDate formattedTimeFromTimeInterval:lastMessage.timestamp];
    }
    return latestMessageTime;
}

会话列表最后一条消息和时间显示的效果演示:

会话列表最后一条消息和时间显示的效果演示

EaseConversationListViewControllerDelegate

点击会话列表用户可以根据 conversationModel 自定义处理逻辑。

/*!
 @method
 @brief 获取点击会话列表的回调
 @discussion 获取点击会话列表的回调后,点击会话列表用户可以根据conversationModel自定义处理逻辑
 @param conversationListViewController 当前会话列表视图
 @param IConversationModel 会话模型
 @result
 */
- (void)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
            didSelectConversationModel:(id<IConversationModel>)conversationModel;
            
//会话列表点击的回调样例
- (void)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
            didSelectConversationModel:(id<IConversationModel>)conversationModel
{
    //样例展示为根据conversationModel,进入不同的会话ViewController
    if (conversationModel) {
        EMConversation *conversation = conversationModel.conversation;
        if (conversation) {
            //demo的逻辑,如果是与机器人的会话,进入机器人ViewController,否侧进入正常会话ViewController
            if ([[RobotManager sharedInstance] isRobotWithUsername:conversation.chatter]) {
                RobotChatViewController *chatController = [[RobotChatViewController alloc] initWithConversationChatter:conversation.chatter conversationType:conversation.conversationType];
                chatController.title = [[RobotManager sharedInstance] getRobotNickWithUsername:conversation.chatter];
                [self.navigationController pushViewController:chatController animated:YES];
            } else {
                ChatViewController *chatController = [[ChatViewController alloc] initWithConversationChatter:conversation.chatter conversationType:conversation.conversationType];
                chatController.title = conversationModel.title;
                [self.navigationController pushViewController:chatController animated:YES];
            }
        }
    }
}

联系人列表初始化

EaseUsersListViewController *listViewController = [[EaseUsersListViewController alloc] init];

联系人列表扩展

需要实现EMUserListViewControllerDataSource

根据buddy获取用户自定信息,联系人列表里展示昵称和头像。

/*!
 @method
 @brief 获取用户模型
 @discussion 根据buddy获取用户自定信息,联系人列表里展示昵称和头像
 @param userListViewController 当前联系人视图
 @param buddy 好友的信息描述类
 @result 返回用户模型
 */
- (id<IUserModel>)userListViewController:(EaseUsersListViewController *)userListViewController
                           modelForBuddy:(EMBuddy *)buddy;
                           
//联系人列表扩展样例
- (id<IUserModel>)userListViewController:(EaseUsersListViewController *)userListViewController
                           modelForBuddy:(EMBuddy *)buddy
{
    //用户可以根据自己的用户体系,根据buddy设置用户昵称和头像
    id<IUserModel> model = nil;
    model = [[EaseUserModel alloc] initWithBuddy:buddy];
    model.avatarURLPath = @"";//头像网络地址
    model.nickname = @"昵称";//用户昵称
    return model;
}

联系人列表头像和昵称的效果演示:

联系人列表头像和昵称的效果演示


上一页:APNS内容解析

下一页:iOS QA