xml地图|网站地图|网站标签 [设为首页] [加入收藏]

看这一篇就够,7单聊集成与使用

来源:http://www.ccidsi.com 作者:集成经验 人气:133 发布时间:2020-04-05
摘要:前提把环信SDK和EaseUI成功集成后,小编集成的环信SDK版本是2.x,EaseUI版本是3.x,效果图如下: 版权表明:本文为博主原创作品,未经博主允许不得转发。 architecture.jpg B8E912F7B8310F96A1FD

前提把环信SDK和EaseUI成功集成后,小编集成的环信SDK版本是2.x,EaseUI版本是3.x,效果图如下:

版权表明:本文为博主原创作品,未经博主允许不得转发。

architecture.jpg

图片 1B8E912F7B8310F96A1FD2EFC81F12769.png

供销合作社方今必要做即时通信, 直接用了三方环信了,明天和大家钻探关于 小编做环信集成的经过和坑点,有何不足的地点,还望咱们多多指正

既是要利用三方环信,第一步当然是下载官方demo了,在那边作者用的本子是环信V3.3.2 , 通过查看官方文书档案大家不难开掘, 相比于事前的环信2.0, 环信3.0 中的主题类为 EMClient 类,通过 EMClient 类能够收获到 chatManagergroupManager、contactManager、roomManager对象。原本 2.0 版本的 SDK 相当多办法提供了合伙、异步回调、异步两种办法,3.0 版只提供一块方法(async领头的方法为异步方法)** 大家只需求知道 2.0版本 [EaseMob shareInstance] → 3.0 版本 [EMClient sharedClient] **

环信官方网址:http://www.easemob.com/

在世襲自EaseMessageViewController的类里对音讯举行扩充

  • - sendTextMessage:(NSString )text withExt:(NSDictionaryState of Qatarext其实大家供给的难为以此法子,对那些法子开展重写就OK了,但是环信却把它私有化了,于是只可以按着上面包车型客车的章程开展音信扩充了,第一有的是EaseUI开源的措施,第二有的是自个儿重写的艺术
世家能够根据不一致的须求选取分歧的模块
  • EMClient: 是 SDK 的进口,首要成就报到、退出、连接管理等功效。也是得到此外模块的输入。
  • EMChatManager: 管理音信的收发,达成会话管理等作用。
  • EMContactManager: 负担好朋友的丰硕删除,黑名单的关押。
  • EMGroupManager: 担当群组的治本,成立、删除群组,管理群组成员等作用。
  • EMChatroomManager: 负担闲聊室的关押。
  • 注册环信开垦者账号并创造后台应用
  • 创设并上传推送证件来落到实处离线推送功效
  • 导入SDK,这里推荐应用CocoaPods进行导入,当中有八个版本供大家选取:HyphenateLiteHyphenate 此中前者包括了实时语音此地大家就只是多演讲了,在这里间附上官方的SDK集成网站供大家参照他事他说加以考察集成iOS SDK前计划职业iOS的SDK导入

*在.pch文件中我们援用 #import <Hyphenate/Hyphenate.h> *在AppDelegate.m中:

//1.初始化SDK //NSLog(@"环信做自动登录时沙盒路径%@",NSHomeDirectory; //AppKey:注册的AppKey,详细见下面注释。 //apnsCertName:推送证书名,详细见下面注释。 EMOptions *options = [EMOptions optionsWithAppkey:HUANXIN_APPKEY]; // options.apnsCertName = @"istore_dev"; EMError *error = [[EMClient sharedClient] initializeSDKWithOptions:options]; if  { NSLog(@"环信初始化成功"); }

在登陆页面LoginViewController.m中:

//因为设置了自动登录模式,所以登录之前要注销之前的用户,否则重复登录会抛出异常 EMError *error1 = [[EMClient sharedClient] logout:YES]; if  { NSLog(@"退出之前的用户成功"); }[[EMClient sharedClient] loginWithUsername:_userTextField.text password:_passTextField.text completion:^(NSString *aUsername, EMError *aError){ if  { kSetLogin; NSLog(@"登陆成功,用户名为:%@",aUsername); // 添加菊花 [custom showWaitView:@"登录中..." byView:self.view completion:^{ // 设置自动登录 [EMClient sharedClient].options.isAutoLogin = YES; // }]; } else { NSLog(@"登陆失败%d",aError.code); //这里可以通过EMError这个类,去查看登录失败的原因 } }];

在注册页面RegisterViewController.m中:

//如果注册不成功,需要去环信官网切换注册模式为开放注册,而不是授权注册 EMError *error = [[EMClient sharedClient] registerWithUsername:_userTextField.text password:_passTextField.text]; if (error == nil) { NSLog; kSetLogin;//这里是注册的时候在调用登录方法, 让其登录一次,只有这样下次才能自动登录,只设置自动登录的Boll值是不行的 //也就是说这里的逻辑是一旦让用户注册,如果注册成功直接跳转到我的页面,并设置下次自动登录,并不是注册完成后回到登录页面 [[EMClient sharedClient] loginWithUsername:_userTextField.text password:_passTextField.text completion:^(NSString *aUsername, EMError *aError) { [EMClient sharedClient].options.isAutoLogin = YES; }]; MineViewController *mineVC = [MineViewController new]; mineVC.hidesBottomBarWhenPushed = YES; for (UIViewController *vc in self.navigationController.viewControllers) { if ([vc isKindOfClass:[MineViewController class]]) { [self.navigationController popToViewController:vc animated:YES]; } } }else{ NSLog(@"注册失败%d",error.code); }

设置自动登陆的代办,以至落实逻辑,在AppDelegate.m中:

//2.监听自动登录的状态 //设置代理 [[EMClient sharedClient] addDelegate:self delegateQueue:nil]; //3.如果登录过,直接来到主界面 BOOL isAutoLogin = [EMClient sharedClient].options.isAutoLogin; jLog(@"登录状态为:%d",isAutoLogin); if (isAutoLogin == YES) { self.window.rootViewController = [BaseTabBarController new]; }else{ //部分APP这里就是返回登录页面, 这里就不做操作了 NSLog(@"环信自动登录失败,或者是没有登陆过"); }

内需在乎的是:丰盛代理一定毫无忘了移除代理,那么些权且算四个微细注意点

//移除代理, 因为这里是多播机制- dealloc { [[EMClient sharedClient] removeDelegate:self];}//自动登录的回调- autoLoginDidCompleteWithError:(EMError *)aError{ if  { NSLog(@"自动登录成功"); [CustomView alertMessage:@"环信自动登录成功" view:self.window]; }else{ NSLog(@"自动登录失败%d",aError.code); }}/** 环信 监听网络状态 1.登录成功后,手机无法上网时 2.登录成功后,网络状态变化时 aConnectionState:当前状态 */- didConnectionStateChanged:(EMConnectionState)aConnectionState{ if (aConnectionState == EMConnectionConnected) { NSLog(@"网络连接成功"); }else{ NSLog; //监听网络状态(这里通知的目地是检测到如果没网络的情况下,修改Navigation.title的值) [[NSNotificationCenter defaultCenter] postNotificationName: AFNetworkingReachabilityDidChangeNotification object:nil]; }}/*! * 重连 * 有以下几种情况,会引起该方法的调用: * 1. 登录成功后,手机无法上网时,会调用该回调 * 2. 登录成功后,网络状态变化时,会调用该回调 */- connectionStateDidChange:(EMConnectionState)aConnectionState{ NSLog(@"断线重连不需要其他操作%u",aConnectionState);}//APP进入后台- applicationDidEnterBackground:(UIApplication *)application { [[EMClient sharedClient] applicationDidEnterBackground:application];}//APP将要从后台返回- applicationWillEnterForeground:(UIApplication *)application { [[EMClient sharedClient] applicationWillEnterForeground:application];}

提及底是退出登陆:

- quitLogin:(UIButton *)button { custom = [CustomView new]; if  { [self alertWithTitle:nil message:@"是否确定退出登录?" actionATitle:@"确定" actionAHandler:^(UIAlertAction *action) { [UserInfoClass clearAllInfo]; [UserInfoClass printAllInfo]; NSLog(@"%@",[NSThread currentThread]); //退出登录 [[CustomView new] showWaitView:@"退出登录成功" byView:self.view completion:^{ [[EMClient sharedClient] logout:YES completion:^(EMError *aError) { if  { NSLog(@"退出环信登录成功"); }else{ NSLog(@"退出环信登录失败,%u",aError.code); } }]; [self.navigationController popViewControllerAnimated:YES]; }]; } actionBTitle:@"取消" actionBHandler:nil totalCompletion:nil]; } else { [custom showAlertView:@"您尚未登录" byView:self.view completion:nil]; }}

扩充到此处之后,相信大家就会完成轻便的报到,注册以致自动登陆了,是还是不是也比较容易呢,接下去大致说一下在登入,注册进程中遇见的主题素材。

  1. 引用头文件的时候报错现身:Hyphenate/EMSDK.h’ file no found缓慢解决方法: 换下援引#import <HyphenateLite/HyphenateLite.h>或者#import <Hyphenate/Hyphenate.h>若是此办法拾叁分, 能够试试选中你的品种中的Pods -> EaseUI->Build Phases->Link Binary With Libraries ,点➕->Add Other ,找到工程里面,Pods里面包车型大巴Hyphenate文件夹上边包车型地铁Hyphenate.framework 点击open,重新编写翻译就好了图片 2
  2. 真机上登陆,注册未有效果裁撤措施: 点击工程名进入工程安装 -> BuildSettings -> 搜索bitcode -> 将Enable Bitcode设置为NO
  3. 合併动态库上传AppStore现身难点, 打包上线时报错EPAJERORO途胜 ITMS-90087: "Unsupported Architectures. The executable for xiantaiApp.app/Frameworks/Hyphenate.framework contains unsupported architectures '[x86_64, i386]'."消除办法: 环信:由于 iOS 编写翻译的特殊性,为了便利开采者使用,大家将 i386 x86_64 armv7 arm64 多少个平台都合併到了一同,所以使用动态库上传appstore时须求将i386 x86_64四个阳台删除后,本事健康提交审核在SDK当前路径下试行以下命令删除i386 x86_64多个阳台iOS的SDK导入中有详细地阐明,拿实时音录制版本版本为例 : 实施完以上命令如图所示图片 3运作达成后得到的Hyphenate.framework正是终极的结果,拖进工程,编写翻译打包上架。图片 4留心: 最终获得的包必得真机编写翻译运维,而且工程要安装编写翻译二进制文件General->Embedded Bunaries.*删除i386、x86_64阳台后,SDK会不可能支撑模拟器编写翻译,只须求在上传AppStore时在开展删减,上传后,替换为除去前的SDK,提议先分别把i386、x86_64、arm64、armv7各平台的包拆分到当地,上传App Store时合併arm64、armv7平台,并移入Hyphenate.framework内。上传后,重新把各平台包合并移入动态库*
  4. 照例是包裹错误: ERROR ITMS-90535: "Unexpected CFBundleExecutable Key. 。。。。。。 consider contacting the developer of the framework for an update to address this issue."消弭方法: 从EaseUIResource.bundle中找到info.plist删掉CFBundleExecutable,只怕全体info.plist删掉

接下去我们说一下,会话谈天部分和对话列表的四个部分这里运用的是EaseUI ,它包裹了 IM 功用常用的控件(如谈心会话、会话列表、联系人列表)

请戳这里查看 → EaseUI使用指南在那集成EaseUI的时候,有二种方法:

  1. 使用cocoapods导入 pod 'EaseUI', :git => '', :tag => '3.3.2'(这里笔者引入使用第一种,比较简便,轻便)
  2. 手动导入文本平素将EaseUI拖入已经集成SDK的项目中(注意: 是因为EaseUI中有多少个常用的第三方库 MJRefresh SDWebImage MBProgressHUD。那会跟本人项目中的冲突。

咱俩先来探访使用第一种方法集成时候的经过和碰到的坑点:

坑点1: 使用cocoaPods时候,出现了报错的新闻,发掘无法将环信的EaseUI导入。那时大家跟随提示的一声令下举行创新pods就足以了,首假若pod 难题 本地饭店太旧了, 终端实践pod repo update, 之后在pod search 'Hyphenate' 如果可以找到3.3.0本子, 就足以下载了 podfile 里面 platform 要钦命8.0

在导入实现之后,在.pch文件中引用了#import <EaseUI/EaseUI.h>,编写翻译,恩,居然未有报错,看来能够开展下一步了间接在AppDelegate.m中伊始化EaseUI:

[[EaseSDKHelper shareHelper] hyphenateApplication:application didFinishLaunchingWithOptions:launchOptions appkey:HUANXIN_APPKEY apnsCertName:nil otherConfig:@{kSDKConfigEnableConsoleLogger:[NSNumber numberWithBool:YES]}];

当时,当自个儿自信跑起来了工程,纳尼??无法自动登入了,每一次必需分离登入以往,再登陆贰次今后才干促成全自动登陆,然后当笔者第四回运转为工人身份程的时候开掘自动登入又失效了,什么鬼?!

图片 52C83F9DCCF6D31A0960BCCC31D46A933.jpg坑点2: 直接登陆不可能发送音信, 必需自动登入今后才具发送接收,自动登入大部分时候会走失败的回调终极仰仗万能的环信客服人士提供了本事扶持,一定要说环信的客服依旧很给力的图片 6WX20170614-165444@2x.png原本是利用pods导入了多少个本子的SDK,使用pods导入的同桌们自然要小心那个主题材料呀,不要再次导入,不然会晤世过多不明不白的bug,图片 7I27FF4PMBQQCP{PLIQ{5E4B.jpg

接下去大家看一后一次之种方法:手动导入EaseUI

  1. 第一我们遵照下载好的环信demo中的文件拖入到工程中,图片 8

    假诺一旦集成红包效用,就拉长RedacketSDK

  2. 把demo中的pch文件 拷贝到温馨的pch文件中,并且在谐和独具的pch文件的头和尾增多

#ifdef __OBJC__//#endif
  1. 编写翻译后,工程会并发如下错误:

    图片 9WX20170614-171907@2x.png图片 10

那个是因为用到了UI基Terry的类,但是只导入了Foundation框架,那个荒谬在其余类里也会产出,大家得以手动改正Founfation为UIKit,不过小编不提出那样做,第一这几个做法的工程量十分大, 在其余类里面也要导入,二,不方便人民群众移植,当将来环信更新的时候我们依然必要做相通的操作,这里自个儿的做法的始建一个pch文件,在pch文件之中程导弹入UIKit。消除办法:建三个PCH文件在里面增加如下代码:

#ifdef __OBJC__ #import <UIKit/UIKit.h> #import <Foundation/Foundation.h>#define NSEaseLocalizedString(key, comment) [[NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"EaseUIResource" withExtension:@"bundle"]] localizedStringForKey: value:@"" table:nil]#endif

此间须要潜心早晚要加盟--OBJC --,否则大概会报NSObjcRunTime的谬误

4.环信里边集成的MBProgressHUD SDWebImage MJRefresh 与我们工程中融合为一的那多少个第三方库产生矛盾!撤消格局:删掉工程中自个儿集成的那几个第三方库,恐怕去除环信EaseUI 里面包车型大巴这一个第三方库!须要小心的是:假诺除去的是环信集成的第三方库!由于环信在合龙的第三方库中加了EM前缀! 记得删掉EaseUI 中使用方法的前缀,不然会报错!

万一集成的是不包罗实时音录制的SDK , 手动导入EaseUI的话 , 那么这时候还大概会报Hyphenate/EMSDK.h’ file no found那儿要求把 #import <Hyphenate/Hyphenate.h>注释掉,然后把报错地方的Hyphenate换成HyphenateLite就可以了,和地方提到的第一点是一成不改变的到这里未来,应该未有怎么难点,编写翻译假若成功的话,那么恭喜你了

由来,我们就导入了EaseUI并在appDelegate.m中开端化了EaseUI,接下去大家就先来周密闲聊的页面

EaseUI集成应用其实轻巧非常多众多,里面也卷入了有关头像小名的装置,所需求做的只是把代理方法完成,前提是您的闲聊页面等都以世袭EaseUI里面包车型客车相干的类去做的。

这里给大家推荐环信官方论坛的一个飞速集成谈天的网站:IOS快速集成环信IM - 基于官方的德姆o优化,5秒钟集成环信IM功效

图片 11E04D3C27-B372-4D17-B801-A50C1E395589.jpeg

是因为环信官方只是因而客户名的id进行对话,所以不是好朋友也足以开展闲谈,我们先做三个轻松易行的单聊页面,如图 (PS:客户头像环信并不开展仓库储存,所以大家后期实今世理方法实行拍卖就能够了)

首先我们成立一个ChatViewController类并继续于EaseMessageViewController在ChatViewController.m中:

@interface ChatViewController () < UIAlertViewDelegate, EaseMessageViewControllerDelegate, EaseMessageViewControllerDataSource, EMClientDelegate, UIImagePickerControllerDelegate>{ UIMenuItem *_copyMenuItem; UIMenuItem *_deleteMenuItem; UIMenuItem *_transpondMenuItem;}@property (nonatomic) BOOL isPlayingAudio;@property (nonatomic) NSMutableDictionary *emotionDic; //表情@end

在ViewDidLoad的办法中:大家修改环信的部分装置,让她更符合大家的开支必要

- viewDidLoad { [super viewDidLoad]; self.showRefreshHeader = YES; self.delegate = self; self.dataSource = self; if ([[DeviceInfo SystemVersion] floatValue] >= 7.0) { self.edgesForExtendedLayout = UIRectEdgeNone; } self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; //修改聊天界面的颜色 // self.view.backgroundColor = [UIColor colorWithString:@"#f8f8f8"]; //自定义气泡 [[EaseBaseMessageCell appearance] setSendBubbleBackgroundImage:[[UIImage imageNamed:@"右气泡"] stretchableImageWithLeftCapWidth:5 topCapHeight:35]]; [[EaseBaseMessageCell appearance] setRecvBubbleBackgroundImage:[[UIImage imageNamed:@"左气泡"] stretchableImageWithLeftCapWidth:35 topCapHeight:35]]; //设置头像圆角 [[EaseBaseMessageCell appearance] setAvatarSize:40.f]; [[EaseBaseMessageCell appearance] setAvatarCornerRadius:20.f]; //隐藏对话时的昵称 [EaseBaseMessageCell appearance].messageNameIsHidden = YES; //修改字体高度,这样在隐藏昵称的时候,可以让气泡对齐 [EaseBaseMessageCell appearance].messageNameHeight = 10; //修改发送图片,定位,等的所在的View的颜色... [[EaseChatBarMoreView appearance] setMoreViewBackgroundColor:[UIColor colorWithRed:240 / 255.0 green:242 / 255.0 blue:247 / 255.0 alpha:1.0]]; // [[EaseChatBarMoreView appearance] setMoreViewBackgroundColor:[UIColor colorWithString:@"#0a0a0a"]]; //删除功能模块中的实时通话 [self.chatBarMoreView removeItematIndex:3]; //删除功能模块中的录制视频(注意:删除通话以后,视频的索引变成了3,所以这里还是3哦) [self.chatBarMoreView removeItematIndex:3]; //更改功能模块中的图片和文字 [self.chatBarMoreView updateItemWithImage:[UIImage imageNamed:@"information_photo"] highlightedImage:[UIImage imageNamed:@"information_photo_hl"] title:@"照片" atIndex:0]; [self.chatBarMoreView updateItemWithImage:[UIImage imageNamed:@"information_location"] highlightedImage:[UIImage imageNamed:@"information_location_hl"] title:@"位置" atIndex:1]; [self.chatBarMoreView updateItemWithImage:[UIImage imageNamed:@"information_photograph"] highlightedImage:[UIImage imageNamed:@"information_photograph_hl"] title:@"拍摄" atIndex:2]; //设置按住说话的图片数组// NSArray *arr = @[@"information_voice_one",@"information_voice_two",@"information_voice_three",@"information_voice_four",@"information_voice_five",kDefaultUserHeadImage];// [self.recordView setVoiceMessageAnimationImages:arr]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deleteAllMessages:) name:KNOTIFICATIONNAME_DELETEALLMESSAGE object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector name:@"ExitGroup" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(insertCallMessage:) name:@"insertCallMessage" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCallNotification:) name:@"callOutWithChatter" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCallNotification:) name:@"callControllerClose" object:nil]; //通过会话管理者获取已收发消息 (bug:会话列表已经调用了刷新,如果继续调用的话会出现消息重复的现象)// [self tableViewDidTriggerHeaderRefresh]; //处理表情崩溃 // EaseEmotionManager *manager = [[EaseEmotionManager alloc] initWithType:(EMEmotionDefault) emotionRow:3 emotionCol:7 emotions:[EaseEmoji allEmoji]]; // [self.faceView setEmotionManagers:@[manager]]; //语音动态图片数组 /* NSArray *array = [[NSArray alloc]initWithObjects: [UIImage imageNamed:@"chat_sender_audio_playing_full"], [UIImage imageNamed:@"chat_sender_audio_playing_000"], [UIImage imageNamed:@"chat_sender_audio_playing_001"], [UIImage imageNamed:@"chat_sender_audio_playing_002"], [UIImage imageNamed:@"chat_sender_audio_playing_003"], nil];*/ // [[EaseBaseMessageCell appearance] setSendMessageVoiceAnimationImages:array]; /* NSArray * array1 = [[NSArray alloc] initWithObjects: [UIImage imageNamed:@"chat_receiver_audio_playing_full"], [UIImage imageNamed:@"chat_receiver_audio_playing000"], [UIImage imageNamed:@"chat_receiver_audio_playing001"], [UIImage imageNamed:@"chat_receiver_audio_playing002"], [UIImage imageNamed:@"chat_receiver_audio_playing003"],nil];*/ // [[EaseBaseMessageCell appearance] setRecvMessageVoiceAnimationImages:array1];}

那边要留意的是改动成效模块中的图片和文字的时候,文字是绝非效果的,源码中尚无增加Label的代码,需求大家自身去写,能够加上分类,也得以一向在源码上改,笔者这里由于只是多了Label而已,所以是一直在源码上改的

在EaseChatBarMoreView.m中,下边的措施中增添Label就能够

- updateItemWithImage:(UIImage *)image highlightedImage:(UIImage *)highLightedImage title:(NSString *)title atIndex:(NSInteger)index {

对了,借使要校正ChatBarMoreView的冲天的话,在第220行

if (_maxIndex >=5) { frame.size.height = 150; } else { // 修改高度 frame.size.height = 120; }

在ChatViewController.m中,大家接二连三丰硕:只顾:这里可能会冷俊不禁发掘重复音信。[self tableViewDidTriggerHeaderRefresh]; 检查一下这几个主意是或不是在chatViewController 和EaseMessageViewCOntroller 的ViewDidLoad 里面都调用了,看假诺都有,随意删除三个那个艺术。就ok了!

- viewWillAppear:animated{ [super viewWillAppear:animated]; if (self.conversation.type == EMConversationTypeGroupChat) { if ([[self.conversation.ext objectForKey:@"subject"] length]) { self.title = [self.conversation.ext objectForKey:@"subject"]; } }}

落实收到音讯随后播放音频以至感动

//收到消息的回调- messagesDidReceive:(NSArray *)aMessages { //收到消息时,播放音频 [[EMCDDeviceManager sharedInstance] playNewMessageSound]; //收到消息时, 震动 [[EMCDDeviceManager sharedInstance] playVibration];}

根据服从EaseMessageViewControllerDelegate的代办,完结长按手势的功用,转载,复制,删除如下:

//是否允许长按- messageViewController:(EaseMessageViewController *)viewController canLongPressRowAtIndexPath:(NSIndexPath *)indexPath{ return YES;}//触发长按手势- messageViewController:(EaseMessageViewController *)viewController didLongPressRowAtIndexPath:(NSIndexPath *)indexPath{ 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;}

- _showMenuViewController:showInView andIndexPath:(NSIndexPath *)indexPath messageType:(EMMessageBodyType)messageType{ if (self.menuController == nil) { self.menuController = [UIMenuController sharedMenuController]; } if (_deleteMenuItem == nil) { _deleteMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"删除", @"Delete") action:@selector(deleteMenuAction:)]; } if (_copyMenuItem == nil) { _copyMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"复制", @"Copy") action:@selector(copyMenuAction:)]; } if (_transpondMenuItem == nil) { _transpondMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"转发", @"Transpond") action:@selector(transpondMenuAction:)]; } if (messageType == EMMessageBodyTypeText) { [self.menuController setMenuItems:@[_copyMenuItem, _deleteMenuItem,_transpondMenuItem]]; } else if (messageType == EMMessageBodyTypeImage){ [self.menuController setMenuItems:@[_deleteMenuItem,_transpondMenuItem]]; } else { [self.menuController setMenuItems:@[_deleteMenuItem]]; } [self.menuController setTargetRect:showInView.frame inView:showInView.superview]; [self.menuController setMenuVisible:YES animated:YES];}

- transpondMenuAction:sender{ if (self.menuIndexPath && self.menuIndexPath.row > 0) { id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row];// ContactListSelectViewController *listViewController = [[ContactListSelectViewController alloc] initWithNibName:nil bundle:nil];// listViewController.messageModel = model;// [listViewController tableViewDidTriggerHeaderRefresh];// [self.navigationController pushViewController:listViewController animated:YES]; } self.menuIndexPath = nil;}- copyMenuAction:sender{ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; if (self.menuIndexPath && self.menuIndexPath.row > 0) { id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row]; pasteboard.string = model.text; } self.menuIndexPath = nil;}- deleteMenuAction:sender{ if (self.menuIndexPath && self.menuIndexPath.row > 0) { id<IMessageModel> model = [self.dataArray objectAtIndex:self.menuIndexPath.row]; NSMutableIndexSet *indexs = [NSMutableIndexSet indexSetWithIndex:self.menuIndexPath.row]; NSMutableArray *indexPaths = [NSMutableArray arrayWithObjects:self.menuIndexPath, nil]; [self.conversation deleteMessageWithId:model.message.messageId error:nil]; [self.messsagesSource removeObject:model.message]; if (self.menuIndexPath.row - 1 >= 0) { id nextMessage = nil; id prevMessage = [self.dataArray objectAtIndex:(self.menuIndexPath.row - 1)]; if (self.menuIndexPath.row   1 < [self.dataArray count]) { nextMessage = [self.dataArray objectAtIndex:(self.menuIndexPath.row   1)]; } if ((!nextMessage || [nextMessage isKindOfClass:[NSString class]]) && [prevMessage isKindOfClass:[NSString class]]) { [indexs addIndex:self.menuIndexPath.row - 1]; [indexPaths addObject:[NSIndexPath indexPathForRow:(self.menuIndexPath.row - 1) inSection:0]]; } } [self.dataArray removeObjectsAtIndexes:indexs]; [self.tableView beginUpdates]; [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; [self.tableView endUpdates]; if ([self.dataArray count] == 0) { self.messageTimeIntervalTag = -1; } } self.menuIndexPath = nil;}

丰硕表情,并发送,这里本身并不曾见面其余同学说的神采发送崩溃的难点,可是依然将消除方法贴出来,在ViewDidLoad中,我们能够看一下

//获取表情列表- emotionFormessageViewController:(EaseMessageViewController *)viewController{ NSMutableArray *emotions = [NSMutableArray array]; for (NSString *name in [EaseEmoji allEmoji]) { EaseEmotion *emotion = [[EaseEmotion alloc] initWithName:@"" emotionId:name emotionThumbnail:name emotionOriginal:name emotionOriginalURL:@"" emotionType:EMEmotionDefault]; [emotions addObject:emotion]; } EaseEmotion *temp = [emotions objectAtIndex:0]; EaseEmotionManager *managerDefault = [[EaseEmotionManager alloc] initWithType:EMEmotionDefault emotionRow:3 emotionCol:7 emotions:emotions tagImage:[UIImage imageNamed:temp.emotionId]]; NSMutableArray *emotionGifs = [NSMutableArray array]; _emotionDic = [NSMutableDictionary dictionary]; NSArray *names = @[@"icon_002",@"icon_007",@"icon_010",@"icon_012",@"icon_013",@"icon_018",@"icon_019",@"icon_020",@"icon_021",@"icon_022",@"icon_024",@"icon_027",@"icon_029",@"icon_030",@"icon_035",@"icon_040"]; int index = 0; for (NSString *name in names) { index  ; EaseEmotion *emotion = [[EaseEmotion alloc] initWithName:[NSString stringWithFormat:@"[表情%d]",index] emotionId:[NSString stringWithFormat:@"em%d",(1000   index)] emotionThumbnail:[NSString stringWithFormat:@"%@_cover",name] emotionOriginal:[NSString stringWithFormat:@"%@",name] emotionOriginalURL:@"" emotionType:EMEmotionGif]; [emotionGifs addObject:emotion]; [_emotionDic setObject:emotion forKey:[NSString stringWithFormat:@"em%d",(1000   index)]]; } EaseEmotionManager *managerGif= [[EaseEmotionManager alloc] initWithType:EMEmotionGif emotionRow:2 emotionCol:4 emotions:emotionGifs tagImage:[UIImage imageNamed:@"icon_002_cover"]]; return @[managerDefault,managerGif]; }//判断消息是否为表情消息- isEmotionMessageFormessageViewController:(EaseMessageViewController *)viewController messageModel:(id<IMessageModel>)messageModel{ BOOL flag = NO; if ([messageModel.message.ext objectForKey:MESSAGE_ATTR_IS_BIG_EXPRESSION]) { return YES; } return flag;}//根据消息获取表情信息- (EaseEmotion*)emotionURLFormessageViewController:(EaseMessageViewController *)viewController messageModel:(id<IMessageModel>)messageModel{ NSString *emotionId = [messageModel.message.ext objectForKey:MESSAGE_ATTR_EXPRESSION_ID]; EaseEmotion *emotion = [_emotionDic objectForKey:emotionId]; if (emotion == nil) { emotion = [[EaseEmotion alloc] initWithName:@"" emotionId:emotionId emotionThumbnail:@"" emotionOriginal:@"" emotionOriginalURL:@"" emotionType:EMEmotionGif]; } return emotion;}//获取发送表情消息的扩展字段- (NSDictionary*)emotionExtFormessageViewController:(EaseMessageViewController *)viewController easeEmotion:(EaseEmotion*)easeEmotion{ return @{MESSAGE_ATTR_EXPRESSION_ID:easeEmotion.emotionId,MESSAGE_ATTR_IS_BIG_EXPRESSION:@};}//view标记已读- messageViewControllerMarkAllMessagesAsRead:(EaseMessageViewController *)viewController{ [[NSNotificationCenter defaultCenter] postNotificationName:@"setupUnreadMessageCount" object:nil];}

最后便是贯彻ViewDidLoad中的通告了,这里的文告是删除全部会话,以致对此实时语音的局地贯彻,未有这个须求的同室们方可略过

#pragma mark - EMClientDelegate//当前登录账号在其它设备登录时会接收到此回调- userAccountDidLoginFromOtherDevice{ if ([self.imagePicker.mediaTypes count] > 0 && [[self.imagePicker.mediaTypes objectAtIndex:0] isEqualToString:(NSString *)kUTTypeMovie]) { [self.imagePicker stopVideoCapture]; }}//当前登录账号已经被从服务器端删除时会收到该回调- userAccountDidRemoveFromServer{ if ([self.imagePicker.mediaTypes count] > 0 && [[self.imagePicker.mediaTypes objectAtIndex:0] isEqualToString:(NSString *)kUTTypeMovie]) { [self.imagePicker stopVideoCapture]; }}//服务被禁用- userDidForbidByServer{ if ([self.imagePicker.mediaTypes count] > 0 && [[self.imagePicker.mediaTypes objectAtIndex:0] isEqualToString:(NSString *)kUTTypeMovie]) { [self.imagePicker stopVideoCapture]; }}- showGroupDetailAction{ [self.view endEditing:YES];// if (self.conversation.type == EMConversationTypeGroupChat) {// EMGroupInfoViewController *infoController = [[EMGroupInfoViewController alloc] initWithGroupId:self.conversation.conversationId];// [self.navigationController pushViewController:infoController animated:YES];// }// else if (self.conversation.type == EMConversationTypeChatRoom)// {// ChatroomDetailViewController *detailController = [[ChatroomDetailViewController alloc] initWithChatroomId:self.conversation.conversationId];// [self.navigationController pushViewController:detailController animated:YES];// }}- deleteAllMessages:sender{ if (self.dataArray.count == 0) { [self showHint:NSLocalizedString(@"message.noMessage", @"no messages")]; return; } if ([sender isKindOfClass:[NSNotification class]]) { NSString *groupId = (NSString *)[(NSNotification *)sender object]; BOOL isDelete = [groupId isEqualToString:self.conversation.conversationId]; if (self.conversation.type != EMConversationTypeChat && isDelete) { self.messageTimeIntervalTag = -1; [self.conversation deleteAllMessages:nil]; [self.messsagesSource removeAllObjects]; [self.dataArray removeAllObjects]; [self.tableView reloadData]; [self showHint:NSLocalizedString(@"message.noMessage", @"no messages")]; } } else if ([sender isKindOfClass:[UIButton class]]){ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"prompt", @"Prompt") message:NSLocalizedString(@"sureToDelete", @"please make sure to delete") delegate:self cancelButtonTitle:NSLocalizedString(@"cancel", @"Cancel") otherButtonTitles:NSLocalizedString(@"ok", @"OK"), nil]; [alertView show]; }}- exitChat{ [self.navigationController popToViewController:self animated:NO]; [self.navigationController popViewControllerAnimated:YES];}- insertCallMessage:(NSNotification *)notification{ id object = notification.object; if  { EMMessage *message = (EMMessage *)object; [self addMessageToDataSource:message progress:nil]; [[EMClient sharedClient].chatManager importMessages:@[message] completion:nil]; }}- handleCallNotification:(NSNotification *)notification{ id object = notification.object; if ([object isKindOfClass:[NSDictionary class]]) { //开始call self.isViewDidAppear = NO; } else { //结束call self.isViewDidAppear = YES; }}

利落到近日停止,闲聊页面基本上就多数了,这里要求注重表达的是闲谈页面头像的多寡管理

在这里边环信给出了2种管理头像的情势,让大家一块来看一下,别称和头像的呈现与更新

付出文书档案:http://docs.easemob.com/im/start

1、 环信EaseUI提供的API的实现

方法一:从应用软件服务器获取别名和头像
  • 小名和头像的取得:当接到一条新闻时,获得发送者的客户ID,然后寻找手提式无线电话机本地数据库是不是有此顾客ID的别名和头像,如未有则调用应用程式服务器接口通过客户ID查询出别名和头像,然后保留到地面数据库和缓存,下一次此客商发来消息就可以直接询问缓存或许本地数据库,不须求再一次向APP服务器发起号召。

  • 别名和头像的换代:当点击发送者头像时加载顾客详细情形时从应用程式服务器查询此客户的实际新闻然后更新本地数据库和缓存。当顾客自身更新别名或头像时,也足以发送一条透传新闻到其余客户和客户所在的群,来更新该客户的别称和头像。

一、前言

在温馨做的第多少个类型中,接触到了环信,原本未有对环信接入某些掌握,以致在类型中央银行使境遇了不少的坑,最新环信V3.3.7本子也对金立X进行了适配,今后温馨也可能有一些空闲时间,对环信集成实行规整,方便有关作用飞速支付,上边起首介绍环信集成与使用啊。

- sendTextMessage:(NSString *)text{ [self sendTextMessage:text withExt:nil];}- sendTextMessage:(NSString *)text withExt:(NSDictionary*)ext{ EMMessage *message = [EaseSDKHelper sendTextMessage:text to:self.conversation.conversationId messageType:[self _messageTypeFromConversationType] messageExt:ext]; [self _sendMessage:message];}
格局二:从音信扩张中取得外号和头像
  • 外号和头像的得到:把顾客核心的别称和头像的U奔驰G级L放到新闻的强大中,通过音信传递给接受方,当接到一条音讯时,则能透过音信的扩充得到发送者的外号和头像UEscortL,然后保留到本地数据库和缓存。当展现别名和头像时,请从本土只怕缓存中读取,不要直接从音讯中把赋值拿给分界面(不然当顾客别名改成后,同壹个人交易会示区别的小名)。

  • 别名和头像的翻新:当增加音信中的别称和头像 UEscortI 与当前本土数据库和缓存中的相应数据分歧的时候,供给把新的小名保存到地点数据库和缓存,并下载新的头像并保留到地面数据库和缓存。

*此处我们选择选择方案二,首先大家要实现成储的效益,通过FMDB完结对顾客model的仓库储存,这里我们可以依据本人的供给进行仓库储存相关音讯,在签到成功以后你得先把自身的音讯囤积起来,在改换了个人资料之后,你要立异这里的储存音信。如此那般就能够造成立异头像后历史的头像也会更新*

粗略来讲:流程是这么的,存款和储蓄顾客的model音信 → 把客户消息扩张附加到要发送的音信中去 → 采用到信息之后通过数据源方法赋值到头像上去

#pragma mark - EaseMessageViewControllerDataSource // 数据源方法 - (id<IMessageModel>)messageViewController:(EaseMessageViewController *)viewController modelForMessage:(EMMessage *)message{ id<IMessageModel> model = nil; // 根据聊天消息生成一个数据源Model //NSLog(@"-======%@",message.from); //debugObj(message.ext); model = [[EaseMessageModel alloc] initWithMessage:message]; NSDictionary * messageDic = message.ext; UserInfoModel * userinfoModel = [ChatUserDataManagerHelper queryByuserEaseMobId:messageDic[CHATUSERID]]; if (userinfoModel != nil) { model.nickname = userinfoModel.usernickName; model.avatarURLPath = userinfoModel.userHeaderImageUrl; } // 默认头像 //model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"]; //Placeholder image for network error //项目图片取出错误的时候就用这张代替 model.failImageName = @"icon_Default-Avatar"; return model; } 

这边在贴三个代理方法,供大家查看

/*! @method @brief 获取消息自定义cell @discussion 用户根据messageModel判断是否显示自定义cell。返回nil显示默认cell,否则显示用户自定义cell @param tableView 当前消息视图的tableView @param messageModel 消息模型 @result 返回用户自定义cell */- (UITableViewCell *)messageViewController:(UITableView *)tableView cellForMessageModel:(id<IMessageModel>)messageModel { return nil;}/*! @method @brief 点击消息头像 @discussion 获取用户点击头像回调 @param viewController 当前消息视图 @param messageModel 消息模型 */- messageViewController:(EaseMessageViewController *)viewController didSelectAvatarMessageModel:(id<IMessageModel>)messageModel{ NSLog(@"点击头像回调"); // UserProfileViewController *userprofile = [[UserProfileViewController alloc] initWithUsername:messageModel.message.from]; // [self.navigationController pushViewController:userprofile animated:YES];}

接下去,我们一起来拜候会话列表的完结,雷同的,我们也是创立一个类并继续于EaseConversationListViewController

图片 12WX20170615-101039@2x.png

废话非常的少说,上Code,在MessageViewController.m中在ViewDidLoad中,大家参与如下代码:

//首次进入刷新数据,加载会话列表 [self tableViewDidTriggerHeaderRefresh]; [[EMClient sharedClient].chatManager addDelegate:self delegateQueue:nil];//获取当前所有会话 self.datalistArray = (NSMutableArray *) [[EMClient sharedClient].chatManager getAllConversations];

- viewWillAppear:animated{ [super viewWillAppear:animated]; [self tableViewDidTriggerHeaderRefresh]; [self refreshAndSortView]; self.datalistArray = (NSMutableArray *) [[EMClient sharedClient].chatManager getAllConversations]; //获取当前所有会话 [_messageTableView reloadData];}/** * 收到消息回调 */- didReceiveMessages:(NSArray *)aMessages{ [self tableViewDidTriggerHeaderRefresh]; [self refreshAndSortView]; //刷新内存中的消息 //加载新的会话 self.datalistArray = (NSMutableArray *) [[EMClient sharedClient].chatManager getAllConversations]; //这里需要的话可以加入时间排序}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *funcIdentifier = @"funcIdentifier"; if (indexPath.section == 0) { MsgFuncTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:funcIdentifier]; if  { cell = [[MsgFuncTableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:funcIdentifier]; } UIView *lineView = [UIView new]; lineView.backgroundColor = [UIColor colorWithNumber:kLineColor]; [cell addSubview:lineView]; [lineView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.bottom.equalTo; make.height.equalTo; }]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.imageV.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@",[_funcArray objectAtIndex:0][indexPath.row]]]; cell.label.text = [_funcArray objectAtIndex:1][indexPath.row]; return cell; } else if (indexPath.section == 1) {// MessageChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];// if  {// cell = [[MessageChatTableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:cellIdentifier];// }// 这里开始我们使用环信提供的一种cell EaseConversationCell * cell = [tableView dequeueReusableCellWithIdentifier:@"reuseID"]; if  { cell = [[EaseConversationCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:@"reuseID"]; } EMConversation *conversation = [_datalistArray objectAtIndex:indexPath.row]; // EMConversationTypeChat = 0, 单聊会话 // EMConversationTypeGroupChat, 群聊会话 // EMConversationTypeChatRoom 聊天室会话 switch (conversation.type) { //单聊会话 case EMConversationTypeChat: { //这里有个小坑,刚开始不知道怎么获取到对方的昵称,就用了下面的方法去获取,根据当前的会话是接收方还是发送方来获取发送的对象,或接收的对象,结果有些能获取到,有些返回的Null, // cell.textLabel.text = [conversation lastReceivedMessage].direction == EMMessageDirectionSend? [conversation lastReceivedMessage].to : [conversation lastReceivedMessage].from; cell.titleLabel.text = conversation.conversationId; NSLog(@"发送方%@------接收方%@",[conversation lastReceivedMessage].from,[conversation lastReceivedMessage].to); //头像,我这里用固定的头像 cell.avatarView.image = [UIImage imageNamed:kDefaultUserHeadImage]; //设置头像圆角 cell.avatarView.imageCornerRadius = 20; //是否显示角标 cell.avatarView.showBadge = YES; //未读消息数量 cell.avatarView.badge = conversation.unreadMessagesCount; break; } default: break; } //这里是将会话的最后一条消息装换成具体内容展示 cell.detailLabel.text = [self subTitleMessageByConversation:conversation]; //显示最后一条消息的时间 cell.timeLabel.text = [NSString stringWithFormat:@"%@",[self lastMessageDateByConversation:conversation]]; //添加分割线 UIView *lineView = [UIView new]; lineView.backgroundColor = [UIColor colorWithNumber:kLineColor]; [cell addSubview:lineView]; [lineView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.right.bottom.equalTo; make.height.equalTo; }]; return cell; } else { return [UITableViewCell new]; }}

在UITableView的didSelect中,代码如下:

EMConversation *msgConversation = _datalistArray[indexPath.row]; ChatViewController *chatVC = [[ChatViewController alloc] initWithConversationChatter:msgConversation.conversationId conversationType:EMConversationTypeChat]; chatVC.hidesBottomBarWhenPushed = YES; chatVC.title = msgConversation.conversationId; [self.navigationController pushViewController:chatVC animated:YES];

接下去就是获得最终音信的文字也许项目,以至获得最终一条音讯突显的年月

//得到最后消息文字或者类型-(NSString *)subTitleMessageByConversation:(EMConversation *)conversation{ NSString *ret = @""; EMMessage *lastMessage = [conversation latestMessage]; EMMessageBody * messageBody = lastMessage.body; if (lastMessage) { EMMessageBodyType messageBodytype = lastMessage.body.type; switch (messageBodytype) { // EMMessageBodyTypeText = 1, /*! ~chinese 文本类型 ~english Text */ // EMMessageBodyTypeImage, /*! ~chinese 图片类型 ~english Image */ // EMMessageBodyTypeVideo, /*! ~chinese 视频类型 ~english Video */ // EMMessageBodyTypeLocation, /*! ~chinese 位置类型 ~english Location */ // EMMessageBodyTypeVoice, /*! ~chinese 语音类型 ~english Voice */ // EMMessageBodyTypeFile, /*! ~chinese 文件类型 ~english File */ // EMMessageBodyTypeCmd, /*! ~chinese 命令类型 ~english Command */ //图像类型 case EMMessageBodyTypeImage: { ret = NSLocalizedString(@"[图片消息]", @"[image]"); } break; //文本类型 case EMMessageBodyTypeText: { NSString *didReceiveText = [EaseConvertToCommonEmoticonsHelper convertToSystemEmoticons:((EMTextMessageBody *)messageBody).text]; //表情映射 ret = didReceiveText; } break; //语音类型 case EMMessageBodyTypeVoice: { ret = NSLocalizedString(@"[语音消息]", @"[voice]"); } break; //位置类型 case EMMessageBodyTypeLocation: { ret = NSLocalizedString(@"[地理位置信息]", @"[location]"); } break; //视频类型 case EMMessageBodyTypeVideo: { ret = NSLocalizedString(@"[视频消息]", @"[video]"); } break; default: break; } } return ret;}//获得最后一条消息显示的时间- (NSString *)lastMessageDateByConversation:(EMConversation *)conversation { NSString *latestMessageTime = @""; EMMessage *lastMessage = [conversation latestMessage];; if (lastMessage) { latestMessageTime = [NSDate formattedTimeFromTimeInterval:lastMessage.timestamp]; } return latestMessageTime;}//给加载会话列表添加下拉刷新方法- tableViewDidTriggerHeaderRefresh { [super tableViewDidTriggerHeaderRefresh]; //这里必须写super,完全继承 __weak MessageViewController *weakSelf = self; self.messageTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{ [weakSelf.messageTableView reloadData]; [weakSelf tableViewDidFinishTriggerHeader:YES reload:NO]; // [weakSelf.messageTableView reloadData]; //刷新数据源 // [weakSelf refreshAndSortView]; //刷新内存页面 [weakSelf.messageTableView.mj_header endRefreshing]; //结束刷新 }]; self.messageTableView.mj_header.accessibilityIdentifier = @"refresh_header"; // header.updatedTimeHidden = YES;}

*结束到这里基本上就已经到位轻便的单聊了,至于加上知音关联人列表都比较轻松,我们能够到环信官方网站中友好查看,以往偶然光的话会补上群组,闲聊室这一块的,最终补上两条科学的篇章,我们有连锁须要的话能够去拜会*据说环信达成出殡和下葬/预览文件的效劳基于环信完成实时摄像语音通话成效

结束语:本次轻松集成环信固然成功了,希望我们能多多点拨,多提宝贵意见,有如何不足之处能够在篇章下方留言,希望那篇作品能真的的支援到我们,假设你认为还算不错的话,请点赞或打赏!谢谢!

二、小说介绍内容目录

1、集成首要步骤简单介绍

2、项目集成HyphenateSDK

3、项目集成EaseUI

4、项目具体应用

5、不可计数难点解决

本文由68399皇家赌场发布于集成经验,转载请注明出处:看这一篇就够,7单聊集成与使用

关键词: 篇就够 指南 头像 昵称 消息

最火资讯