目錄
1. 概念
2. 集成融云
3. 會話列表頁(自定義CELL)
聊天頁(自定義消息痒留、CELL)
4. 用戶信息谴麦、小角標、消息類型
1. 概念
融云SDK的系統(tǒng)架構
IMKit
封裝了各種界面對象伸头。
用于快速集成匾效。
IMLib
封裝了通信能力和 Conversation,Message 等各種對象恤磷,提供基本通信能力庫面哼。
用于高度自定義UI野宜。
Protocol
融云的核心協(xié)議棧,使用融云自定義的私有二進制協(xié)議
相關名詞
通知
提示用戶的方式魔策,消息匈子、角標、彈窗
推送
從服務器發(fā)送消息到前端的技術
廣播
向所有客戶端發(fā)送一條消息
應用切換到后臺2分鐘后闯袒,將自動切斷與服務器的連接旬牲,變?yōu)殡x線狀態(tài)。
用戶A向用戶B發(fā)送消息:
首先搁吓,應用會將消息傳遞給融云服務器原茅,如果用戶B在線則將消息傳給用戶B,如果用戶B不在線堕仔,則將推送消息給用戶B綁定的設備擂橘。
2. 集成融云
第一步:
蘋果開發(fā)者中心創(chuàng)建Identify,打開推送功能摩骨,創(chuàng)建測試通贞、發(fā)布推送證書,下載雙擊安裝并導出為p12格式恼五。
融云開發(fā)者官網創(chuàng)建應用昌罩,上傳2個p12證書,拿到Key和Sect
第二步:
項目|CapBilities|打開通知功能灾馒,并勾選Background Mode下的Remote notifications
cocoaPods 引入
# 聊天
pod 'RongCloudIM/IMLib'
pod 'RongCloudIM/IMKit'
第三步:
AppDelegate配置
#import <RongIMKit/RongIMKit.h>
didFinish方法中+注冊融云茎用、注冊APNs(注冊成功后將token傳給融云)
// 注冊融云
[[RCIM sharedRCIM] initWithAppKey:RongAppKeyStore];
// 遠程推送的內容(點擊通知欄的通知,且當App被殺死時)
NSDictionary *remoteNotificationUserInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
實現前臺睬罗、后臺收到消息后的回調(處于前臺轨功、處于后臺點擊通知會分別調用相應的方法,程序銷毀點擊通知會將通知消息存放在didFinishLaunchingWithOptions的launchOptions參數中)
登錄融云
// 鏈接服務器---昵稱/頭像/id
YTAccount *acount=[YTAccountTool account];
NSDictionary *paramDic=@{@"userId":acount.userId,@"portraitUri":acount.iconurl,@"name":acount.name};
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 下面是按融云的提示寫的
NSString * timestamp = [[NSString alloc] initWithFormat:@"%ld",(NSInteger)[NSDate timeIntervalSinceReferenceDate]];
NSString * nonce = [NSString stringWithFormat:@"%d",arc4random()];
NSString * appkey = RongAppKeyStore;
NSString *SignatureWillMD5 = [NSString stringWithFormat:@"%@%@%@",appkey,nonce,timestamp];//這個要加密
// NSString *Signature = [self MD5String:@"aaaaa"];
// 以下拼接請求內容
[manager.requestSerializer setValue:appkey forHTTPHeaderField:@"App-Key"];
[manager.requestSerializer setValue:nonce forHTTPHeaderField:@"Nonce"];
[manager.requestSerializer setValue:timestamp forHTTPHeaderField:@"Timestamp"];
[manager.requestSerializer setValue:SignatureWillMD5 forHTTPHeaderField:@"Signature"];
[manager.requestSerializer setValue:RongAppSecStore forHTTPHeaderField:@"appSecret"];
[manager.requestSerializer setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
// 開始請求
[manager POST:kCustomerRCTOKEN parameters:paramDic progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSString *token=responseObject[@"token"];
// success/error/tokenIncorrent三者只會調用一個且僅調用一次
[[RCIM sharedRCIM]connectWithToken:token success:^(NSString *userId) {
// success
NSLog(@"登錄融云成功,token:%@",token);
/*
// 設置當前登陸用戶
RCUserInfo *userInfo=[RCUserInfo new];
[userInfo setName:@""];
[userInfo setUserId:@""];
[userInfo setPortraitUri:@""];
[[RCIM sharedRCIM]setCurrentUserInfo:userInfo];
[RCIM sharedRCIM].receiveMessageDelegate=self;
*/
} error:^(RCConnectErrorCode status) {
// 出錯了
NSLog(@"登錄錯誤碼%ld",(long)status);
// 除了以下錯誤容达,融云都會自動重練
// AppKey出錯 :RC_CONN_ID_REJECT = 31002
// Token無效(前后臺不一致古涧,token過期) :RC_CONN_TOKEN_INCORRECT = 31004
// BundleID 不正確 :RC_CONN_PACKAGE_NAME_INVALID = 31007
// App Key 被封禁或已刪除 :RC_CONN_APP_BLOCKED_OR_DELETED = 31008
// 用戶被封禁 :RC_CONN_USER_BLOCKED = 31009
// 當前用戶在其他設備上登錄,此設備被踢下線 :RC_DISCONN_KICK = 31010
// SDK 沒有初始化 :RC_CLIENT_NOT_INIT = 33001
// 接口調用時傳入的參數錯誤 :RC_INVALID_PARAMETER = 33003,RC_INVALID_ARGUMENT = -1000
} tokenIncorrect:^{
// 獲取token失敗花盐,再次獲取一次
NSLog(@"token error");
//
[[RCIM sharedRCIM]connectWithToken:token success:^(NSString *userId) {
//
NSLog(@"登錄融云成功,token:%@",token);
[RCIM sharedRCIM].receiveMessageDelegate=self;
} error:^(RCConnectErrorCode status) {
//
NSLog(@"登錄錯誤碼%ld",(long)status);
} tokenIncorrect:^{
// 獲取token失敗羡滑,不用再次調用(避免循環(huán)調用)
NSLog(@"token error");
}];
}];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"獲取融云TOKEN失敗%@",error);
}];
斷開融云
一般不需要手動斷開,因為:SDK在前后臺切換或者網絡出現異常都會自動斷開或重連算芯。
方式一:斷開連接之后是否接收遠程推送
[[RCIM sharedRCIM]disconnect:true];
方式二:斷開連接之后仍接收遠程推送
[[RCIM sharedRCIM]disconnect];
方式三:斷開連接之后不接收遠程推送
[[RCIM sharedRCIM]logout];
會話列表頁
繼承RCConversationListViewController (類似tableViewController)
聊天內容頁
繼承RCConversationViewController (類似collectionViewController)
3. IMKit
會話列表頁自定義UI樣式:
1柒昏、覆寫willDisplayConversationTableCell方法(不能高度自定制)
2、直接自定義CELL (允許高度自定制)
3.1 會話列表頁(自定義CELL)
1.會話列表頁 YTConversationListController
YTConversationListController.h
#import <RongIMKit/RongIMKit.h>
@interface YTConversationListController : RCConversationListViewController
@end
YTConversationListController.m
#import "YTConversationListController.h"
#import "YTMessageViewController.h"
#import "YTRCListCell.h"
@interface YTConversationListController ()
@end
@implementation YTConversationListController
// 更新未讀消息角標
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.conversationListTableView reloadData];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
}
// UI
-(void)setupUI{
self.navigationItem.title=@"消息";
// 設置需要顯示的會話類型
[self setDisplayConversationTypes:@[@(ConversationType_PRIVATE)]];
ConversationType_PRIVATE 單聊
ConversationType_DISCUSSION 討論組
ConversationType_CHATROOM 聊天室
ConversationType_GROUP 群組
ConversationType_SYSTEM 系統(tǒng)會話(只能由服務器發(fā)起)
ConversationType_APPSERVICE 應用內公眾服務會話
ConversationType_PUBLICSERVICE 跨應用公眾服務會話
ConversationType_PUSHSERVICE 推送服務會話
ConversationType_CUSTOMERSERVICE 客服
// 設置需要顯示的聚合會話類型
[self setCollectionConversationType:@[@(ConversationType_DISCUSSION),
@(ConversationType_GROUP)]];
// 是否顯示 無網絡時的提示(默認:true)
[self setIsShowNetworkIndicatorView:false];
// ?沒有消息時顯示的bgView
[self setEmptyConversationView:bgView];
// cell bgColor
[self setCellBackgroundColor:[UIColor blueColor]];
// 置頂Cell bgColor
[self setTopCellBackgroundColor:[UIColor redColor]];
// conversationListTableView繼承自UITableView
// 設置 頭也祠、尾視圖
[self.conversationListTableView setTableHeaderView:[UIView new]];
[self.conversationListTableView setTableFooterView:[UIView new]];
// 分割線
[self.conversationListTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
// bgColor
[self.conversationListTableView setBackgroundColor:[UIColor whiteColor]];
// 內間距
[self.conversationListTableView setContentInset:UIEdgeInsetsMake(10, 0, 0, 0)];
// 自定義CELL
[self.conversationListTableView registerClass:[YTRCListCell class] forCellReuseIdentifier:NSStringFromClass([YTRCListCell class])];
/*
// 頭像style (默認昙楚;矩形,圓形)
[[RCIM sharedRCIM]setGlobalMessageAvatarStyle:RC_USER_AVATAR_CYCLE];
// 頭像size(默認:46*46诈嘿,必須>36*36)
[[RCIM sharedRCIM]setGlobalMessagePortraitSize:CGSizeMake(46, 46)];
// 個人信息,自定義后不再有效堪旧。沒自定義CELL時可使用削葱,并實現getUserInfoWithUserId代理方法(詳見聊天頁)
[[RCIM sharedRCIM]setUserInfoDataSource:self];
*/
// 推送
[self setupPush];
}
// 如果關閉推送,彈框去設置推送
-(void)setupPush{
//
if ([[UIDevice currentDevice].systemVersion floatValue]>=8.0f) {
UIUserNotificationSettings *setting = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (UIUserNotificationTypeNone == setting.types) {
//
NSLog(@"推送關閉 8.0");
YTRecommendView *recV=[YTRecommendView ViewWithTitle:@"您關閉了系統(tǒng)通知" withContent:@"開啟系統(tǒng)通知淳梦,以免錯過重要消息" withImgName:@"orderG" withButtonTitle:@"去開啟"];
recV.goBlock=^{
// 跳手機系統(tǒng)的 通知設置
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
};
}else{
NSLog(@"推送打開 8.0");
}
}else{
//
UIRemoteNotificationType type = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if(UIRemoteNotificationTypeNone == type){
NSLog(@"推送關閉");
YTRecommendView *recV=[YTRecommendView ViewWithTitle:@"您關閉了系統(tǒng)通知" withContent:@"開啟系統(tǒng)通知析砸,以免錯過重要消息" withImgName:@"orderG" withButtonTitle:@"去開啟"];
recV.goBlock=^{
// 跳手機系統(tǒng)的 通知設置
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
};
}else{
NSLog(@"推送打開");
}
}
}
#pragma mark 自定義CELL 以下方法按需求去實現
// dataSource (修改數據源來修改UI)(在上方新增自定義cell)
// 要更換為RC_CONVERSATION_MODEL_TYPE_CUSTOMIZATION否則不調用覆寫方法)
-(NSMutableArray *)willReloadTableData:(NSMutableArray *)dataSource {
RCConversationModel *model=dataSource[0];
[model setConversationModelType:RC_CONVERSATION_MODEL_TYPE_CUSTOMIZATION];
// 修改model
// ...
return dataSource;
}
// height
- (CGFloat)rcConversationListTableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 95;
}
// 每行的編輯格式
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row==0){
return UITableViewCellEditingStyleNone;
}else{
return UITableViewCellEditingStyleDelete;
}
}
// 編輯后調用
-(void)rcConversationListTableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
// 服務器刪除
RCConversationModel *model = self.conversationListDataSource[indexPath.row];
[[RCIMClient sharedRCIMClient] removeConversation:ConversationType_PRIVATE
targetId:model.targetId];
// UI本地刪除
[self.conversationListDataSource removeObjectAtIndex:indexPath.row];
[self.conversationListTableView reloadData];
}
// cell
- (RCConversationBaseCell *)rcConversationListTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// model-dataSource
RCConversationModel *model = self.conversationListDataSource[indexPath.row];
// 請求個人信息---自己的服務器
__weak YTConversationListController *weakSelf = self;
if(!model.extend && model.lastestMessage){
//
[LHAFNetWork POST:YTBaseUrl(kCustomerMessage) params:@{@"userId":model.targetId} success:^(NSURLSessionDataTask *task, id responseObject) {
if(SUCCESS){
//
NSDictionary *dic=(NSDictionary *)responseObject[@"data"];
if([dic isEqual:[NSNull null]]){
//
return;
}
//
YTAccount *acount=[YTAccountTool account];
acount.userId=dic[@"id"];
acount.name=dic[@"name"];
acount.iconurl=YTImgBaseUrl(dic[@"photo"]);
//
RCUserInfo *userInfo = [[RCUserInfo alloc]init];
userInfo.userId=acount.userId;
userInfo.name=acount.name;
userInfo.portraitUri=acount.iconurl;
model.extend=userInfo;
[weakSelf.conversationListTableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
} fail:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"%@",error);
}];
}
//
YTRCListCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([YTRCListCell class])];
[cell setConvM:model withIsFirst:indexPath.row==0?true:false withUserInfo:model.extend];
return cell;
}
// will display
- (void)willDisplayConversationTableCell:(RCConversationBaseCell *)cell atIndexPath:(NSIndexPath *)indexPath{
// 選中不高亮(在cell中設置無效)
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
// 獲取Model會話類型,做其它處理
RCConversationModel *model=cell.model;
if(model.conversationModelType != RC_CONVERSATION_MODEL_TYPE_CUSTOMIZATION){
// 必須強制轉換
RCConversationCell *RCcell = (RCConversationCell *)cell;
// 是否未讀消息數(頭像右上角爆袍,默認:true)
[RCcell setIsShowNotificationNumber:true];
RCcell.conversationTitle.font = [UIFont fontWithName:@"PingFangSC-Light" size:18];
RCcell.messageContentLabel.font = [UIFont fontWithName:@"PingFangSC-Light" size:16];
RCcell.messageCreatedTimeLabel.font = [UIFont fontWithName:@"PingFangSC-Light" size:14];
}
}
// 點擊cell跳-重寫RCConversationListViewController的onSelectedTableRow事件
- (void)onSelectedTableRow:(RCConversationModelType)conversationModelType
conversationModel:(RCConversationModel *)model
atIndexPath:(NSIndexPath *)indexPath {
// 跳轉到 聊天內容頁
YTMessageViewController *conversationVC = [YTMessageViewController new];
conversationVC.conversationType = model.conversationType; // 數據源type:單聊...
conversationVC.title = ((RCUserInfo *)model.extend).name; // 標題:對方昵稱
conversationVC.targetId = model.targetId; // 對方ID
conversationVC.isNoHave=true;
[self.navigationController pushViewController:conversationVC animated:YES];
}
// onRCIMReceiveMessage首繁。收到消息---更新未讀角標
-(void)onRCIMReceiveMessage:(RCMessage *)message left:(int)left{
//
[self.conversationListTableView reloadData];
}
@end
- 自定義CELL頁 YTRCListCell
YTRCListCell.h
#import <RongIMKit/RongIMKit.h>
@class RCConversationModel;
@interface YTRCListCell : RCConversationBaseCell
-(void)setConvM:(RCConversationModel *)convM withIsFirst:(BOOL)isF withUserInfo:(RCUserInfo *)info;
@end
YTRCListCell.m
#import "YTRCListCell.h"
#import "YTConvertToTimeTool.h"
@interface YTRCListCell()
@property (nonatomic,strong) UILabel *nameL;
@property (nonatomic,strong) UILabel *contentL;
@property (nonatomic,strong) UILabel *timeL;
@property (nonatomic,strong) UIImageView *photoImgV;
@property (nonatomic,strong) UILabel *numL;
@end
@implementation YTRCListCell
//
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
//
if(self=[super initWithStyle:style reuseIdentifier:reuseIdentifier]){
//
[self setSelectionStyle:UITableViewCellSelectionStyleNone]; // 此處設置無效,willdisplay中設置
[self setupUI];
}
return self;
}
//
-(void)setupUI{
// bg
UIImageView *bgImgV=[UIImageView new];
[bgImgV setImage:[UIImage imageNamed:@"messageList"]];
[bgImgV setContentMode:UIViewContentModeTop];
[self addSubview:bgImgV];
[bgImgV autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsMake(0, 10, 10, 10)];
// photo
UIImageView *photoImgV=[UIImageView new];
_photoImgV=photoImgV;
[photoImgV.layer setMasksToBounds:true];
[photoImgV.layer setCornerRadius:50/2];
[bgImgV addSubview:photoImgV];
[photoImgV autoAlignAxisToSuperviewAxis:ALAxisHorizontal];
[photoImgV autoSetDimension:ALDimensionWidth toSize:50];
[photoImgV autoSetDimension:ALDimensionHeight toSize:50];
[photoImgV autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:5];
// name
UILabel *nameL=[UILabel new];
_nameL=nameL;
[nameL setFont:YTFONT_PF_S(15)];
[nameL setTextColor:YTColorFromRGB(0x414141)];
[bgImgV addSubview:nameL];
[nameL autoPinEdge:ALEdgeTop toEdge:ALEdgeTop ofView:photoImgV];
[nameL autoPinEdge:ALEdgeLeft toEdge:ALEdgeRight ofView:photoImgV withOffset:5];
[nameL autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:80];
// content
UILabel *contentL=[UILabel new];
_contentL=contentL;
[contentL setFont:YTFONT_PF(13)];
[contentL setTextColor:YTColorFromRGB(0x414141)];
[bgImgV addSubview:contentL];
[contentL autoPinEdge:ALEdgeLeft toEdge:ALEdgeLeft ofView:nameL];
[contentL autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:photoImgV];
[contentL autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:36];
// time
UILabel *timeL=[UILabel new];
_timeL=timeL;
[timeL setFont:YTFONT_PF(15)];
[timeL setTextColor:YTColorFromRGB(0x5d5d5d)];
[bgImgV addSubview:timeL];
[timeL autoAlignAxis:ALAxisHorizontal toSameAxisOfView:nameL];
[timeL autoPinEdgeToSuperviewEdge:ALEdgeRight withInset:15];
//
UILabel *numL=[UILabel new];
_numL=numL;
[numL setTextAlignment:NSTextAlignmentCenter];
[numL setBackgroundColor:[UIColor redColor]];
[numL.layer setCornerRadius:15/2];
[numL.layer setMasksToBounds:true];
[numL setFont:YTFONT_PF(12)];
[numL setTextColor:YTColorFromRGB(0xffffff)];
[bgImgV addSubview:numL];
[numL autoPinEdge:ALEdgeRight toEdge:ALEdgeRight ofView:timeL];
[numL autoSetDimension:ALDimensionWidth toSize:15];
[numL autoSetDimension:ALDimensionHeight toSize:15];
[numL autoAlignAxis:ALAxisHorizontal toSameAxisOfView:contentL];
}
-(void)setConvM:(RCConversationModel *)convM withIsFirst:(BOOL)isF withUserInfo:(RCUserInfo *)info{
//
[_timeL setText:[YTConvertToTimeTool ConvertChatMessageTime:(convM.receivedTime>convM.sentTime?convM.receivedTime:convM.sentTime)/1000]];
[_numL setText:[NSString stringWithFormat:@"%ld",convM.unreadMessageCount]];
if(convM.unreadMessageCount==0){
[_numL setHidden:true];
}else{
[_numL setHidden:false];
}
if(isF){
//
NSString *titleStr=@"YOTO 官方 ";
NSMutableAttributedString *muT=[[NSMutableAttributedString alloc]initWithString:titleStr attributes:@{NSForegroundColorAttributeName:YTColorFromRGB(0x414141),NSFontAttributeName:YTFONT_PF_S(15)}];
[muT addAttributes:@{NSFontAttributeName:YTFONT_PF_S(12),NSForegroundColorAttributeName:[UIColor whiteColor],NSBackgroundColorAttributeName:YTMainColor} range:[titleStr rangeOfString:@" 官方 "]];
[_nameL setAttributedText:muT];
//
[_photoImgV setImage:[UIImage imageNamed:@"official"]];
if(convM.lastestMessage){
[_contentL setText:((RCTextMessage *)convM.lastestMessage).content];
}
}else if(info){
[_photoImgV sd_setImageWithURL:[NSURL URLWithString:info.portraitUri] placeholderImage:[UIImage imageNamed:@"userList"]];
[_nameL setText:info.name];
[_contentL setText:((RCTextMessage *)convM.lastestMessage).content];
}
}
@end
3.2 聊天頁
- 聊天內容頁 YTMessageViewController
YTMessageViewController.h
#import <RongIMKit/RongIMKit.h>
@interface YTMessageViewController : RCConversationViewController
@end
YTMessageViewController.m
@interface YTMessageViewController ()<RCIMUserInfoDataSource,YTCollectionHeadReusableViewProtocol,YTDZHeadCollectionReusableViewProtocol>
@property (nonatomic, strong) RCUserInfo *userInfo2;
@end
@implementation YTMessageViewController
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[IQKeyboardManager sharedManager].enable=false;
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[IQKeyboardManager sharedManager].enable=true;
}
//
- (void)viewDidLoad {
[super viewDidLoad];
/*
conversationMessageCollectionView是繼承自UICollectionView
//
cell 為 RCMessageBaseCell陨囊,Model 為 RCMessageModel
// 會話類型
RCConversationType type=self.conversationType;
// targetId
NSString *targetId=self.targetId;
*/
// bgColor
[self.conversationMessageCollectionView setBackgroundColor:[UIColor whiteColor]];
// 頭視圖
if(_isCustome){
[self.conversationMessageCollectionView registerClass:[YTDZHeadCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"YTDZHeadCollectionReusableView"];
}else{
[self.conversationMessageCollectionView registerClass:[YTCollectionHeadReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"YTCollectionHeadReusableView"];
}
// (用來設置 管家是否與用戶溝通)
if (!_isNoHave) {
[self message];
}
// 如果沒有歷史消息弦疮,發(fā)送初始消息
if(self.conversationDataRepository.count==0 && ![self.targetId isEqualToString:@"111111111111111"]){
//
[self sendInitMessage];
}
// 個人信息
[[RCIM sharedRCIM]setUserInfoDataSource:self];
// iconStyle 無效
// [[RCIM sharedRCIM]setGlobalConversationAvatarStyle:RC_USER_AVATAR_CYCLE];
}
// cell樣式
-(void)willDisplayMessageCell:(RCMessageBaseCell *)cell atIndexPath:(NSIndexPath *)indexPath{
//
if([cell isMemberOfClass:[RCTextMessageCell class]]){
// 內容
RCTextMessageCell *textC=(RCTextMessageCell *)cell;
UILabel *textL=(UILabel *)textC.textLabel;
[textL setTextColor:YTColorFromRGB(0x414141)];
[textL setFont:YTFONT_PF(15)];
// 頭像切圓
UIImageView *portraitImageView = (UIImageView *)textC.portraitImageView;
portraitImageView.layer.cornerRadius = CGRectGetHeight(portraitImageView.frame)/2;
//
CGRect frame=portraitImageView.frame;
frame.origin.y=CGRectGetMaxY(textC.bubbleBackgroundView.frame)-CGRectGetHeight(portraitImageView.frame)+6;
portraitImageView.frame=frame;
}else if([cell isMemberOfClass:[RCRichContentMessageCell class]]){
//
RCRichContentMessageCell *rCell=(RCRichContentMessageCell *)cell;
// 頭像切圓
UIImageView *portraitImageView = (UIImageView *)rCell.portraitImageView;
portraitImageView.layer.cornerRadius = CGRectGetHeight(portraitImageView.frame)/2;
//
CGRect frame=portraitImageView.frame;
frame.origin.y=CGRectGetMaxY(rCell.bubbleBackgroundView.frame)-CGRectGetHeight(portraitImageView.frame)+6;
portraitImageView.frame=frame;
}else if([cell isMemberOfClass:[RCVoiceMessageCell class]]){
RCVoiceMessageCell *rCell=(RCVoiceMessageCell *)cell;
// 頭像切圓
UIImageView *portraitImageView = (UIImageView *)rCell.portraitImageView;
portraitImageView.layer.cornerRadius = CGRectGetHeight(portraitImageView.frame)/2;
//
CGRect frame=portraitImageView.frame;
frame.origin.y=CGRectGetMaxY(rCell.bubbleBackgroundView.frame)-CGRectGetHeight(portraitImageView.frame)+6;
portraitImageView.frame=frame;
}else if([cell isMemberOfClass:[RCImageMessageCell class]]){
RCImageMessageCell *rCell=(RCImageMessageCell *)cell;
// 頭像切圓
UIImageView *portraitImageView = (UIImageView *)rCell.portraitImageView;
portraitImageView.layer.cornerRadius = CGRectGetHeight(portraitImageView.frame)/2;
//
CGRect frame=portraitImageView.frame;
frame.origin.y=CGRectGetMaxY(rCell.pictureView.frame)-CGRectGetHeight(portraitImageView.frame)+6;
portraitImageView.frame=frame;
}else if([cell isMemberOfClass:[RCLocationMessageCell class]]){
RCLocationMessageCell *rCell=(RCLocationMessageCell *)cell;
// 頭像切圓
UIImageView *portraitImageView = (UIImageView *)rCell.portraitImageView;
portraitImageView.layer.cornerRadius = CGRectGetHeight(portraitImageView.frame)/2;
//
CGRect frame=portraitImageView.frame;
frame.origin.y=CGRectGetMaxY(rCell.pictureView.frame)-CGRectGetHeight(portraitImageView.frame)+6;
portraitImageView.frame=frame;
}
}
// 點擊鏈接CELL時跳轉
-(void)didTapUrlInMessageCell:(NSString *)url model:(RCMessageModel *)model{
/* 管家端
// id-是否定制
NSLog(@"%@",url);
NSArray *arr=[url componentsSeparatedByString:@"-"];
NSString *recId=arr[0];
BOOL isDZ=[arr[1] boolValue];
// 跳
*/
//
// 客戶端跳
YTRouteDetailViewController *routeC=[YTRouteDetailViewController new];
if(_routeModel){
routeC.themeId=_routeModel.themeId;
}else{
NSArray *arr=[url componentsSeparatedByString:@"-"];
NSString *recId=arr[0];
routeC.themeId=recId;
}
[self.navigationController pushViewController:routeC animated:true];
}
// headSize
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section{
//
//
if(_isNoHave){
return CGSizeZero;
}else{
if(_isCustome){
return CGSizeMake(KScreenWidth, 229);
}else{
return CGSizeMake(KScreenWidth, 209);
}
}
}
// head foot
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
//
if(kind==UICollectionElementKindSectionHeader){
//
if(_isCustome){
YTDZHeadCollectionReusableView *view=[collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"YTDZHeadCollectionReusableView" forIndexPath:indexPath];
view.themeM=_routeModel;
view.dele=self;
view.tapG = ^{
YTRouteDetailViewController *routeC=[YTRouteDetailViewController new];
[routeC setThemeId:_routeModel.themeId];
[self.navigationController pushViewController:routeC animated:true];
};
return view;
}else{
YTCollectionHeadReusableView *view=[collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"YTCollectionHeadReusableView" forIndexPath:indexPath];
[view setThemeM:_routeModel];
view.dele=self;
view.tapG = ^{
YTRouteDetailViewController *routeC=[YTRouteDetailViewController new];
[routeC setThemeId:_routeModel.themeId];
[self.navigationController pushViewController:routeC animated:true];
};
return view;
}
}
return [UICollectionReusableView new];
}
#pragma mark dele
// 發(fā)送鏈接
-(void)goRoute{
//
RCRichContentMessage *msg=[RCRichContentMessage messageWithTitle:@"" digest:@"行程鏈接" imageURL:@"" url:[NSString stringWithFormat:@"%@-%@",_routeModel?_routeModel.themeId:self.targetId,[NSNumber numberWithBool:_isCustome]] extra:@""];
[[RCIM sharedRCIM]sendMessage:ConversationType_PRIVATE targetId:self.targetId content:msg pushContent:@"" pushData:@"" success:^(long messageId) {
//
NSLog(@"發(fā)送鏈接成功");
[self.conversationMessageCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:self.conversationDataRepository.count-1 inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:true];
} error:^(RCErrorCode nErrorCode, long messageId) {
[YTHUD showError:@"發(fā)送鏈接失敗,請重新發(fā)送"];
}];
}
// moreHistory
-(void)moreHistory{
//
YTMoreHistoryMessageViewController *moreHistoryC=[YTMoreHistoryMessageViewController new];
moreHistoryC.themeId=_routeModel.themeId;
moreHistoryC.targetId=self.targetId;
moreHistoryC.conversationType = self.conversationType;
[self.navigationController pushViewController:moreHistoryC animated:true];
}
// 發(fā)送初始消息
-(void)sendInitMessage{
RCTextMessage *msg = [RCTextMessage messageWithContent:@"你好蜘醋,看見你發(fā)的出行線路胁塞,感覺還不錯,我挺感興趣的压语,現在有時間可以具體聊聊嗎啸罢?"];
[[RCIM sharedRCIM]sendMessage:ConversationType_PRIVATE targetId:self.targetId content:msg pushContent:@"" pushData:@"" success:^(long messageId) {
//
NSLog(@"發(fā)送初始消息成功");
} error:^(RCErrorCode nErrorCode, long messageId) {
[YTHUD showError:@"發(fā)送初始消息失敗,請重新發(fā)送"];
}];
}
// (用來設置 管家是否與用戶溝通)
-(void)message{
//
[AFNetWorkTool POST:YTBaseUrl(kCustomerMessagepush) params:@{@"Id":self.targetId,@"isChoosed":@"3"} success:^(NSURLSessionDataTask *task, id responseObject) {
if(SUCCESS){
NSLog(@"發(fā)送成功");
}
} fail:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"%@",error);
}];
}
#pragma mark 獲取個人信息
- (void)getUserInfoWithUserId:(NSString *)userId completion:(void (^)(RCUserInfo *))completion{
YTAccount *acount=[YTAccountTool account];
if ([userId isEqualToString:[NSString stringWithFormat:@"%@",acount.userId]]) {
dispatch_async(dispatch_get_main_queue(), ^{
RCUserInfo *user = [[RCUserInfo alloc]init];
user.userId=[NSString stringWithFormat:@"%@",acount.userId];
user.name=acount.name;
user.portraitUri=acount.iconurl?[NSString stringWithFormat:@"%@%@",AccessPhoto,acount.iconurl]:@"";
completion(user);
});
}else{
[AFNetWorkTool POST:YTBaseUrl(kCustomerMessage) params:@{@"userId": userId} success:^(NSURLSessionDataTask *task, id responseObject) {
if(SUCCESS){
NSDictionary *dic=(NSDictionary *)responseObject[@"data"];
YTAccount *acount=[YTAccountTool account];
acount.userId=dic[@"id"];
acount.name=dic[@"name"];
acount.iconurl=YTImgBaseUrl(dic[@"photo"]);
//
dispatch_async(dispatch_get_main_queue(), ^{
RCUserInfo * userInfo2 = [[RCUserInfo alloc]init];
userInfo2.userId=acount.userId;
userInfo2.name=acount.name;
userInfo2.portraitUri=YTImgBaseUrl(acount.iconurl);
completion(userInfo2);
});
}
} fail:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"%@",error);
}];
}
}
@end
- 點擊胎食、長按相關 覆寫方法
// --------- 點擊扰才、長按事件 ---------
// 點擊CELL的 消息調用
-(void)didTapMessageCell:(RCMessageModel *)model{}
// 點擊CELL的 URL調用
-(void)didTapUrlInMessageCell:(NSString *)url model:(RCMessageModel *)model{}
// 點擊CELL的 phone調用
-(void)didTapPhoneNumberInMessageCell:(NSString *)phoneNumber model:(RCMessageModel *)model{}
// 點擊CELL的 頭像調用
-(void)didTapCellPortrait:(NSString *)userId{}
// 長按CELL的頭像調用
-(void)didLongPressCellPortrait:(NSString *)userId{}
// 長按CELL的消息調用
-(void)didLongPressCellPortrait:(NSString *)userId{}
- 消息相關
// --------- 發(fā)送/刪除/插入 消息(可覆寫,要調super) ---------
// 發(fā)送消息
[self sendMessage:[RCMessageContent new] pushContent:@"接收方離線時顯示的遠程推送內容"];
// 重新發(fā)送消息
[self resendMessage:[RCMessageContent new]];
// 插入消息(臨時厕怜,退出再進就沒了)
[self appendAndDisplayMessage:[RCMessage new]];
// 發(fā)送多媒體信息(除文本消息外都是)
[self sendMediaMessage:[RCMessageContent new] pushContent:@"" appUpload:<#(BOOL)#>];
// 刪除消息
[self deleteMessage:[RCMessageModel new]];
舉例:插入
// 是否保存到本地數據庫衩匣,如果不保存,則下次進入聊天界面將不再顯示酣倾。
BOOL saveToDB = NO;
RCMessage *insertMessage;
RCInformationNotificationMessage *warningMessage = [RCInformationNotificationMessage notificationWithMessage:@"提醒消息" extra:nil];
if (saveToDB) {
// 如果保存到本地數據庫舵揭,需要調用insertMessage生成消息實體并插入數據庫谤专。
insertMessage = [[RCIMClient sharedRCIMClient] insertOutgoingMessage:self.conversationType
targetId:self.targetId
sentStatus:SentStatus_SENT
content:warningMessage];
} else {
// 如果不保存到本地數據庫躁锡,需要初始化消息實體并將messageId要設置為-1。
insertMessage =[[RCMessage alloc] initWithType:self.conversationType
targetId:self.targetId
direction:MessageDirection_SEND
messageId:-1
content:warningMessage];
}
// 在當前聊天界面插入該消息
[self appendAndDisplayMessage:insertMessage];
// --------- 消息發(fā)送前后置侍、顯示CELL前的回調 (覆寫做額外處理) ---------
// 發(fā)送消息前調用
-(RCMessageContent *)willSendMessage:(RCMessageContent *)messageContent{ return messageContent; }
// 發(fā)送消息后調用
-(void)didSendMessage:(NSInteger)status content:(RCMessageContent *)messageContent{}
// 插入消息前調用
-(RCMessage *)willAppendAndDisplayMessage:(RCMessage *)message{ return message; }
// 顯示cell前調用
-(void)willDisplayMessageCell:(RCMessageBaseCell *)cell atIndexPath:(NSIndexPath *)indexPath{}
// --------- 未讀消息數UI相關 ---------
// 導航欄左側按鈕 未讀消息數(置為nil映之,不再顯示)
// 需要統(tǒng)計未讀數的會話類型數組
self.displayConversationTypeArray=nil;
// 當未讀消息超過一個屏幕時 在右上角顯示未讀消息數 默認:false不顯示
[self setEnableUnreadMessageIcon:true];
// 當未處在最下方,收到消息時 右下角是否顯示按鈕-滾動到最下方 默認:false不顯示
[self setEnableNewComingMessageIcon:true];
- 輸入工具欄
// 輸入框的默認輸入模式
[self setDefaultInputType:RCChatSessionInputBarInputText];
/*
RCChatSessionInputBarInputText, 文本(默認)
RCChatSessionInputBarInputVoice, 語音
RCChatSessionInputBarInputExtention 擴展
*/
// 輸入框的UI樣式
// 按照會話類型來設置的蜡坊,不要隨意設置
[self.chatSessionInputBarControl setInputBarType:RCChatSessionInputBarControlDefaultType style:RC_CHAT_INPUT_BAR_STYLE_CONTAINER];
/*
type
RCChatSessionInputBarControlDefaultType 非公眾服務(默認)
RCChatSessionInputBarControlPubType 公眾服務
RCChatSessionInputBarControlCSRobotType 客服機器人會話
RCChatSessionInputBarControlNoAvailableType 客服機器人會話
style
RC_CHAT_INPUT_BAR_STYLE_SWITCH_CONTAINER_EXTENTION 語音/文本切換功能+內容輸入功能+擴展功能
RC_CHAT_INPUT_BAR_STYLE_EXTENTION_CONTAINER_SWITCH 擴展功能+內容輸入功能+語音/文本切換功能
RC_CHAT_INPUT_BAR_STYLE_CONTAINER_SWITCH_EXTENTION 內容輸入功能+語音/文本切換功能+擴展功能
RC_CHAT_INPUT_BAR_STYLE_CONTAINER_EXTENTION_SWITCH 內容輸入功能+擴展功能+語音/文本切換功能
RC_CHAT_INPUT_BAR_STYLE_SWITCH_CONTAINER 語音/文本切換功能+內容輸入功能
RC_CHAT_INPUT_BAR_STYLE_CONTAINER_SWITCH 內容輸入功能+語音/文本切換功能
RC_CHAT_INPUT_BAR_STYLE_EXTENTION_CONTAINER 擴展功能+內容輸入功能
RC_CHAT_INPUT_BAR_STYLE_CONTAINER_EXTENTION 內容輸入功能+擴展功能
RC_CHAT_INPUT_BAR_STYLE_CONTAINER 內容輸入功能
*/
// 自定義工具欄
[self setChatSessionInputBarControl:[RCChatSessionInputBarControl new]];
- Emoji表情區(qū)域
Emoji表情,可修改plist文件
// 自定義EmojiView
[self.chatSessionInputBarControl setEmojiBoardView:[RCEmojiBoardView new]];
- 擴展區(qū)域
// 擴展View是否隱藏
[self.extensionView setHidden:true];
// 添加item (tag不要1001~杠输,那是系統(tǒng)預留的)
[self.chatSessionInputBarControl.pluginBoardView insertItemWithImage:[UIImage imageWithNamed:@""] title:@"" tag:10];
[self.chatSessionInputBarControl.pluginBoardView insertItemWithImage:[UIImage imageWithNamed:@""] title:@"" atIndex:0 tag:10];
// 更新item
[self.chatSessionInputBarControl.pluginBoardView updateItemAtIndex:0 image:[UIImage imageWithNamed:@""] title:@""];
[self.chatSessionInputBarControl.pluginBoardView updateItemWithTag:10 image:[UIImage imageWithNamed:@""] title:@""];
// 移除item
[self.chatSessionInputBarControl.pluginBoardView removeItemAtIndex:0];
[self.chatSessionInputBarControl.pluginBoardView removeItemWithTag:10];
[self.chatSessionInputBarControl.pluginBoardView removeAllItems];
// 覆寫,點擊某項后調用
- (void)pluginBoardView:(RCPluginBoardView *)pluginBoardView clickedItemWithTag:(NSInteger)tag {
[super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
switch (tag) {
case 2001:
[self navigateToPay];
break;
case 2002:
[self navigateToPic];
break;
case 2003:
[self navigateToSend];
break;
default:
break;
}
}
3.3 聊天頁(自定義消息和CELL)
第一步:
自定義消息秕衙,并注冊(registerMessageType)
第二步:
自定義cell 蠢甲,并注冊(registerClass:forMessageClass:,則不再走 cellForItem据忘、 sizeForItem)
第三步:
發(fā)送自定義消息
第一步:
自定義消息鹦牛,并注冊(registerMessageType)
// 創(chuàng)建自定義消息類
繼承:RCMessageContent (所有消息的基類)搞糕,并實現其遵守的協(xié)議(必須實現,見下)
// 注冊自定義消息類(必須注冊)
[[RCIM sharedRCIM]registerMessageType:[RCMessageContent class]];
:RCMessageContent
該類遵守了RCMessageCoding,RCMessagePersistentCompatible,RCMessageContentView3個協(xié)議曼追,如下
// ---------- 消息內容的編解碼協(xié)議 ----------
@protocol RCMessageCoding <NSObject>
@required
// 返回消息唯一標識(不要以RC:開頭)
+(NSString *)getObjectName;
// 消息內容->json
- (NSData *)encode;
// json->消息內容
- (void)decodeWithData:(NSData *)data;
/*
主要有三個功能:
提供消息唯一標識符
消息發(fā)送時將消息中的所有信息編碼為 JSON 數據傳輸
消息接收時將 JSON 數據解碼還原為消息對象
*/
@end
// ---------- 消息內容的存儲協(xié)議 ----------
@protocol RCMessagePersistentCompatible <NSObject>
@required
// 返回消息的存儲策略(在本地是否存儲窍仰、是否計入未讀消息數)
+(RCMessagePersistent)persistentFlag;
/*
用于確定消息內容的存儲策略,指明此消息類型在本地是否存儲礼殊、是否計入未讀消息數驹吮,RCMessagePersistent有4種:
MessagePersistent_NONE 在本地不存儲,不計入未讀數晶伦。
MessagePersistent_ISPERSISTED 表示客戶端收到消息后碟狞,要進行未讀消息計數(未讀消息數增加 1),所有內容型消息都應該設置此值婚陪。非內容類消息暫不支持消息計數篷就。
MessagePersistent_ISCOUNTED 表示客戶端收到消息后,要進行存儲近忙,并在之后可以通過接口查詢竭业。
MessagePersistent_STATUS 在本地不存儲,不計入未讀數及舍,并且如果對方不在線未辆,服務器會直接丟棄該消息,對方如果之后再上線也不會再收到此消息(聊天室類型除外锯玛,此類消息聊天室會視為普通消息)咐柜。
*/
@end
// ---------- 消息內容摘要的協(xié)議 ----------
@protocol RCMessageContentView
@optional
// 返回在會話列表、本地通知中顯示的消息內容摘要(最新消息的摘要)
-(NSString *)conversationDigest;
@end
實例
#define RCDeliverSentMessageTypeIdentifier @"RCC:XDispatchMsg"
#import <RongIMLib/RongIMLib.h>
#import "ChatModel.h"
@interface RCDeliverSentMessage : RCMessageContent
@property (nonatomic, strong) ChatModel *chatModel;
@end
#import "RCDeliverSentMessage.h"
#import "MJExtension.h"
@implementation RCDeliverSentMessage
#pragma mark RCMessagePersistentCompatible協(xié)議
///消息是否存儲攘残,是否計入未讀數
+ (RCMessagePersistent)persistentFlag {
return (MessagePersistent_ISPERSISTED | MessagePersistent_ISCOUNTED);
}
#pragma mark NSCoding協(xié)議
/// NSCoding
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.chatModel = [aDecoder decodeObjectForKey:@"chatModel"];
}
return self;
}
/// NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.chatModel forKey:@"chatModel"];
}
#pragma mark RCMessageContentView協(xié)議
/// 會話列表中顯示的最新消息內容
- (NSString *)conversationDigest {
return @"[已發(fā)貨]";
}
#pragma mark RCMessageCoding協(xié)議
///將消息內容編碼成json
- (NSData *)encode {
NSMutableDictionary *dataDict = [NSMutableDictionary dictionary];
if (self.chatModel) {
[dataDict setObject:self.chatModel.mj_keyValues forKey:@"chatModel"];
}
if (self.senderUserInfo) {
NSMutableDictionary *userInfoDic = [[NSMutableDictionary alloc] init];
if (self.senderUserInfo.name) {
[userInfoDic setObject:self.senderUserInfo.name forKeyedSubscript:@"name"];
}
if (self.senderUserInfo.portraitUri) {
[userInfoDic setObject:self.senderUserInfo.portraitUri forKeyedSubscript:@"portrait"];
}
if (self.senderUserInfo.userId) {
[userInfoDic setObject:self.senderUserInfo.userId forKeyedSubscript:@"id"];
}
[dataDict setObject:userInfoDic forKey:@"user"];
}
NSData *data = [NSJSONSerialization dataWithJSONObject:dataDict options:kNilOptions error:nil];
return data;
}
///將json解碼生成消息內容
- (void)decodeWithData:(NSData *)data {
if (data) {
__autoreleasing NSError *error = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (dictionary) {
self.chatModel = [ChatModel mj_objectWithKeyValues:dictionary[@"chatModel"]];
NSDictionary *userinfoDic = dictionary[@"user"];
[self decodeUserInfo:userinfoDic];
}
}
}
///消息的類型名
+ (NSString *)getObjectName {
return DeliverSentMessageTypeIdentifier;
}
-(instancetype)init{
if (self = [super init]) {
self.chatModel = [[ChatModel alloc]init];
}
return self;
}
@end
第二步:
自定義cell 拙友,并注冊(registerClass:forMessageClass:,則不再走 cellForItem歼郭、 sizeForItem)
:RCMessageBaseCell
在baseContentView添加自定義控件(建議在 baseContentView 上方預留 10)
適合高度自定制
或
:RCMessageCell
在繼承 RCMessageBaseCell 的基礎上增加顯示頭像和昵稱
在messageContentView添加自定義控件遗契。
不適合高度自定制
// 注冊CELL
[self registerClass:[RCMessageCell class] forMessageClass:[RCMessageContent class]];
// size cell中實現(必須實現,不會再走cellforItem病曾、sizeForItem)
+(CGSize)sizeForMessageModel:(RCMessageModel *)model withCollectionViewWidth:(CGFloat)collectionViewWidth referenceExtraHeight:(CGFloat)extraHeight{
// ExtraHeigh: cell內容區(qū)域之外的高度
return CGSizeMake(100, 100);
}
4. 相關
- 用戶信息
為了信息安全和一致:存儲在App服務器而不是融云服務器牍蜂。
融云提供了 用戶信息提供者、群組信息提供者泰涂、群名片信息提供者鲫竞,只需實現響應協(xié)議即可正確獲取并顯示信息。
// --------- 群組信息提供者 ---------
<RCIMGroupInfoDataSource>
[[RCIM sharedRCIM]setGroupInfoDataSource:self];
- (void)getGroupInfoWithGroupId:(NSString *)groupId
completion:(void (^)(RCGroup *groupInfo))completion{
}
// --------- 群名片信息提供者 ---------
<RCIMGroupUserInfoDataSource>
[[RCIM sharedRCIM]setGroupUserInfoDataSource:self];
- (void)getUserInfoWithUserId:(NSString *)userId
inGroup:(NSString *)groupId
completion:(void (^)(RCUserInfo *userInfo))completion{
}
// --------- 用戶信息提供者 ---------
<RCIMUserInfoDataSource>
[[RCIM sharedRCIM]setUserInfoDataSource:self];
// 融云在需要顯示用戶信息時調用
- (void)getUserInfoWithUserId:(NSString *)userId completion:(void (^)(RCUserInfo *))completion{
// 判斷是否是自己
YTAccount *acount=[YTAccountTool account];
if ([userId isEqualToString:[NSString stringWithFormat:@"%@",acount.userId]]) { // 是
RCUserInfo *user = [[RCUserInfo alloc]init];
user.userId=[NSString stringWithFormat:@"%@",acount.userId];
user.name=acount.name;
user.portraitUri=acount.iconurl?[NSString stringWithFormat:@"%@%@",AccessPhoto,acount.iconurl]:@"";
return completion(user);
}else{ // 別人
// 獲取信息
[AFNetWorkTool POST:YTBaseUrl(kCustomerMessage) params:@{@"userId":userId} success:^(NSURLSessionDataTask *task, id responseObject) {
if(SUCCESS){
NSDictionary *dic=(NSDictionary *)responseObject[@"data"];
//
RCUserInfo *userInfo2 = [[RCUserInfo alloc]init];
userInfo2.userId=dic[@"id"];
userInfo2.name=dic[@"name"];
userInfo2.portraitUri=YTImgBaseUrl(dic[@"photo"]);
// 刷新UI
[[RCIM sharedRCIM]refreshUserInfoCache:userInfo2 withUserId:userInfo2.userId];
}
} fail:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"%@",error);
}];
}
return completion(nil);
}
緩存
緩存---有:顯示逼蒙;無:通過信息提供者獲取信息并緩存从绘。
// 是否持久化緩存到本地(默認:false,通過信息提供者獲取數據后進行緩存,關閉App后刪除緩存僵井;true則不會刪除,重新啟動后使用緩存顯示)
[[RCIM sharedRCIM]setEnablePersistentUserInfoCache:true];
// 清除緩存數據
// 清除用戶信息緩存
[[RCIM sharedRCIM]clearUserInfoCache];
// 清除群組信息緩存
[[RCIM sharedRCIM]clearGroupInfoCache];
// 清除群名片信息緩存
[[RCIM sharedRCIM]clearGroupUserInfoCache];
// 獲取緩存
// 獲取緩存中的用戶信息
[[RCIM sharedRCIM]getUserInfoCache:@"userId"];
// 獲取緩存中的群組信息
[[RCIM sharedRCIM]getGroupInfoCache:@"groupId"];
// 獲取緩存中的群名片信息
[[RCIM sharedRCIM]getGroupUserInfoCache:@"userId" withGroupId:@"groupId"];
// 刷新緩存赁还,可能不會立即刷新UI,可通過reloadData強制刷新
// 刷新用戶緩存
[[RCIM sharedRCIM]refreshUserInfoCache:[RCUserInfo new] withUserId:@""];
// 刷新群組緩存
[[RCIM sharedRCIM]refreshGroupInfoCache:[RCGroup new] withGroupId:@""];
// 刷新群中的個人名片緩存
[[RCIM sharedRCIM]refreshGroupUserInfoCache:[RCUserInfo new] withUserId:@"" withGroupId:@""];
- 會話類型
單聊 ConversationType_PRIVATE
// 一個繼承了RCConversationViewController的類
YTMessageViewController *conversationVC = [YTMessageViewController new];
conversationVC.conversationType = ConversationType_PRIVATE; // 會話類型
conversationVC.title = ((RCUserInfo *)model.extend).name; // navTitle
conversationVC.targetId = model.targetId; // 目標ID
[self.navigationController pushViewController:conversationVC animated:YES];
群組 ConversationType_GROUP
// 進入群組:和單聊相同驹沿,只是type不同
所有群組操作都通過App后臺交互艘策,再由App后臺和融云交互。
討論組 ConversationType_DISCUSSION
兩個以上用戶一起進行聊天時生成討論組渊季。
用戶可以自行添加好友生成一個討論組聊天朋蔫。
成員關系由融云負責建立并保持。
退出聊天界面或者離線后可以收到推送通知却汉。
同一個用戶最多可加入 500 個討論組驯妄。
討論組不能解散。
// 進入討論組:和單聊相同合砂,只是type不同
// 創(chuàng)建討論組(用戶ID)
[[RCIM sharedRCIM]createDiscussion:@"討論組名稱" userIdList:@[] success:^(RCDiscussion *discussion) {
// discussion討論組對象
} error:^(RCErrorCode status) {
NSLog(@"創(chuàng)建討論組出錯青扔,錯誤碼:%d",(int)status);
}];
// 添加用戶到討論組(用戶ID)
[[RCIM sharedRCIM]addMemberToDiscussion:@"討論組id" userIdList:@[] success:^(RCDiscussion *discussion) {
// discussion討論組對象
} error:^(RCErrorCode status) {
NSLog(@"加入討論組出錯,錯誤碼:%d",(int)status);
}];
// 移除成員從討論組
[[RCIM sharedRCIM]removeMemberFromDiscussion:@"討論組id" userId:@"用戶id" success:^(RCDiscussion *discussion) {
// discussion討論組對象
} error:^(RCErrorCode status) {
NSLog(@"移除成員從討論組出錯翩伪,錯誤碼:%d",(int)status);
}];
// 退出討論組
[[RCIM sharedRCIM]quitDiscussion:@"討論組id" success:^(RCDiscussion *discussion) {
// discussion討論組對象
} error:^(RCErrorCode status) {
NSLog(@"退出討論組出錯微猖,錯誤碼:%d",(int)status);
}];
// 獲取討論組信息出錯
[[RCIM sharedRCIM]getDiscussion:@"討論組id" success:^(RCDiscussion *discussion) {
// discussion討論組對象
} error:^(RCErrorCode status) {
NSLog(@"獲取討論組信息出錯,錯誤碼:%d",(int)status);
}];
// 設置討論組名稱
[[RCIM sharedRCIM]setDiscussionName:@"討論組id" name:@"討論組名稱" success:^{
// discussion討論組對象
} error:^(RCErrorCode status) {
NSLog(@"設置討論組名稱出錯缘屹,錯誤碼:%d",(int)status);
}];
// 設置討論組加人權限(是否放開)
[[RCIM sharedRCIM]setDiscussionInviteStatus:@"討論組id" isOpen:true success:^{
// discussion討論組對象
} error:^(RCErrorCode status) {
NSLog(@"設置討論組加人權限出錯凛剥,錯誤碼:%d",(int)status);
}];
聊天室 ConversationType_CHATROOM
// 加入聊天室(設置id,type轻姿,直接push就加入了犁珠。同單聊)
聊天室的消息沒有 Push 通知,沒有人數限制互亮,退出后不再接收消息并清空本地消息記錄犁享。
融云默認一個用戶同時只能加入一個聊天室。
一個聊天室一個小時內沒有人發(fā)送消息或者加入時會被自動銷毀豹休。
// 設置 獲取歷史記錄的條數(默認獲取10條歷史記錄)(-1:不獲取炊昆,0:默認10條,0<count<=50)
[self setDefaultHistoryMessageCountOfChatRoom:-1];
// 自定義
// 加入聊天室(不存在則創(chuàng)建)
[[RCIMClient sharedRCIMClient]joinChatRoom:@"聊天室ID" messageCount:10 success:^{
} error:^(RCErrorCode status) {
NSLog(@"加入聊天室出錯慕爬,錯誤碼:%d",(int)status);
}];
// 加入已存在的聊天室
[[RCIMClient sharedRCIMClient]joinExistChatRoom:@"聊天室ID" messageCount:10 success:^{
} error:^(RCErrorCode status) {
NSLog(@"加入聊天室出錯窑眯,錯誤碼:%d",(int)status);
}];
// 退出聊天室
[[RCIMClient sharedRCIMClient]quitChatRoom:@"聊天室ID" success:^{
} error:^(RCErrorCode status) {
NSLog(@"退出聊天室出錯,錯誤碼:%d",(int)status);
}];
// 獲取聊天室信息
[[RCIMClient sharedRCIMClient]getChatRoomInfo:@"聊天室ID" count:10 order:RC_ChatRoom_Member_Desc success:^(RCChatRoomInfo *chatRoomInfo) {
} error:^(RCErrorCode status) {
NSLog(@"獲取聊天室信息出錯医窿,錯誤碼:%d",(int)status);
}];
系統(tǒng)會話 ConversationType_SYSTEM
不能從App發(fā)起系統(tǒng)會話,只能通過App服務器來發(fā)起
客服 ConversationType_CUSTOMERSERVICE
客服---收費 (融云Demo有個RCDCustomerServiceViewController)
// 啟動客服
// type,title,客服id,csInfo(用于上傳用戶信息到客服后臺炊林,數據的nickName和portraitUrl必須填寫)姥卢,push即可
// 退出客服
// 點擊返回按鈕即可退出(默認),可通過改 來讓退出頁面不退出客服
<key>CustomerService</key>
<dict>
<key>SuspendWhenLeave</key>
<true/> 改為false
</dict>
// 評價
// 在客服頁面停留一分鐘后退出會自動彈出客服評價,退出時彈出(人工和機器 界面不一樣)
公眾號
// 跳轉到 已關注的公眾號列表頁
RCPublicServiceListViewController *publicServiceVC = [[RCPublicServiceListViewController alloc] init];
[self.navigationController pushViewController:publicServiceVC animated:YES];
// 跳轉到 搜索公眾號頁
RCPublicServiceSearchViewController *searchFirendVC = [[RCPublicServiceSearchViewController alloc] init];
[self.navigationController pushViewController:searchFirendVC animated:YES];
// 跳轉到會話頁(type独榴,id僧叉,navTitle,userName)
- 消息類型
- 系統(tǒng)自帶消息類型
文本消息
RCTextMessage *txtMsg = [RCTextMessage messageWithContent:@""];
圖文消息
RCRichContentMessage *richMsg = [RCRichContentMessage messageWithTitle:@"title" digest:@"內容摘要" imageURL:@"imgURL" url:@"跳轉url" extra:nil];
位置消息 (縮略圖棺榔、坐標瓶堕、地址名)
RCLocationMessage *locationMessage = [RCLocationMessage messageWithLocationImage:[UIImage new] location:location locationName:@"locationName"];
語音消息
RCVoiceMessage *rcVoiceMessage = [RCVoiceMessage messageWithAudio:[NSData new] duration:10];
/*
必須:wav格式衙熔、采樣率必須是8000Hz其兴,采樣位數(精度)必須為16位,10s
參考IMKit中的錄音參數:
NSDictionary *settings = @{AVFormatIDKey: @(kAudioFormatLinearPCM),
AVSampleRateKey: @8000.00f,
AVNumberOfChannelsKey: @1,
AVLinearPCMBitDepthKey: @16,
AVLinearPCMIsNonInterleaved: @NO,
AVLinearPCMIsFloatKey: @NO,
AVLinearPC'MIsBigEndianKey: @NO};
*/
發(fā)送上述消息(會話類型,目標用戶ID,文本消息,接收方離線時需要顯示的內容,接收方離線時需要在遠程推送中不顯示的內容)
[[RCIM sharedRCIM]sendMessage:ConversationType_PRIVATE targetId:@"目標ID" content:txtMsg pushContent:@"" pushData:@"" success:^(long messageId) {
} error:^(RCErrorCode nErrorCode, long messageId) {
}];
圖片消息 (縮略圖:240*240袁翁,大圖尺寸:960*960)
RCImageMessage *imageMessage = [RCImageMessage messageWithImage:[UIImage new]];
RCImageMessage *imageMessage = [RCImageMessage messageWithImageURI:@"imgURL"];
RCImageMessage *imageMessage = [RCImageMessage messageWithImageData:[NSData data]];
/*
縮略圖尺寸為:240 x 240 像素惶看,以寬度和高度中較長的邊不超過 240 像素等比壓縮者铜。
大圖尺寸為:960 x 960 像素窃判,以寬度和高度中較長的邊不超過 960 像素等比壓縮
*/
文件消息
RCFileMessage *fileMessage = [RCFileMessage messageWithFile:@"filePath"];
發(fā)送上述消息斜姥,存儲有效期為 6 個月
[[RCIM sharedRCIM]sendMediaMessage:ConversationType_PRIVATE targetId:@"目標ID" content: imageMessage pushContent:@"" pushData:@"" progress:^(int progress, long messageId) { // 0~100
} success:^(long messageId) {
} error:^(RCErrorCode errorCode, long messageId) {
} cancel:^(long messageId) {
}];
- 消息接收監(jiān)聽
// 消息接收監(jiān)聽 <RCIMReceiveMessageDelegate> 放在AppDelegate中
[RCIM sharedRCIM].receiveMessageDelegate=self;
// 收到消息時調用(此時message已被存儲數據庫惑惶,詳見下圖)
-(void)onRCIMReceiveMessage:(RCMessage *)message left:(int)left{
// 發(fā)送通知设塔、播放音效凄吏、震動。闰蛔。痕钢。
// AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); // 震動
}
// 撤回消息時回調
-(void)onRCIMMessageRecalled:(long)messageId{
}
// 接收到消息準備播放方提示音前調用(默認:false,播放默認提示音)
-(BOOL)onRCIMCustomAlertSound:(RCMessage *)message{
return true; // 不播放默認提示音
}
// 應用處于后臺時序六,接收到消息時彈出本地通知時調用(默認:false盖喷,默認提示框)
-(BOOL)onRCIMCustomLocalNotification:(RCMessage *)message withSenderName:(NSString *)senderName{
// true則不彈通知
return false;
}
- 消息提醒
// 設置 是否屏蔽消息提醒
[[RCIMClient sharedRCIMClient]setConversationNotificationStatus:ConversationType_PRIVATE targetId:@"id" isBlocked:true success:^(RCConversationNotificationStatus nStatus) {
} error:^(RCErrorCode status) {
NSLog(@"屏蔽消息提醒出錯,錯誤碼:%d",(int)status);
}];
// 獲取 是否屏蔽消息提醒
[[RCIMClient sharedRCIMClient]getConversationNotificationStatus:ConversationType_PRIVATE targetId:@"" success:^(RCConversationNotificationStatus nStatus) {
} error:^(RCErrorCode status) {
NSLog(@"獲取是否屏蔽消息提醒出錯难咕,錯誤碼:%d",(int)status);
}];
時間段屏蔽
// 設置 時間段屏蔽课梳,(開始屏蔽消息提醒的時間,持續(xù)時間0~1440min)
[[RCIMClient sharedRCIMClient]setNotificationQuietHours:@"HH:MM:SS s" spanMins:10 success:^{
} error:^(RCErrorCode status) {
NSLog(@"時間段屏蔽出錯余佃,錯誤碼:%d",(int)status);
}];
// 刪除 時間段屏蔽
[[RCIMClient sharedRCIMClient]removeNotificationQuietHours:^{
} error:^(RCErrorCode status) {
NSLog(@"刪除時間段屏蔽出錯暮刃,錯誤碼:%d",(int)status);
}];
// 獲取 時間段屏蔽
[[RCIMClient sharedRCIMClient]getNotificationQuietHours:^(NSString *startTime, int spansMin){
} error:^(RCErrorCode status) {
NSLog(@"獲取時間段屏蔽出錯,錯誤碼:%d",(int)status);
}];
通知
可通過RCIMReceiveMessageDelegate的onRCIMCustomAlertSound爆土、onRCIMCustomLocalNotification來選擇性關閉
// 是否關閉提示音(默認:false)
[[RCIM sharedRCIM]setDisableMessageAlertSound:true];
// 是否關閉通知
[[RCIM sharedRCIM]setDisableMessageNotificaiton:true];
- 小角標
獲取所有未讀消息數
#import <RongIMKit/RongIMKit.h>
// 獲取所有未讀消息數
-(NSInteger)getUnreadCount{
int unreadMsgCount = [[RCIMClient sharedRCIMClient] getUnreadCount:@[
@(ConversationType_PRIVATE),
@(ConversationType_DISCUSSION),
@(ConversationType_APPSERVICE),
@(ConversationType_PUBLICSERVICE),
@(ConversationType_GROUP)
]];
return unreadMsgCount ;
}
// 設置角標
-(void)setBadageNum{
NSInteger unreadMessageCount = [self getUnreadCount];
// 設置tabbar 的icon
UITabBarController *tabbar = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController ;
if ([tabbar isKindOfClass:[UITabBarController class]]) {
UITabBarItem *item = [tabbar.tabBar.items objectAtIndex:1];
// 如果沒有未讀消息返回值為nil
if (unreadMessageCount == 0 || unreadMessageCount == nil) {
item.badgeValue = nil ;
return ;
}
item.badgeValue = [NSString stringWithFormat:@"%d",unreadMessageCount];
}
}
收到消息時設置小角標
// <RCIMReceiveMessageDelegate>
[RCIM sharedRCIM].receiveMessageDelegate=self;
// 收到消息時調用(更新)
-(void)onRCIMReceiveMessage:(RCMessage *)message left:(int)left{
dispatch_async(dispatch_get_main_queue(), ^{
[self setBadageNum];
});
}
進入聊天頁設置小角標
功能進階部分(紅包椭懊、動態(tài)表情、群組@) 待續(xù)