写在开头:本片文章记录融云开发的流程以及开发过程中遇到的问题,由于项目中没有好友的概念,所以没有添加好友删除好友的功能,做的功能罗列如下:
1、集成sdk
2、注册登录
3、会话列表
4、会话页面
5、位置实时共享
6、头像昵称
7、未读角标
8、搜索历史记录
本人遇到的问题,相信大多数人都会遇到的问题在下面一一记录
一、集成sdk的时候遇到的问题
1、现在大多数iOS开发集成第三方都使用cocopods,很少用手动下载集成,一方面比较快捷方便,另一方面减少不比较的麻烦冲突。一般开发者都是想快速开发使用SDK自带的页面,那就
target 'MyApp' do
pod 'RongCloudIM/IMKit', '~> 2.9.0'
end
否则
target 'MyApp' do
pod 'RongCloudIM/IMLib', '~> 2.9.0'
end
注意一点:后面的版本号最好用最新的,官网文档中写的不是最新的(2.8.3),不要直接复制粘贴过去就pod install,因为iOS(2.8.0之前老版本)和安卓版本差别大的话,可能会造成苹果和安卓互通音视频的时候出问题
2、还有可能出现冲突的问题,那就只能根据自己的情况去除重复文件了
二、连接服务器(登录融云)
//因为获取 Token 时需要提供 App Key 和 App Secret。如果在客户端请求 Token,假如您的 App 代码一旦被反编译,则会导致您的 App Key 和 App Secret 泄露。所以,务必在您的服务器端获取 Token,一句话:让后台登录接口返回融云的token,然后在登录成功后,调用下面的方法登录融云。
//登录融云
[[RCIM sharedRCIM] connectWithToken:@"YourTestUserToken" success:^(NSString *userId) {
NSLog(@"登陆成功。当前登录的用户ID:%@", userId);
} error:^(RCConnectErrorCode status) {
NSLog(@"登陆的错误码为:%d", status);
} tokenIncorrect:^{
//token过期或者不正确。
//如果设置了token有效期并且token过期,请重新请求您的服务器获取新的token
//如果没有设置token有效期却提示token错误,请检查您客户端和服务器的appkey是否匹配,还有检查您获取token的流程。
NSLog(@"token错误");
}];
注意:如果怕APP每次杀死在进入的时候会断开连接,所以在APPdelegate.m里面再调一次
三、会话列表
//融云 IMKit 已经实现了一个默认的会话列表视图控制器,您直接使用或继承 RCConversationListViewController,即可快速启动和使用会话列表界面。
- (void)viewDidLoad {
//重写显示相关的接口,必须先调用super,否则会屏蔽SDK默认的处理
[super viewDidLoad];
//设置需要显示哪些类型的会话
[self setDisplayConversationTypes:@[@(ConversationType_PRIVATE),
@(ConversationType_DISCUSSION),
@(ConversationType_CHATROOM),
@(ConversationType_GROUP),
@(ConversationType_APPSERVICE),
@(ConversationType_SYSTEM)]];
//设置需要将哪些类型的会话在会话列表中聚合显示
[self setCollectionConversationType:@[@(ConversationType_DISCUSSION),
@(ConversationType_GROUP)]];
}
1、这个页面如果断开连接有个红色的提示栏,网络断开如果想去掉,在本类中写:
self.isShowNetworkIndicatorView = NO;
2、如果想在头部加东西
self.conversationListTableView.tableHeaderView = self.header;
3、点击cell处理事件
//重写RCConversationListViewController的onSelectedTableRow事件
- (void)onSelectedTableRow:(RCConversationModelType)conversationModelType
conversationModel:(RCConversationModel *)model
atIndexPath:(NSIndexPath *)indexPath {
RCConversationViewController *conversationVC = [[RCConversationViewController alloc]init];
conversationVC.conversationType = model.conversationType;
conversationVC.targetId = model.targetId;
conversationVC.title = @"想显示的会话标题";
[self.navigationController pushViewController:conversationVC animated:YES];
}
四、会话页面(单聊)
1、修改扩展内容中的图片和名字
//更新扩展部分的图片
[self.chatSessionInputBarControl.pluginBoardView updateItemWithTag:PLUGIN_BOARD_ITEM_ALBUM_TAG image:[UIImage imageNamed:@"actionbar_picture_icon"] title:@"照片"];
2、语音视频
//导入callkit
pod 'RongCloudRTC/RongCallKit', '~> 2.9.0'
注意:开通功能后自动显示出来
3、小视频(相册中可选视频)
从 SDK 2.8.29 版本开始支持小视频功能
下载小视频 SDK 将 RongSight.framework 编译连接到自己的项目里面就可以使用小视频功能,不需要写额外的代码。
/*!
选择媒体资源时,是否包含视频文件,默认值是NO
@discussion 默认是不包含
*/
@property(nonatomic, assign) BOOL isMediaSelectorContainVideo;
在APPdelegate.m中设置:
[[RCIM sharedRCIM] setIsMediaSelectorContainVideo:YES];
五、头像昵称
1、形状问题
// 设置头像为圆形
[RCIM sharedRCIM].globalMessageAvatarStyle = RC_USER_AVATAR_CYCLE;
[RCIM sharedRCIM].globalConversationAvatarStyle = RC_USER_AVATAR_CYCLE;
在APPdelegate.m中设置,会话列表和会话页面中的头像变成圆形了、但是音视频的页面不会被修改的,如果你想修改那里的头像形状,就不能用cocopods来导入
pod 'RongCloudRTC/RongCallKit', '~> 2.9.0'
因为这样的话,页面是没有暴露出来的,就不能去修改。真的想修改,只能下载开源的callkit代码导入项目中,进行修改,只不过手动导入的话,可能还是会有一些不必要的麻烦。
2、头像
有两种情况,第一种是像demo中的一样头像是用背景色加上昵称的第一位字来生成一张image,存储到本地,进行展示。第二种是用自己的在线图片url。
第一种的做法:去demo中把图1这些类拉过来进行修改,保证不报错,能实现自己的功能,该删的删,该改的改~
第二种的做法:就不用这么麻烦了~
展示刷新头像的方法也有两种,最好不要都用,二选其一即可
*************第一种开始*****************
首先遵守协议:RCIMUserInfoDataSource,RCIMGroupInfoDataSource
在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中 设置代理:
[[RCIM sharedRCIM] setUserInfoDataSource:self];
[[RCIM sharedRCIM] setGroupInfoDataSource:self];
实现代理方法,以个人信息为例:(本地头像的情况,通过demo中的两个类来生成的图片)
- (void)getUserInfoWithUserId:(NSString *)userId
completion:(void (^)(RCUserInfo *userInfo))completion
{
if ([userId isEqualToString:[RDAppDataManager instance].ucUserId]) {
RCUserInfo *info = [[RCUserInfo alloc] init];
info.userId = userId;
info.name = [RDAppDataManager instance].nickName;
FTAddressBookUserModel *model = [[FTAddressBookUserModel alloc] init];
model.name = [RDAppDataManager instance].nickName;
model.userId = [RDAppDataManager instance].ucUserId;
model.color = [RDAppDataManager instance].color;
info.portraitUri = [RCDUtilities defaultUserPortrait:model];
[[RCIM sharedRCIM] refreshUserInfoCache:info withUserId:info.userId];
return completion(info);
}else{
FTAddressBookUserModel *model = [[StorageManager sharedInstance] getUserWithUserID:userId];
RCUserInfo *info = [[RCUserInfo alloc] init];
info.userId = userId;
info.name = model.name;
info.portraitUri = [RCDUtilities defaultUserPortrait:model];
[[RCIM sharedRCIM] refreshUserInfoCache:info withUserId:info.userId];
return completion(info);
}
}
(链接头像的情况)
- (void)getUserInfoWithUserId:(NSString *)userId
completion:(void (^)(RCUserInfo *userInfo))completion
{
if ([userId isEqualToString:@"当前登录用户的融云id"]) {
return completion([[RCUserInfo alloc] initWithUserId:userId name:@"当前登录用户的用户名" portrait:@"当前登录用户头像的url"]);
}else
{
根据存储联系人信息的模型,通过 userId 来取得对应的name和头像url,进行以下设置(此处因为项目接口尚未实现,所以就只能这样给大家说说,请见谅)
return completion([[RCUserInfo alloc] initWithUserId:userId name:@"name" portrait:@"http://pic32.nipic.com/20130827/12906030_123121414000_2.png"]);
}
}
// 从 2.6.0 开始 IMKit 可以缓存用户信息到数据库里,开发者可以开启 RCIM.h 里的enablePersistentUserInfoCache (当然也可以不用写这个,个人感觉没有什么显著的用处)
[RCIM sharedRCIM].enablePersistentUserInfoCache = YES;
/*!
获取群组信息
@param groupId 群组ID
@param completion 获取群组信息完成之后需要执行的Block
@param groupInfo(in completion) 该群组ID对应的群组信息
@discussion SDK通过此方法获取用户信息并显示,请在completion的block中返回该用户ID对应的用户信息。
在您设置了用户信息提供者之后,SDK在需要显示用户信息的时候,会调用此方法,向您请求用户信息用于显示。
*/
- (void)getGroupInfoWithGroupId:(NSString *)groupId
completion:(void (^)(RCGroup *groupInfo))completion
{
// 此处做相应的群组设置即可,获取群组信息的流程与获取用户信息的流程一致
}
************第一种结束******************
************第二种开始******************
//APPdelegate.m 设置消息体内是否携带用户信息
[RCIM sharedRCIM].enableMessageAttachUserInfo = YES;
// 设置当前用户信息
链接融云成功后在成功回调里面设置一下currentUserInfo就行了
[[RCIM sharedRCIM] connectWithToken:@"YourTestUserToken" success:^(NSString *userId) {
NSLog(@"登陆成功。当前登录的用户ID:%@", userId);
[RCIM sharedRCIM].currentUserInfo = [[RCUserInfo alloc] initWithUserId:@"当前登录用户的融云id" name:@"当前登录用户的用户名" portrait:@"用户头像的url"];
} error:^(RCConnectErrorCode status) {
NSLog(@"登陆的错误码为:%d", status);
} tokenIncorrect:^{
//token过期或者不正确。
//如果设置了token有效期并且token过期,请重新请求您的服务器获取新的token
//如果没有设置token有效期却提示token错误,请检查您客户端和服务器的appkey是否匹配,还有检查您获取token的流程。
NSLog(@"token错误");
}];
************第二种结束******************
图1
六、位置实时共享
1、首先将下面的文件(融云demo里面关于位置的文件)拉进项目里
2、导入头文件
#import "RealTimeLocationEndCell.h"
#import "RealTimeLocationStartCell.h"
#import "RealTimeLocationStatusView.h"
#import "RealTimeLocationViewController.h"
3、遵守协议 < RCRealTimeLocationObserver, RealTimeLocationStatusViewDelegate,UIActionSheetDelegate>
4、属性
@property(nonatomic, weak) id<RCRealTimeLocationProxy> realTimeLocation;
@property(nonatomic, strong) RealTimeLocationStatusView *realTimeLocationStatusView;
5、在viewDidLoad中添加代码如下:
/*******************实时地理位置共享***************/
[self registerClass:[RealTimeLocationStartCell class] forMessageClass:[RCRealTimeLocationStartMessage class]];
[self registerClass:[RealTimeLocationEndCell class] forMessageClass:[RCRealTimeLocationEndMessage class]];
WeakSelf;
[[RCRealTimeLocationManager sharedManager] getRealTimeLocationProxy:self.conversationType
targetId:self.targetId
success:^(id<RCRealTimeLocationProxy> realTimeLocation) {
weakSelf.realTimeLocation = realTimeLocation;
[weakSelf.realTimeLocation addRealTimeLocationObserver:weakSelf];
[weakSelf updateRealTimeLocationStatus];
}
error:^(RCRealTimeLocationErrorCode status) {
NSLog(@"get location share failure with code %d", (int)status);
}];
/******************实时地理位置共享**************/
6、代理中的几个方法
//选择位置按钮时弹出两个选项(位置实时共享,发送位置)
- (void)pluginBoardView:(RCPluginBoardView *)pluginBoardView
clickedItemWithTag:(NSInteger)tag {
switch (tag) {
case PLUGIN_BOARD_ITEM_LOCATION_TAG:
{
if (self.realTimeLocation) {
UIActionSheet *actionSheet = [[UIActionSheet alloc]
initWithTitle:nil
delegate:self
cancelButtonTitle:@"取消"
destructiveButtonTitle:nil
otherButtonTitles:@"发送位置", @"位置实时共享", nil];
[actionSheet showInView:self.view];
} else {
[super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
}
}
break;
default:
{
[super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
}
break;
}
}
//结束定位
- (void)willMoveToParentViewController:(UIViewController*)parent{
[super willMoveToParentViewController:parent];
if (!parent) {
[self.realTimeLocation quitRealTimeLocation];
}
}
//发送位置和位置实时共享选择
#pragma mark - UIActionSheet Delegate
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
switch (buttonIndex) {
case 0: {
[super pluginBoardView:self.pluginBoardView
clickedItemWithTag:PLUGIN_BOARD_ITEM_LOCATION_TAG];
} break;
case 1: {
[self showRealTimeLocationViewController];
} break;
}
}
//点击cell时,如果是地理位置,进行位置共享
#pragma mark override
- (void)didTapMessageCell:(RCMessageModel *)model {
[super didTapMessageCell:model];
if ([model.content isKindOfClass:[RCRealTimeLocationStartMessage class]]) {
[self showRealTimeLocationViewController];
}
}
以下代理方法 原封不动放下面
/*******************实时地理位置共享***************/
- (void)showRealTimeLocationViewController {
RealTimeLocationViewController *lsvc = [[RealTimeLocationViewController alloc] init];
lsvc.realTimeLocationProxy = self.realTimeLocation;
if ([self.realTimeLocation getStatus] == RC_REAL_TIME_LOCATION_STATUS_INCOMING) {
[self.realTimeLocation joinRealTimeLocation];
} else if ([self.realTimeLocation getStatus] == RC_REAL_TIME_LOCATION_STATUS_IDLE) {
[self.realTimeLocation startRealTimeLocation];
}
[self.navigationController presentViewController:lsvc
animated:YES
completion:^{
}];
}
- (void)updateRealTimeLocationStatus {
if (self.realTimeLocation) {
[self.realTimeLocationStatusView updateRealTimeLocationStatus];
WeakSelf;
NSArray *participants = nil;
switch ([self.realTimeLocation getStatus]) {
case RC_REAL_TIME_LOCATION_STATUS_OUTGOING:
[self.realTimeLocationStatusView updateText:@"你正在共享位置"];
break;
case RC_REAL_TIME_LOCATION_STATUS_CONNECTED:
case RC_REAL_TIME_LOCATION_STATUS_INCOMING:
participants = [self.realTimeLocation getParticipants];
if (participants.count == 1) {
NSString *userId = participants[0];
[weakSelf.realTimeLocationStatusView
updateText:[NSString stringWithFormat:@"user<%@>正在共享位置", userId]];
[[RCIM sharedRCIM].userInfoDataSource
getUserInfoWithUserId:userId
completion:^(RCUserInfo *userInfo) {
if (userInfo.name.length) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.realTimeLocationStatusView
updateText:[NSString stringWithFormat:@"%@正在共享位置", userInfo.name]];
});
}
}];
} else {
if (participants.count < 1)
[self.realTimeLocationStatusView removeFromSuperview];
else
[self.realTimeLocationStatusView
updateText:[NSString stringWithFormat:@"%d人正在共享地理位置", (int)participants.count]];
}
break;
default:
break;
}
}
}
- (RealTimeLocationStatusView *)realTimeLocationStatusView {
if (!_realTimeLocationStatusView) {
_realTimeLocationStatusView =
[[RealTimeLocationStatusView alloc] initWithFrame:CGRectMake(0, STATUS_BAR_HEIGHT + 44, self.view.frame.size.width, 0)];
_realTimeLocationStatusView.delegate = self;
[self.view addSubview:_realTimeLocationStatusView];
}
return _realTimeLocationStatusView;
}
#pragma mark - RealTimeLocationStatusViewDelegate
- (void)onJoin {
[self showRealTimeLocationViewController];
}
- (RCRealTimeLocationStatus)getStatus {
return [self.realTimeLocation getStatus];
}
- (void)onShowRealTimeLocationView {
[self showRealTimeLocationViewController];
}
#pragma mark - RCRealTimeLocationObserver
- (void)onRealTimeLocationStatusChange:(RCRealTimeLocationStatus)status {
WeakSelf;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf updateRealTimeLocationStatus];
});
}
- (void)onReceiveLocation:(CLLocation *)location type:(RCRealTimeLocationType)type fromUserId:(NSString *)userId {
WeakSelf;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf updateRealTimeLocationStatus];
});
}
- (void)onParticipantsJoin:(NSString *)userId {
WeakSelf;
if ([userId isEqualToString:[RCIMClient sharedRCIMClient].currentUserInfo.userId]) {
[self notifyParticipantChange:@"你加入了地理位置共享"];
} else {
[[RCIM sharedRCIM].userInfoDataSource
getUserInfoWithUserId:userId
completion:^(RCUserInfo *userInfo) {
if (userInfo.name.length) {
[weakSelf notifyParticipantChange:[NSString stringWithFormat:@"%@加入地理位置共享",
userInfo.name]];
} else {
[weakSelf notifyParticipantChange:[NSString stringWithFormat:@"user<%@>加入地理位置共享",
userId]];
}
}];
}
}
- (void)onParticipantsQuit:(NSString *)userId {
WeakSelf;
if ([userId isEqualToString:[RCIMClient sharedRCIMClient].currentUserInfo.userId]) {
[self notifyParticipantChange:@"你退出地理位置共享"];
} else {
[[RCIM sharedRCIM].userInfoDataSource
getUserInfoWithUserId:userId
completion:^(RCUserInfo *userInfo) {
if (userInfo.name.length) {
[weakSelf notifyParticipantChange:[NSString stringWithFormat:@"%@退出地理位置共享",
userInfo.name]];
} else {
[weakSelf notifyParticipantChange:[NSString stringWithFormat:@"user<%@>退出地理位置共享",
userId]];
}
}];
}
}
- (void)onRealTimeLocationStartFailed:(long)messageId {
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < self.conversationDataRepository.count; i++) {
RCMessageModel *model = [self.conversationDataRepository objectAtIndex:i];
if (model.messageId == messageId) {
model.sentStatus = SentStatus_FAILED;
}
}
NSArray *visibleItem = [self.conversationMessageCollectionView indexPathsForVisibleItems];
for (int i = 0; i < visibleItem.count; i++) {
NSIndexPath *indexPath = visibleItem[i];
RCMessageModel *model = [self.conversationDataRepository objectAtIndex:indexPath.row];
if (model.messageId == messageId) {
[self.conversationMessageCollectionView reloadItemsAtIndexPaths:@[ indexPath ]];
}
}
});
}
- (void)notifyParticipantChange:(NSString *)text {
WeakSelf;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.realTimeLocationStatusView updateText:text];
[weakSelf performSelector:@selector(updateRealTimeLocationStatus) withObject:nil afterDelay:0.5];
});
}
- (void)onFailUpdateLocation:(NSString *)description {
}
位置实时共享
网友评论