iOS:集成环信EaseMobSDK单聊功能
当然在集成环信之前需要一些准备操作:
1、首先注册环信开发者账号,直接进入环信官网注册即可:http://www.easemob.com
2、按照文档一步一步将需要的文件全部拖入工程中:http://docs.easemob.com/start/start
以下是我集成的文件:使用
EaseUI集成:http://docs.easemob.com/start/300iosclientintegration/140easeuiuseguide
libEaseMobClientSDK.a包
ChatDemo-UI3.0中的ChatView中的聊天控制器
我主要使用EaseMob中这个EaseSDKHelper单例类来注册、登录、获取最新消息、推送等
在App启动程序时:
进入EaseSDKHelper单例类中,添加一些自定义的方法
#pragma mark - init easemob 注册环信 - (void)easemobApplication:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
appkey:(NSString *)appkey
apnsCertName:(NSString *)apnsCertName
otherConfig:(NSDictionary *)otherConfig
{
//注册登录状态监听
// [[NSNotificationCenter defaultCenter] addObserver:self
// selector:@selector(loginStateChange:)
// name:KNOTIFICATION_LOGINCHANGE
// object:nil]; //注册AppDelegate默认回调监听
[self _setupAppDelegateNotifications]; //注册apns
[self _registerRemoteNotification]; //注册easemob sdk
[[EaseMob sharedInstance] registerSDKWithAppKey:appkey
apnsCertName:apnsCertName
otherConfig:otherConfig];
// 注册环信监听
[self registerEaseMobLiteNotification]; //启动easemob sdk
[[EaseMob sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
[[EaseMob sharedInstance].chatManager setIsAutoFetchBuddyList:YES];
} #pragma mark - EMChatManagerLoginDelegate 自动登录代理回调 // 自动登录开始回调
-(void)willAutoLoginWithInfo:(NSDictionary *)loginInfo error:(EMError *)error
{
// UIAlertView *alertView = nil;
if (error) {
// alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"prompt", @"Prompt") message:NSLocalizedString(@"login.errorAutoLogin", @"Automatic logon failure") delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"OK") otherButtonTitles:nil, nil]; //发送自动登陆状态通知
[[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_LOGINCHANGE object:@NO];
}
else{
// alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"prompt", @"Prompt") message:NSLocalizedString(@"login.beginAutoLogin", @"Start automatic login...") delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"OK") otherButtonTitles:nil, nil]; //将旧版的coredata数据导入新的数据库
EMError *error = [[EaseMob sharedInstance].chatManager importDataToNewDatabase];
if (!error) {
[[EaseMob sharedInstance].chatManager loadDataFromDatabase]; //获取数据
[self loadConversations];
}
}
// [alertView show];
} // 自动登录结束回调
-(void)didAutoLoginWithInfo:(NSDictionary *)loginInfo error:(EMError *)error
{
// UIAlertView *alertView = nil;
if (error) {
// alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"prompt", @"Prompt") message:NSLocalizedString(@"login.errorAutoLogin", @"Automatic logon failure") delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"OK") otherButtonTitles:nil, nil]; //发送自动登陆状态通知
[[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_LOGINCHANGE object:@NO];
}
else{
//获取群组列表
[[EaseMob sharedInstance].chatManager asyncFetchMyGroupsList]; // alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"prompt", @"Prompt") message:NSLocalizedString(@"login.endAutoLogin", @"End automatic login...") delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"OK") otherButtonTitles:nil, nil];
} // [alertView show];
} #pragma make - login easemob 用户登录 - (void)loginWithUsername:(NSString *)username
password:(NSString *)password
{
[[EaseMob sharedInstance].chatManager asyncLoginWithUsername:username password:password completion:^(NSDictionary *loginInfo, EMError *error) {
if (!error) {
//设置自动登录
[[EaseMob sharedInstance].chatManager setIsAutoLoginEnabled:YES]; //获取数据
[self loadConversations]; //发送自动登陆状态通知
[[NSNotificationCenter defaultCenter] postNotificationName:KNOTIFICATION_LOGINCHANGE object:@YES];
}
} onQueue:nil];
} #pragma mark - load conversations 加载会话列表
-(void)loadConversations{ // [[EaseMob sharedInstance].chatManager importDataToNewDatabase]; //获取数据库中数据
[[EaseMob sharedInstance].chatManager loadDataFromDatabase]; // 当前登录用户回话对象列表
NSArray *conversations = [[EaseMob sharedInstance].chatManager conversations];
if (conversations.count == ) {
//从数据库conversation表获取
conversations = [[EaseMob sharedInstance].chatManager loadAllConversationsFromDatabaseWithAppend2Chat:YES];
}
_conversations = conversations;
}
在会话列表控制器中:
@interface KJAnswerQuestionController ()<UITableViewDataSource,UITableViewDelegate>
/** 所有会话 */
@property (strong,nonatomic)NSArray *arrConversations;
/** 提示视图 */
@property (strong,nonatomic)UIView *showingView;
@end @implementation KJAnswerQuestionController - (void)viewDidLoad {
[super viewDidLoad]; //创建tableView
self.view.backgroundColor = HMColor(, , );
self.tableView = [[UITableView alloc]initWithFrame:self.view.bounds];
self.tableView.dataSource = self;
self.tableView.delegate = self;
self.tableView.tableFooterView = [[UIView alloc]initWithFrame:CGRectZero];
[self.view addSubview:self.tableView];
MoveUnderLine(self.tableView); //创建提示视图
self.showingView = [UIView createViewWithShowingTitle:@"没有聊天记录哟!"];
[self.view addSubview:self.showingView]; //获取数据库中数据
self.arrConversations = [EaseSDKHelper shareHelper].conversations;
[self.tableView reloadData]; //显示隐藏
if (self.arrConversations.count == ) {
[self.showingView setHidden:NO];
[self.tableView setHidden:YES];
}else{
[self.showingView setHidden:YES];
[self.tableView setHidden:NO];
}
} //消息刷新
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated]; NSArray *conversations = [[EaseMob sharedInstance].chatManager conversations];
if (conversations == ) {
conversations = [[EaseMob sharedInstance].chatManager loadAllConversationsFromDatabaseWithAppend2Chat:YES];
}
if (conversations > self.arrConversations) {
self.arrConversations = conversations;
}
[self.tableView reloadData];
} #pragma Mark- 数据源方法
//返回行数
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.arrConversations.count;
} //返回cell
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ //取出单个会话
KJContactCell *cell = [KJContactCell createCellWithTableView:tableView];
EMConversation *conversation = [self.arrConversations objectAtIndex:indexPath.row];
cell.conversation = conversation;
return cell;
} #pragma mark - 代理方法
//设置cell的高度
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return ;
} //选中cell时的处理
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//取出单个会话
EMConversation *conversation = [self.arrConversations objectAtIndex:indexPath.row];
KJChatViewController *chatVC = [[KJChatViewController alloc]initWithConversationChatter:conversation.chatter conversationType:eConversationTypeChat];
chatVC.title = conversation.chatter;
chatVC.conversation = conversation;
chatVC.conversation.enableUnreadMessagesCountEvent = YES;
[self.navigationController pushViewController:chatVC animated:YES];
}
@end
在聊天控制器中,直接集成ChatViewController
#import "ChatViewController.h" @interface KJChatViewController : ChatViewController @end #import "KJChatViewController.h" @interface KJChatViewController () @end @implementation KJChatViewController - (void)viewDidLoad {
[super viewDidLoad];
} //将未读消息标记为已读
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
// [self.conversation markMessageWithId:self.conversation.chatter asRead:YES];
[self.conversation markAllMessagesAsRead:YES];
} @end
在自定义的会话列表cell中,显示会话联系人、最后一条记录、时间
#import <UIKit/UIKit.h> @class KJBadgeButton;
@interface KJContactCell : UITableViewCell
//创建cell
+(instancetype)createCellWithTableView:(UITableView *)tableView;
@property (strong,nonatomic)EMConversation *conversation;
@property (strong,nonatomic)KJBadgeButton *badgeBtn; //提示数字
@end #import "KJContactCell.h"
#import "KJContact.h"
#import "KJBadgeButton.h" #define cellBorder 10
#define iconWidth 40
#define textHeight 30 @interface KJContactCell()
@property (strong,nonatomic)UIImageView *iconView; //头像
@property (strong,nonatomic)UILabel *MainTextLabel; //姓名
@property (strong,nonatomic)UILabel *subTextLabel; //消息
@end @implementation KJContactCell static NSString *reuseIdentifier = @"Cell"; +(instancetype)createCellWithTableView:(UITableView *)tableView{ KJContactCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell) {
cell = [[KJContactCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
}
cell.detailTextLabel.textColor = HMColor(, , );
cell.detailTextLabel.font = fontSize_13;
return cell;
} -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) { //1.头像
self.iconView = [[UIImageView alloc]init];
[self.contentView addSubview:self.iconView]; //2.提示badge
self.badgeBtn = [[KJBadgeButton alloc]initWithFrame:CGRectMake(cellBorder+iconWidth-, , , )];
[self.contentView addSubview:self.badgeBtn]; //3.姓名
self.MainTextLabel = [[UILabel alloc]init];
self.MainTextLabel.textColor = HMColor(, , );
self.MainTextLabel.font = fontSize_14;
[self.contentView addSubview:self.MainTextLabel]; //4.消息
self.subTextLabel = [[UILabel alloc]init];
self.subTextLabel.textColor = HMColor(, , );
self.subTextLabel.font = fontSize_13;
[self.contentView addSubview:self.subTextLabel];
}
return self;
} -(void)layoutSubviews{
[super layoutSubviews]; //1.头像
self.iconView.frame = CGRectMake(cellBorder, cellBorder, iconWidth, iconWidth); //2.姓名
self.MainTextLabel.frame = CGRectMake(CGRectGetMaxX(self.iconView.frame)+*cellBorder, , , textHeight); //3.消息
self.subTextLabel.frame =CGRectMake(CGRectGetMaxX(self.iconView.frame)+*cellBorder, CGRectGetMaxY(self.MainTextLabel.frame), SCREEN_WIDTH-CGRectGetMaxX(self.iconView.frame)-cellBorder, textHeight);
} //接收联系人数据
-(void)setConversation:(EMConversation *)conversation{
_conversation = conversation; //设置头像
self.iconView.image = [UIImage imageNamed:@"head"]; //设置姓名
self.MainTextLabel.text = conversation.chatter; //设置提示数字
[self.badgeBtn setBadgeValue:[NSString stringWithFormat:@"%ld",conversation.unreadMessagesCount]]; //设置历史消息
self.subTextLabel.text = [self latestMessageTitleForConversation:conversation]; //设置历史消息时间
self.detailTextLabel.text = [self latestMessageTimeForConversation:conversation];
} #pragma mark - 最后一条消息展示内容
-(NSString *)latestMessageTitleForConversation:(EMConversation *)conversation
{
//用户获取最后一条message,根据message的messageBodyType展示显示最后一条message对应的文案
NSString *latestMessageTitle = @"";
EMMessage *lastMessage = [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;
} #pragma mark - 最后一条消息展示时间
- (NSString *)latestMessageTimeForConversation:(EMConversation *)conversation
{
//用户获取最后一条message,根据lastMessage中timestamp,自定义时间文案显示(例如:"1分钟前","14:20")
NSString *latestMessageTime = @"";
EMMessage *lastMessage = [conversation latestMessage];;
if (lastMessage) {
latestMessageTime = [NSDate formattedTimeFromTimeInterval:lastMessage.timestamp];
}
return latestMessageTime;
}
@end
消息提醒按钮
#import <UIKit/UIKit.h> @interface KJBadgeButton : UIButton
@property (nonatomic, copy) NSString *badgeValue;
@end #import "KJBadgeButton.h" @implementation KJBadgeButton - (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.hidden = YES;
self.userInteractionEnabled = NO;
[self setBackgroundImage:[UIImage resizedImageWithName:@"main_badge"] forState:UIControlStateNormal];
self.titleLabel.font = [UIFont systemFontOfSize:];
}
return self;
} - (void)setBadgeValue:(NSString *)badgeValue
{
#warning copy
// _badgeValue = badgeValue;
_badgeValue = [badgeValue copy]; if (badgeValue && [badgeValue intValue] != ) {
self.hidden = NO;
// 设置文字
[self setTitle:badgeValue forState:UIControlStateNormal]; // 设置frame
CGRect frame = self.frame;
CGFloat badgeH = self.currentBackgroundImage.size.height;
CGFloat badgeW = self.currentBackgroundImage.size.width;
if (badgeValue.length > ) {
// 文字的尺寸
CGSize badgeSize = [badgeValue sizeWithFont:self.titleLabel.font];
badgeW = badgeSize.width + ;
}
frame.size.width = badgeW;
frame.size.height = badgeH;
self.frame = frame;
} else {
self.hidden = YES;
}
} @end
最后在TabbarController中检测未读消息
//首先得注册代理,监听未读消息数
[[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];
#pragma mark - EMChatManagerChatDelegate
/**
* 历史会话列表更新了会调用
*/
- (void)didUpdateConversationList:(NSArray *)conversationList
{
// 给数据源重新赋值
self.arrConversations = conversationList; //刷新表格
[self.messageVc.tableView reloadData]; // 显示总的未读数
[self showTabBarBadge];
} /**
* 未读消息数改变了会调用
*/
- (void)didUnreadMessagesCountChanged
{
//刷新表格
[self.messageVc.tableView reloadData]; // 显示总的未读数
[self showTabBarBadge];
} /**
* 将总的未读消息数显示到tabBar上
*/
- (void)showTabBarBadge
{
NSInteger totalUnreadCount = ;
for (EMConversation *conversation in self.arrConversations) { //获取所有的联系人发来的未读消息
totalUnreadCount += [conversation unreadMessagesCount]; }
if (totalUnreadCount > ) {
self.messageVc.tabBarItem.badgeValue = [NSString stringWithFormat:@"%ld",totalUnreadCount];
UIApplication *application = [UIApplication sharedApplication];
[application setApplicationIconBadgeNumber:totalUnreadCount];
}else{
self.messageVc.tabBarItem.badgeValue = nil;
UIApplication *application = [UIApplication sharedApplication];
[application setApplicationIconBadgeNumber:];
}
}
//移除代理
-(void)dealloc{
[[EaseMob sharedInstance].chatManager removeDelegate:self];
}
测试后:
以上只是实现了单聊和群聊的功能,那么实时语音和视频如何实现呢,下面这个就是干货:
1.集成实时通话的前提是集成好单聊,并且使用的是libEaseMobClientSDK.a包,因为这个包 包含实时通话的功能
2.将demo3.0中的Call文件(实时通话的界面)以及Resources(通话界面的资源图片)加到你自己的工程中
3.点击实时通话或者视频的按钮,实际是发起的通知,在你工程中的主控制器中监听这个通知,在通知的方法中实现发起实时通话的方法以及跳转到通话界面
4.接收实时通话的回调是 - (void)callSessionStatusChanged:(EMCallSession *)callSession changeReason:(EMCallStatusChangedReason)reason error:(EMError *)error
5.实时通话用的协议是:EMCallManagerDelegate 代理:[[EaseMob sharedInstance].callManager addDelegate:self delegateQueue:nil];
6.具体添加哪些方法看下上传的ViewController文件,按照这个文件中的方法加到自己的主控制器中,demo中的实现在MainViewController.m类
代码如下:
记得先导入call文件:这个是用来进行视频和电话语音的类
ViewController.docx文件:http://i.cnblogs.com/Files.aspx
#import "ChatViewController.h"
@interface KJChatViewController : ChatViewController
@end #import "KJChatViewController.h"
#import "CallViewController.h"
@interface KJChatViewController ()<EMCallManagerDelegate>
@end
@implementation KJChatViewController - (void)viewDidLoad {
[super viewDidLoad]; // 实时通话的代理
[self registerNotifications]; // 监听发起实时通话的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(callOutWithChatter:) name:@"callOutWithChatter" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(callControllerClose:) name:@"callControllerClose" object:nil];
} //将未读消息标记为已读
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
// [self.conversation markMessageWithId:self.conversation.chatter asRead:YES];
[self.conversation markAllMessagesAsRead:YES];
} -(void)registerNotifications
{
[self unregisterNotifications]; [[EaseMob sharedInstance].callManager addDelegate:self delegateQueue:nil];
} -(void)unregisterNotifications
{ [[EaseMob sharedInstance].callManager removeDelegate:self];
} - (void)dealloc
{
[self unregisterNotifications];
} // 发起实时通话
- (void)callOutWithChatter:(NSNotification *)notification
{
id object = notification.object;
if ([object isKindOfClass:[NSDictionary class]]) {
if (![self canRecord]) {
return;
} EMError *error = nil;
NSString *chatter = [object objectForKey:@"chatter"];
EMCallSessionType type = [[object objectForKey:@"type"] intValue];
EMCallSession *callSession = nil;
if (type == eCallSessionTypeAudio) {
callSession = [[EaseMob sharedInstance].callManager asyncMakeVoiceCall:chatter timeout: error:&error];
}
else if (type == eCallSessionTypeVideo){
if (![CallViewController canVideo]) {
return;
}
callSession = [[EaseMob sharedInstance].callManager asyncMakeVideoCall:chatter timeout: error:&error];
} if (callSession && !error) {
[[EaseMob sharedInstance].callManager removeDelegate:self]; CallViewController *callController = [[CallViewController alloc] initWithSession:callSession isIncoming:NO];
callController.modalPresentationStyle = UIModalPresentationOverFullScreen;
[self presentViewController:callController animated:NO completion:nil];
} if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"error", @"error") message:error.description delegate:nil cancelButtonTitle:NSLocalizedString(@"ok", @"OK") otherButtonTitles:nil, nil];
[alertView show];
}
}
} #pragma mark - call - (BOOL)canRecord
{
__block BOOL bCanRecord = YES;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0"] != NSOrderedAscending)
{
if ([audioSession respondsToSelector:@selector(requestRecordPermission:)]) {
[audioSession performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {
bCanRecord = granted;
}];
}
} if (!bCanRecord) {
UIAlertView * alt = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"setting.microphoneNoAuthority", @"No microphone permissions") message:NSLocalizedString(@"setting.microphoneAuthority", @"Please open in \"Setting\"-\"Privacy\"-\"Microphone\".") delegate:self cancelButtonTitle:nil otherButtonTitles:NSLocalizedString(@"ok", @"OK"), nil];
[alt show];
} return bCanRecord;
} #pragma mark - ICallManagerDelegate // 接收实时通话的回调函数
- (void)callSessionStatusChanged:(EMCallSession *)callSession changeReason:(EMCallStatusChangedReason)reason error:(EMError *)error
{
if (callSession.status == eCallSessionStatusConnected)
{
EMError *error = nil;
do {
BOOL isShowPicker = [[[NSUserDefaults standardUserDefaults] objectForKey:@"isShowPicker"] boolValue];
if (isShowPicker) {
error = [EMError errorWithCode:EMErrorInitFailure andDescription:NSLocalizedString(@"call.initFailed", @"Establish call failure")];
break;
} if (![self canRecord]) {
error = [EMError errorWithCode:EMErrorInitFailure andDescription:NSLocalizedString(@"call.initFailed", @"Establish call failure")];
break;
} #warning 在后台不能进行视频通话
if(callSession.type == eCallSessionTypeVideo && ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive || ![CallViewController canVideo])){
error = [EMError errorWithCode:EMErrorInitFailure andDescription:NSLocalizedString(@"call.initFailed", @"Establish call failure")];
break;
} if (!isShowPicker){
[[EaseMob sharedInstance].callManager removeDelegate:self];
CallViewController *callController = [[CallViewController alloc] initWithSession:callSession isIncoming:YES];
callController.modalPresentationStyle = UIModalPresentationOverFullScreen;
[self presentViewController:callController animated:NO completion:nil]; // EaseMessageViewController是聊天类,根据自己的聊天类写
if ([self.navigationController.topViewController isKindOfClass:[EaseMessageViewController class]])
{
EaseMessageViewController *chatVc = (EaseMessageViewController *)self.navigationController.topViewController;
chatVc.isViewDidAppear = NO;
}
}
} while (); if (error) {
[[EaseMob sharedInstance].callManager asyncEndCall:callSession.sessionId reason:eCallReasonHangup];
return;
}
}
} - (void)callControllerClose:(NSNotification *)notification
{
// AVAudioSession *audioSession = [AVAudioSession sharedInstance];
// [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
// [audioSession setActive:YES error:nil]; [[EaseMob sharedInstance].callManager addDelegate:self delegateQueue:nil];
} @end
演示结果如下:经本人测试,在真机上运行没有问题,实时语音视频聊天均能够实现
左边为电话通话 右边为视频通话
以下为参考资料:
1.基于环信Demo3.0,实现单聊功能:http://www.jianshu.com/p/f53be9664f14
2.集成环信的即时通讯:http://www.jianshu.com/p/b4618ef39274
3.环信聊天界面 - 显示历史会话记录:http://blog.csdn.net/github_26672553/article/details/50719487
4.环信集成 - 加载会话列表:http://blog.csdn.net/u010545480/article/details/49660255
5.扩展表情包:http://apps.timwhitlock.info/emoji/tables/unicode
其他demo下载: https://github.com/zyprosoft/ZYChat
集成视频:http://www.imgeek.org/video/
github:https://github.com/xiayuanquan/EaseMobChat
本人原创,转载须注明出处,谢谢!
iOS:集成环信EaseMobSDK单聊功能的更多相关文章
- [iOS]集成环信SDK然后运行时候crash了-[NSBundle initWithURL:]: nil URL argument'
Crash的reason是-[NSBundle initWithURL:]: nil URL argument' 1.首先我是用cocoapods导入的环信的SDK.然后怎么运行怎么crash. 2. ...
- Android 使用easeui 3.0 集成环信即时通讯 我踩过的坑
0.关于注冊账号就不用说了. 1.创建应用.获取appkey 0.创建应用 1.填写信息 2.获取appkey 2.集成 0.首先新建一个project 1.这里主要介绍使用easeui来集成环信的即 ...
- 李洪强iOS开发本人集成环信的经验总结_01环信SDK的导入
李洪强iOS开发本人集成环信的经验总结_01环信SDK的导入 01 - 直接在项目中导入SDK和一些静态库 这个时候,没有错误的编译没有错误的话,就说明SDK已经配置成功 还有一种方法是用cocoap ...
- 环信集成 2---基于环信Demo3.0,实现单聊功能
这几天在做环信,所以把环信相关的东西拿过来,做个系统点的东西 注意: 这里Demo集成的是带有实时语音功能的(libEaseMobClientSDK.a). 环信库是直接拖拽EaseMobSDK文件夹 ...
- 李洪强iOS开发本人集成环信的经验总结_09_处理好友请求
李洪强iOS开发本人集成环信的经验总结_09_处理好友请求 实现这种效果: 01 - 遵守处理好友请求的代理协议 02 - 设置代理 03 - 实现代理方法 04 - 实现代理中用到的方法
- 李洪强iOS开发本人集成环信的经验总结_08_自动登录补充
李洪强iOS开发本人集成环信的经验总结_08_自动登录补充 来到Appdelegate里面 01 - 遵守自动登录的代理协议 02 - 设置自动登录的代理 03 - 判断与实现 04 - 代理方法的 ...
- 李洪强iOS开发本人集成环信的经验总结_07_监听好友请求
李洪强iOS开发本人集成环信的经验总结_07_监听好友请求 来到Appdalegate中: 遵守代理协议 设置代理 实现监听好友请求的回调的方法
- 李洪强iOS开发本人集成环信的经验总结_06_发送好友请求
李洪强iOS开发本人集成环信的经验总结_06_发送好友请求 同步好友请求 异步好友请求
- 李洪强iOS开发本人集成环信的经验总结_03_注册
李洪强iOS开发本人集成环信的经验总结_03_注册 环信一共提供了三种注册的方法: 01 同步注册: 02 异步注册: 03 - 使用代理回调进行注册,但是3.0没有了,3.0之前有 调用注册 ...
随机推荐
- 提升 web 应用程序的性能(二)
最佳实践 本章将略述能帮助您提升 web 应用程序性能的最佳实践. 减少 HTTP 请求数 每个 HTTP 请求都有开销,包括查找 DNS.创建连接及等待响应,因此削减不必要的请求数可减少不必要的开销 ...
- iOS5中UIViewController的新方法
iOS5中UIViewController的新方法 前言 在苹果的 WWDC2011 大会视频的<Session 101 - What's New in Cocoa> 和<Sessi ...
- Configuration of OpenCV2.1.0 with VS2010
Add in the system Path: C:\Program Files (x86)\OpenCV-2.1.0\build\bin\Debug Project->Project Prop ...
- CentOS源列表
vi /etc/yum.repos.d/CentOS-Base.repo CentOS 5: # CentOS-Base.repo # # The mirror system uses the con ...
- node.js 资料
supervison可以调试nodejs,并能自动布署 键入命令: npm -g install supervisor
- ThinkPHP 3.2.2 在 volist 多重循环嵌套中使用 if 判断标签
今天在 ThinkPHP 3.2.2 的试图模板中使用多重循环,用来把相应类别下对应的文章都依次循环出来,但是无论如何只能循环出类别,类别下的文章无法循环出,( 错误 ) 代码如下: <voli ...
- 第十章:鸟哥的Linux私房菜
第十章.vim程式编辑器 1. vi与vim 1.1 为何要学vim2. vi的使用 2.1 简易执行范例 2.2 按键说明 2.3 一个案例的练习 2.4 vim的暂存档.救援回复与开启时的警告讯息 ...
- 【转】设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成 ...
- React.js model
// tutorial1.js var CommentBox = React.createClass({ render: function() { return ( <div className ...
- [Virtualization][SDN] VXLAN到底是什么 [转]
写在转发之前: 几个月以前,在北大机房和燕园大厦直接拉了一根光钎.两端彼此为校园内公网IP.为了方便连接彼此机房,我做个一个VPN server在燕园的边界,北大机房使用client拨回.两个物理机房 ...