1.实现完整的融云聊天功能,
2.使用代理文件获取聊天用户信息,
3.使用的自定制通讯录页面.
4.自定制了badge提示.
5.重写融云的聊天页.
6.使用Swizzling修改融云内置的发送地址页面的navigationbar.tintColor.
1.使用cocoapods接入融云sdk.
2.添加bright文件.
3.在.info文件中添加权限提示字段.
4.在appdelegate文件中注册融云sdk.
5.新建通讯录页面.
6.重写会话页面.
7.connect注册登录用户信息.
8.新建delegate文件获取聊天对象信息.
9.通过delegate实现badge提示功能.
10.添加Swizzing修改融云内置页面的navigationbar
cocoaPods是iOS开发中重要的依赖管理工具,这里默认大家会使用,如果不会请自行学习.
podfile文件内容:
platform :ios, '7.0'
target '你的项目名称' do
use_frameworks!
pod 'RongCloudIM/IMKit'
end
因为我们使用了融云官方的会话页面,所以选择使用IMKit,如果页面全部自定制,可以使用IMLib框架
融云的sdk是使用的OC语言,所以我们Swift调用时需要添加桥接文件:
RCIMDemo-Bridging-Header.h
#ifndef RCIMDemo_Bridging_Header_h
#define RCIMDemo_Bridging_Header_h
#import
///修改融云自带ViewController.navigationBar需要的
#import "UIViewController+Swizzling_h.h"
#endif /* RCIMDemo_Bridging_Header_h.h */
NSCameraUsageDescription
RCIM需要您的相机权限,用以选择背景图片等功能
NSLocationAlwaysAndWhenInUseUsageDescription
RCIM需要您的地理位置权限
NSLocationWhenInUseUsageDescription
RCIM需要您的地理位置权限
NSMicrophoneUsageDescription
RCIM需要使用您的麦克风
NSPhotoLibraryUsageDescription
RCIM需要您的相册权限,用以选择背景图片等功能
///在官网上申请的App Key. 同时获取的App Secret我们并不需要,是后台需要的.
RCIM.shared().initWithAppKey("y745wfm8yjzbv")
///是否将用户信息和群组信息在本地持久化存储
RCIM.shared().enablePersistentUserInfoCache = false
///是否在发送的所有消息中携带当前登录的用户信息
RCIM.shared().enableMessageAttachUserInfo = true
///收到信息的代理
RCIM.shared().receiveMessageDelegate = self
///用户信息提供代理
RCIM.shared().userInfoDataSource = RCIMDataSource
RCIM.shared().groupInfoDataSource = RCIMDataSource
RCIM.shared().groupUserInfoDataSource = RCIMDataSource
import UIKit
///示例数据,正常应该从后台获取.
let personArray = ["jame","ethan","wang","bridge","gai"]
///这里使用的是自己实现的通讯录列表,根据需求可以选择使用融云自带的通讯列表页面,新建自己的viewController继承自RCConversationListViewController
class ViewController: UIViewController {
let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(tableView)
// tableView.separatorStyle = .none
tableView.isOpaque = false
tableView.dataSource = self
tableView.delegate = self
/// reloadData后contentOffset更改,导致布局效果问题 添加三行
tableView.estimatedRowHeight = 0
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0
tableView.showsHorizontalScrollIndicator=true;
tableView.showsVerticalScrollIndicator=false;
tableView.register(EWChatListTableViewCell.self, forCellReuseIdentifier: EWChatListTableViewCell.identifier)
///添加通知刷新badge
NotificationCenter.default.addObserver(self, selector: #selector(reloadData), name: Notification.Name(rawValue: "onRCIMReceive"), object: nil)
}
/// 融云通知获取信息时调用刷新群组信息数
@objc private func reloadData(){
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
deinit {
NotificationCenter.default.removeObserver(self, name: Notification.Name(rawValue: "onRCIMReceive"), object: nil)
}
}
extension ViewController:UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return personArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: EWChatListTableViewCell.identifier) as? EWChatListTableViewCell else {
return EWChatListTableViewCell()
}
/** 这里的badge应该是从userDefault中获取.我在融云控制台注册了userID[0,1,2,3,4].并使用控制台给登录的userID"123456"发送消息.
正常接入时userID也是后台传给融云的,可以通过后台获取.
*/
let dic = getMessageDic(PERSONMESSAGEKEY)
let num = dic["\(indexPath.row)"]
cell.setData(name: personArray[indexPath.row], badge: num ?? 0)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
/// targetID就是目标用户的targetID,是后台传给融云的,我们也是从后台获取
let chat = EWPersonChatViewController(conversationType: .ConversationType_PRIVATE, targetId: "\(indexPath.row)")
self.navigationController?.pushViewController(chat!, animated: true)
var dic = getMessageDic(PERSONMESSAGEKEY)
dic.removeValue(forKey: "\(indexPath.row)")
UserDefaults.standard.set(dic, forKey: PERSONMESSAGEKEY)
self.reloadData()
}
import UIKit
class EWPersonChatViewController: RCConversationViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "聊天页"
///如果需要修改页面样式,可以修改self.conversationMessageCollectionView属性
self.conversationMessageCollectionView.backgroundColor = UIColor.brown
addPhoneButton()
// Do any additional setup after loading the view.
}
/// 当退出聊天页时,将这次聊天badge计数删除.
override func viewWillDisappear(_ animated: Bool) {
var dic = getMessageDic(PERSONMESSAGEKEY)
dic[self.targetId] = 0
UserDefaults.standard.setValue(dic, forKey: PERSONMESSAGEKEY)
}
/// 添加聊天页加号按钮中的功能
func addPhoneButton(){
self.chatSessionInputBarControl.pluginBoardView.insertItem(with: UIImage(named: "PersonChatVC_phone"), title: "拨打电话", tag: 4)
}
/// 实现添加的按钮功能
override func pluginBoardView(_ pluginBoardView: RCPluginBoardView!, clickedItemWithTag tag: Int) {
super.pluginBoardView(pluginBoardView, clickedItemWithTag: tag)
if tag == 4{
let str = "telprompt://" + ("18511111111")
UIApplication.shared.open(URL(string: str)!, options: ["":""], completionHandler: nil)
}
}
}
///融云并不管理用户信息,不管是登录账号还是与之聊天的人的信息,所以我们需要在连接融云服务器时就将登录账号的昵称与头像设定好.这样才能在会话页显示正确的数据
/// token也是从后台获取,理论上顺序是登录时获取后台传来的token,再使用这个token链接融云服务.
RCIM.shared().connect(withToken: "dQBWciyYegIh7UpkkirIqP/V9gk1Pf9ZryuCvogQH2pvFx4QxzGsb+jL8Kx0zVRqv//9jeTWRkR5S5eole51Dw==", success: { (userId) in
///这两个都是从后台获取
let username = "登录账号的昵称"
let iconurl = "登录账号的头像路径"
let currentUserInfo = RCUserInfo(userId: userId!, name: username, portrait: iconurl)
///将设置的用户信息赋值给登录账号
RCIM.shared().currentUserInfo = currentUserInfo
}, error: { (error) in
print(error)
}) {
print("token错误")
}
import Foundation
let RCIMDataSource = CRRCIMuserInfoDataSource.shared
///实现三个代理方法,选择自己需要的使用
class CRRCIMuserInfoDataSource: NSObject,RCIMUserInfoDataSource,RCIMGroupInfoDataSource,RCIMGroupUserInfoDataSource{
static let shared = CRRCIMuserInfoDataSource()
func getUserInfo(withUserId userId: String!, inGroup groupId: String!, completion: ((RCUserInfo?) -> Void)!) {
guard userId != nil else { return }
guard groupId != nil else { return }
}
func getGroupInfo(withGroupId groupId: String!, completion: ((RCGroup?) -> Void)!) {
guard groupId != nil else { return }
}
func getUserInfo(withUserId userId: String!, completion: ((RCUserInfo?) -> Void)!) {
guard userId != nil else { return }
/// 融云进行了线程保护,群组状态下获取数据,需要异步调用自己的请求方法
DispatchQueue.global().async {
/*** 通过网络请求根据userID从后台获取用户信息
CRNetworking.getRongCloudPersonNameAndHeadimage(userId: userId, success: { (response) in
guard let model = response as? CRGetPersonResponseModel else { return }
let userinfo = RCUserInfo.init(userId: userId, name: model.name, portrait: basePicPath + model.head_img)
///通过completion回调返回获取的用户数据
completion(userinfo)
}) { (error) in
CRPrint("获取头像姓名失败")
}
*/
}
}
}
//MARK: - 融云获取信息delegate
extension AppDelegate: RCIMReceiveMessageDelegate {
/// 收到信息时的回调方法
///
/// - Parameters:
/// - message: 信息主体
/// - left: 剩余数量,当一次有大量的信息传入时,融云选择重复调用这个方法的形式,left就是剩余未接收的信息数量
func onRCIMReceive(_ message: RCMessage!, left: Int32) {
switch message.conversationType {
///群组聊天数据
case .ConversationType_GROUP:
var dic = getMessageDic(GROUPMESSAGEKEY)
///以发送者的targetID作为Key将数量存储进字典
guard var num = dic[message.targetId] else {
dic[message.targetId] = 1
UserDefaults.standard.setValue(dic, forKey: GROUPMESSAGEKEY)
///获取信息,发送通知
NotificationCenter.default.post(name: Notification.Name(rawValue: "onRCIMReceive"), object: nil)
return
}
///如果字典内已经有发送者的targetID字段,将存储的消息数量加1
num += 1
dic[message.targetId] = num
///保存
UserDefaults.standard.setValue(dic, forKey: GROUPMESSAGEKEY)
///个人聊天信息
case .ConversationType_PRIVATE:
var dic = getMessageDic(PERSONMESSAGEKEY)
///以发送者的targetID作为Key将数量存储进字典
guard var num = dic[message.targetId] else {
dic[message.targetId] = 1
UserDefaults.standard.setValue(dic, forKey: PERSONMESSAGEKEY)
///获取信息,发送通知
NotificationCenter.default.post(name: Notification.Name(rawValue: "onRCIMReceive"), object: nil)
return
}
///如果字典内已经有发送者的targetID字段,将存储的消息数量加1
num += 1
dic[message.targetId] = num
///保存
UserDefaults.standard.setValue(dic, forKey: PERSONMESSAGEKEY)
default:
return
}
NotificationCenter.default.post(name: Notification.Name(rawValue: "onRCIMReceive"), object: nil)
}
}
swift中使用swizzling并不方便,所以使用OC实现方式.
要在桥接文件中添加import
#import "UIViewController+Swizzling_h.h"
#import
/// swizzling修改融云定位页面navigation.tintColor
/// swift中使用swizzling并不方便,所以使用OC实现方式.
@implementation UIViewController (Swizzling_h)
+ (void)load {
[super load];
//原本的willAppear方法
Method willAppearOriginal = class_getInstanceMethod([self class], @selector(viewWillAppear:));
//我们的willAppear方法
Method willAppearNew = class_getInstanceMethod([self class], @selector(swizzlingViewWillAppear:));
//交换
if (!class_addMethod([self class], @selector(viewWillAppear:), method_getImplementation(willAppearNew), method_getTypeEncoding(willAppearNew))) {
method_exchangeImplementations(willAppearOriginal, willAppearNew);
}
}
- (void)swizzlingViewWillAppear:(BOOL)animated {
[self swizzlingViewWillAppear:animated];
if ([self isMemberOfClass:NSClassFromString(@"RCAlumListTableViewController")] || [self isMemberOfClass:NSClassFromString(@"RCLocationPickerViewController")] || [self isMemberOfClass:NSClassFromString(@"RCLocationViewController")] || [self isMemberOfClass:NSClassFromString(@"PUPhotoPickerHostViewController")]) {
[self configureRongCloudNavigation];
}
}
- (void)configureRongCloudNavigation {
///修改navigation样式为需要的样式,已保证与原生app一致
self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init];
self.navigationItem.leftBarButtonItem.tintColor = [UIColor blackColor];
self.navigationItem.rightBarButtonItem.tintColor = [UIColor blackColor];
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
}
@end
github地址:# RCIMDemo
接入融云SDK也是一个前后端协同操作的过程,我们很多数据都需要从后台获取,所以可以双方多交流.
这种简单的介绍没办法详细的写出完整的步骤,所以如果需要非常建议参考Demo进行研究.
有问题欢迎探讨.