EaseUI 使用指南
簡介
EaseUI 封裝了 IM 功能常用的控件(如聊天會話、會話列表、聯系人列表)。旨在幫助開發者快速集成環信 SDK。
源碼地址:
快速集成
方法1:
-
集成 EaseUI 前的准備工作,首先需要 集成環信 iOS SDK;
-
參考 ChatDemo3.0 導入的方式,直接將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展示動態表情的效果圖:
選中消息的回調。
/*!
@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實現的消息長按效果演示:
將 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; }
聯系人列表頭像和昵稱的效果演示: