写在前面
融云是一个比较强大的第三方框架,为我们提供了即时通讯的基本组件,导入SDK,进行一些简单的配置就可以看到会话列表,会话界面,为我们省去了很多工作(IMKit)。IMKit对于要求不是很高,不需要很多自定义(也就是很多奇形怪状的东西)的产品来说已经够用了。
下面是我使用到的IMKit的一些简单功能,做一下分享。
在使用之前,当然需要一个应用,这不需要多说了,去注册一个融云开发者账号,然后创建一个应用吧。
0.导入SDK
有两种方式可以将 SDK 导入您的项目中:
通过 CocoaPods
管理依赖;
手动导入 SDK
并管理依赖;
CocoaPods 是目前最流行的 Cocoa
项目库依赖管理工具之一,考虑到便捷与项目的可维护性,我们更推荐您使用 CocoaPods
导入并管理 SDK。
使用 CocoaPods 导入 SDK
1、 CocoaPods 安装
如果您的机器上已经安装了 CocoaPods
,直接进入下一步即可。
如果您的网络已经翻墙,在终端中运行如下命令直接安装:
sudo gem install cocoapods
如果您的网络不能翻墙,可以通过淘宝的 RubyGems镜像 进行安装。在终端依次运行以下命令:
gem sources --add https://ruby.taobao.org/ --remove https://rubygems.org/sudo gem install cocoapods
2、 查询 CocoaPods
源中的融云 SDK
在终端中运行以下命令:
pod search RongCloudIM
命令截图
您可以看到,融云在 CocoaPods 上提供了三种 SDK 下载,IMLib
、不包含VoIP功能的 IMKit
、包含 VoIP 功能的 IMKit
,您按照您的需求安装选择其中一个即可。
如果运行以上命令,没有搜到融云的 SDK 或者搜不到最新的 SDK 版本,您可以运行以下命令,更新一下您本地的 CocoaPods 源列表。
pod repo update
3、 使用 CocoaPods 导入融云 SDK
在您的工程根目录下新建一个 Podfile 文件,在文件中输入以下内容。(在此以 2.4.0 版本为例,其中 “MyApp” 为自己工程名)
target 'MyApp' dopod 'RongCloudIMKit', '2.4.0'end
然后在终端中运行以下命令:
pod install
完成后,CocoaPods 会在您的工程根目录下生成一个 .xcworkspace
文件。您需要通过此文件打开您的工程,而不是之前的 .xcodeproj
。
请不要在 Podfile
中同时引用 IMKit 和 IMLib,因为 CocoaPods
中的 IMKit 包中已经包含了 IMLib 的内容,重复引用会导致无法正常编译。
CocoaPods 使用说明
指定 SDK 版本
CocoaPods
中,有几种设置 SDK 版本的方法。如:
'>= 2.4.X' 会根据您本地的 CocoaPods 源列表,导入不低于 2.4.X
版本的 SDK。
'~> 2.4.X' 会根据您本地的 CocoaPods 源列表,介于 2.4.X~2.5.0
之前版本的 SDK。
我们建议您锁定版本,便于团队开发。如,指定 2.4.X
版本。
pod 'RongCloudIMKit', '2.4.X'
升级工程的 SDK 版本
更新您工程目录中 Podfile 指定的 SDK 版本后,在终端中执行以下命令。
pod update
清除 CocoaPods 本地缓存
特殊情况下,由于网络或者别的原因,通过 CocoaPods
下载的文件可能会有问题。
这时候您可以删除 CocoaPods
的缓存(~/Library/Caches/CocoaPods/Pods/Release
目录),再次导入即可。
查看当前使用的 SDK 版本
您可以在 Podfile.lock
文件中看到您工程中使用的 SDK 版本。
这是我直接贴的融云开发文档,很简单咯。
手动导入:
就是这个文件夹了,拖到你的工程里,就这么简单。
还有一些简单的配置:
---添加系统依赖库
AssetsLibrary.framework
AudioToolbox.framework
AVFoundation.framework
CFNetwork.framework
CoreAudio.framework
CoreGraphics.framework
CoreLocation.framework
CoreMedia.framework
CoreTelephony.framework
CoreVideo.framework
ImageIO.framework
libc++.tbd
libc++abi.tbd
libsqlite3.tbd
libstdc++.tbd
libxml2.tbd
libz.tbd
MapKit.framework
OpenGLES.framework
QuartzCore.framework
SystemConfiguration.framework
UIKit.framework
Photos.framework
有点多,耐心点吧。
---在 Xcode 项目 Build Settings -> Other Linker Flags 中,增加"-ObjC"。
--- 设置 App 支持 http
iOS 9 中,Apple 引入了新特性 App Transport Security (ATS)
,默认要求 App 必须使用 https 协议。详情:What's New in iOS 9.0
融云 SDK 在 iOS9 上需要使用 http,您需要设置在 App 中使用 http。
在 App 的 Info.plist
中添加 NSAppTransportSecurity 类型Dictionary。
在 NSAppTransportSecurity下添加 NSAllowsArbitraryLoads 类型 Boolean,值设为 YES。
好了,导入成功,配置完成!
1.登录融云
---初始化
在你需要使用融云 SDK 功能的类中,import 相关头文件。
#import <RongIMKit/RongIMKit.h>
如果是 Swift 的话,需要在你工程的 Bridging-Header.h文件中加入 SDK 的引用。
#import <RongIMKit/RongIMKit.h>
之前从融云开发者控制台注册得到的 App Key,现在要用上了。
Objective-C 代码
[[RCIM sharedRCIM] initWithAppKey:@"YourTestAppKey"];
Swift 代码
RCIM.sharedRCIM().initWithAppKey("YourTestAppKey")
--- 获取token
必须在服务器端请求 Token,因为获取 Token 时需要提供 App Key 和 App Secret。如果在客户端请求 Token,假如 App 代码一旦被反编译,则会导致 App Key和App Secret 泄露。
这一步就要向你自己的服务端请求token了,这个服务端的同学会做好,然后给你一个接口,你去请求就OK了。不多说。
调试阶段,服务端的同学还没写好接口,所以就先弄个假的吧
这个API调试,获取token
这下面填上用户ID和昵称就提交就获取到了。
---连接融云服务器
获取token成功后,就可以用这个token连接融云服务器了,So easy!
Objective-C 代码
[[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错误");
}];
Swift代码
RCIM.sharedRCIM().connectWithToken("YourTestUserToken",success: { (userId) -> Void in
print("登陆成功。当前登录的用户ID:\(userId)")
}, error: { (status) -> Void in
print("登陆的错误码为:\(status.rawValue)")
}, tokenIncorrect: {
//token过期或者不正确。
//如果设置了token有效期并且token过期,请重新请求您的服务器获取新的token
//如果没有设置token有效期却提示token错误,请检查您客户端和服务器的appkey是否匹配,还有检查您获取token的流程。
print("token错误")
})
一般登录操作写在AppDelegate里面就可以了,在登陆成功后如果要跳转到某个控制器的话,记得回到主线程哦,不然会很卡很卡
// 在主线程更新UI
dispatch_sync(dispatch_get_main_queue(), ^(){
// 就是这里去跳转啊,刷新UI啊!
});
2.会话列表
用IMKit,就是简单到不要不要的,直接用RCConversationListViewController 或者继承它创建一个控制器一个列表就出现了。我是继承RCConversationListViewController创建了一个控制器,再设置一些属性就OK了。
Objective-C 代码:
//重写显示相关的接口,必须先调用super,否则会屏蔽SDK默认的处理
[super viewDidLoad];
//设置需要显示哪些类型的会话,会话类型有很多,选择你需要的就好啦
[self setDisplayConversationTypes:@[@(ConversationType_PRIVATE), @(ConversationType_DISCUSSION), @(ConversationType_CHATROOM), @(ConversationType_GROUP), @(ConversationType_APPSERVICE), @(ConversationType_SYSTEM)]]; //设置需要将哪些类型的会话在会话列表中聚合显示
[self setCollectionConversationType:@[@(ConversationType_DISCUSSION), @(ConversationType_GROUP)]];}
Swift 代码:
//重写显示相关的接口,必须先调用super,否则会屏蔽SDK默认的处理
super.viewDidLoad()
//设置需要显示哪些类型的会话
self.setDisplayConversationTypes([RCConversationType.ConversationType_PRIVATE.rawValue, RCConversationType.ConversationType_DISCUSSION.rawValue, RCConversationType.ConversationType_CHATROOM.rawValue, RCConversationType.ConversationType_GROUP.rawValue, RCConversationType.ConversationType_APPSERVICE.rawValue, RCConversationType.ConversationType_SYSTEM.rawValue])
//设置需要将哪些类型的会话在会话列表中聚合显示
self.setCollectionConversationType([RCConversationType.ConversationType_DISCUSSION.rawValue, RCConversationType.ConversationType_GROUP.rawValue])
就是这么简单, 一个会话列表就设置好了,当然是很原生态的了.
会话列表嘛,肯定要显示用户名、昵称和头像的,userInfoDataSource 和 groupMemberDataSource两个代理会为我们完美的解决显示用户信息的问题。
设置代理:
[RCIM sharedRCIM].userInfoDataSource = AppDelegate;
[RCIM sharedRCIM].groupMemberDataSource = AppDelegate;
实现代理方法:
- (void)getUserInfoWithUserId:(NSString *)userId completion:(void (^)(RCUserInfo *))completion {
NSLog(@"Rong cloud userID: %@", userId);
RCUserInfo *user = [[RCUserInfo alloc] initWithUserId:userId
name:userName
portrait:userPortrait];
return completion(user);
}
当需要显示用户信息的时候,IMKit自己会走这个方法去查询用户信息,然后显示在适当的位置。
不要傻傻的像上面那么写,然后还问怎么没显示,当然不显示了,用户的信息融云的服务器不会帮你存的,在这里要去请求自己的服务器,根据userID查到用户的信息然后赋值 ,return completion(user);
去融云控制台调试一下吧,还是那个API调试,
在下面填上发送者的ID,接收者的ID,发几条试试,收到消息的感觉有没有很爽~
要做一些实质性的工作了!
我们这个项目呢,需要在消息列表的页面顶端做一个通知的cell,它永远置顶,SDK原生的列表当然没有这个了,还好,是置顶的。
列表嘛,当然还是个tableView,tableView的所有控件当然还在,所以这个置顶的cell简单了,做成headerView 就OK啦!
@interface ConversationListVC ()
@property (nonatomic, strong) ConversationListHeaderView *tableHeaderView;
@end
- (UIView *)tableHeaderView {
if (!_tableHeaderView) {
_tableHeaderView = [[ConversationListHeaderView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, SCALE6P(206))];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(headerViewTapped:)];
[_tableHeaderView addGestureRecognizer:tap];
}
return _tableHeaderView;
}
ConversationListHeaderView 这是我自己写的一个view啦,不用在贴代码了哦!
self.conversationListTableView.tableHeaderView = self.tableHeaderView; //设置tableView头部
self.conversationListTableView.tableFooterView = [UIView new];//这样就不会显示多余的分割线啦
self.conversationListTableView.backgroundColor = [UIColor clearColor];// 背景色嘛,clearColor尽量不要用哦,为什么?自行百度!
self.conversationListTableView.layoutMargins = UIEdgeInsetsZero;
self.conversationListTableView.separatorInset = UIEdgeInsetsZero;
self.topCellBackgroundColor = [UIColor blueColor]; // 置顶的消息背景颜色,不设置的话会有淡淡的蓝色
上面是对“conversationListTableView”的一些设置,这时候会话列表会看起来更舒服一些。
刚刚提到了置顶,当然还会有标为已读,标为未读,删除这些需求了,怎么办?tableView嘛,直接去实现代理方法就好了
- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
RCConversationModel *model = self.conversationListDataSource[indexPath.row];
UITableViewRowAction *deleteRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
[[RCIMClient sharedRCIMClient] removeConversation:ConversationType_PRIVATE targetId:model.targetId];
[self refreshConversationTableViewIfNeeded];
}];
UITableViewRowAction *readRoWAction;
if (model.unreadMessageCount > 0) {
readRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"标为已读" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
[[RCIMClient sharedRCIMClient] clearMessagesUnreadStatus:ConversationType_PRIVATE targetId:model.targetId];
[self refreshConversationTableViewIfNeeded];
}];
} else {
readRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"标为未读" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
[[RCIMClient sharedRCIMClient] setMessageReceivedStatus:model.lastestMessageId receivedStatus:ReceivedStatus_UNREAD];
[self refreshConversationTableViewIfNeeded];// 刷新tableView
}];
}
UITableViewRowAction *topRoWAction;
if (model.isTop) {
topRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"取消置顶" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
[[RCIMClient sharedRCIMClient] setConversationToTop:1 targetId:model.targetId isTop:NO];
[self refreshConversationTableViewIfNeeded];
}];
topRoWAction.backgroundColor = HighlightColor;// 设置成你喜欢的颜色
} else {
topRoWAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"置顶" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
[[RCIMClient sharedRCIMClient] setConversationToTop:1 targetId:model.targetId isTop:YES];
[self refreshConversationTableViewIfNeeded];
}];
topRoWAction.backgroundColor = HighlightColor;
}
return @[deleteRoWAction, readRoWAction, topRoWAction];
}
我做的好像有点简单粗暴,哪位有好方法要告诉我哦!
--- 改变一下cell的样子,我们这个基本没改变什么,只是置顶的时候我们需要一个小标志,我就在这里直接改了
- (void)willDisplayConversationTableCell:(RCConversationBaseCell *)cell atIndexPath:(NSIndexPath *)indexPath {//cell将要显示的时候会走这里,在这里做一些微调当然是可以的了,一定要注意要写 super *******
[super willDisplayConversationTableCell:cell atIndexPath:indexPath];
RCConversationModel *model = self.conversationListDataSource[indexPath.row];
// NSLog(@"title : %@", model.conversationTitle);
RCConversationCell *conversationCell = (RCConversationCell *)cell;
cell.layoutMargins = UIEdgeInsetsZero;
// 置顶设置
if (model.isTop) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width - SCALE6P(39), 0, SCALE6P(39), SCALE6P(45))];
imageView.image = [UIImage imageNamed:@"Top_trangle_image"];
imageView.tag = TOP_CONVERSATION_IMAGE_VIEW_TAG;
[conversationCell.contentView addSubview:imageView];
}
RCUserInfo *userInfo = [[RCIM sharedRCIM] getUserInfoCache:model.targetId];
for (UIView *view in conversationCell.contentView.subviews) {
// 这里改了一下下字体哦~
if ([view isKindOfClass:UILabel.class]) {
UILabel *label = (UILabel *)view;
label.font = MainFontOfSize(label.font.pointSize);
if (label.x < conversationCell.contentView.width / 2) {
if (label.y < conversationCell.contentView.height / 3) {
label.textColor = LabelTextDarkGrayColor;
label.font = MainFontOfSize(SCALE6P(40));
} else {
label.textColor = LabelTextLightGrayColor;
label.font = MainFontOfSize(SCALE6P(34));
}
} else {
label.textColor = LabelTextLightGrayColor;
label.font = MainFontOfSize(SCALE6P(32));
}
}
// 非置顶设置
if (!model.isTop && [view isKindOfClass:UIImageView.class] && view.tag == TOP_CONVERSATION_IMAGE_VIEW_TAG) {
[view removeFromSuperview];
}
}
[cell layoutIfNeeded];
}
/*!
在会话列表中,收到新消息的回调
@param notification 收到新消息的notification
@discussion SDK在此方法中有针对消息接收有默认的处理(如刷新等),如果您重写此方法,请注意调用super。
notification的object为RCMessage消息对象,userInfo为NSDictionary对象,其中key值为@"left",value为还剩余未接收的消息数的NSNumber对象。
*/
- (void)didReceiveMessageNotification:(NSNotification *)notification {
[super didReceiveMessageNotification:notification];
}
/*!
点击Cell头像的回调
@param model 会话Cell的数据模型
*/
- (void)didTapCellPortrait:(RCConversationModel *)model {
}
/*!
长按Cell头像的回调
@param model 会话Cell的数据模型
*/
- (void)didLongPressCellPortrait:(RCConversationModel *)model {
}
/*!
点击会话列表中Cell的回调
@param conversationModelType 当前点击的会话的Model类型
@param model 当前点击的会话的Model
@param indexPath 当前会话在列表数据源中的索引值
@discussion 您需要重写此点击事件,跳转到指定会话的聊天界面。
如果点击聚合Cell进入具体的子会话列表,在跳转时,需要将isEnteredToCollectionViewController设置为YES。
*/
- (void)onSelectedTableRow:(RCConversationModelType)conversationModelType
conversationModel:(RCConversationModel *)model
atIndexPath:(NSIndexPath *)indexPath {
ConversationVC *conversationVC = [[ConversationVC alloc] init];
conversationVC.conversationType = model.conversationType;
conversationVC.targetId = model.targetId;
conversationVC.navigationItem.title = model.conversationTitle;
[self.navigationController pushViewController:conversationVC animated:YES]; // 进入聊天界面了
[self.conversationListTableView cellForRowAtIndexPath:indexPath].selected = NO;
}
还有一些小设置,更简单了 直接到头文件里看一看,都是中文,一目了然,这里就不多说了~
3.会话界面
要发送消息了 就要去聊天界面了,这里更是So easy!
上面点击会话列表cell的回调中进入的那个聊天界面,就是继承RCConversationViewController 的一个控制器——ConversationVC,甚至什么都不用做,就这么push进来了!就可以发消息了!就可以看到收到的消息了!(此时感叹融云SDK的开发者们真的都是神级的任务啊)
/*!
输入框中内容发生变化的回调
@param inputTextView 文本输入框
@param range 当前操作的范围
@param text 插入的文本
*/
- (void)inputTextView:(UITextView *)inputTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
[super inputTextView:inputTextView shouldChangeTextInRange:range replacementText:text];
}
/*!
扩展功能板的点击回调
@param pluginBoardView 输入扩展功能板View
@param tag 输入扩展功能(Item)的唯一标示
*/
- (void)pluginBoardView:(RCPluginBoardView*)pluginBoardView clickedItemWithTag:(NSInteger)tag {
[super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
}
/*!
开始录制语音消息的回调
*/
- (void)onBeginRecordEvent {
[super onBeginRecordEvent];
}
#pragma mark 点击头像
- (void)didTapCellPortrait:(NSString *)userId {
}
#pragma mark 点击头像
- (void)didLongPressCellPortrait:(NSString *)userId {
}
/*!
点击Cell中URL的回调
@param url 点击的URL
@param model 消息Cell的数据模型
*/
- (void)didTapUrlInMessageCell:(NSString *)url
model:(RCMessageModel *)model;
/*!
点击Cell中电话号码的回调
@param phoneNumber 点击的电话号码
@param model 消息Cell的数据模型
*/
- (void)didTapPhoneNumberInMessageCell:(NSString *)phoneNumber
model:(RCMessageModel *)model;
不贴代码了,进入头文件看一吧,基本功能都能实现了。
4.消息缓存
刚刚用了IMKit不久,一直不知道缓存去哪了,怎么才能在断网的时候显示聊天记录呢?
当然不用,自己去写缓存啦,SDK为我们做好了,怎么显示?
还记得连接融云服务器的方法么?
在我们服务端获取token成功之后我们会去连接融云的服务器,断网的时候当然我们不能获取到token了。
我们需要在获取token成功之后缓存一下这个token,
[NSUserDefaults standardUserDefaults] setObject:token forKey:KRongCloudToken];
再次登录的时候,如果获取token失败了,那在登录失败的方法中用缓存的token连接融云服务器,
[[RCIM sharedRCIM] connectWithToken:KRongCloudToken success:^(NSString *userId) {
NSLog(@"登陆成功。当前登录的用户ID:%@", userId);
} error:^(RCConnectErrorCode status) {
NSLog(@"登陆的错误码为:%d", status);
} tokenIncorrect:^{
NSLog(@"token错误");
}];
不要怀疑,写上这一段代码,然后呢 缓存消息没出现啊。。。。
当然不会出现,要告诉SDK取哪个用户的缓存信息啊!
[[RCIM sharedRCIM] connectWithToken:KRongCloudToken success:^(NSString *userId) {
[RCIM sharedRCIM].userInfoDataSource = (id<RCIMUserInfoDataSource>)[UIApplication sharedApplication].delegate;
RCUserInfo *currentUserInfo = [[RCUserInfo alloc] initWithUserId:model.userID
name:[model acquireParentBabyName]
portrait:model.userHeadImage];
[RCIM sharedRCIM].currentUserInfo = currentUserInfo;//告诉SDK当前是哪个用户登录就好了,用户信息是你之前缓存的哦~
} error:^(RCConnectErrorCode status) {
} tokenIncorrect:^{
}];
此时,缓存的聊天消息在断网的情况下也出现了,但是看着很别扭,只有聊得记录,却不显示用户的头像和昵称,别担心,你看到这篇分享的时候下载的SDK版本已经做好了用户信息的缓存,在AppDelegate里面设置一下就看到完整的信息了
[RCIM sharedRCIM].enablePersistentUserInfoCache = YES;
缓存就这些吧,其实什么都不用做,设置一下就好了~
5.消息推送
收到消息之后要通知用户啊,这就需要远程推送了。
不知道你的项目之前有没有用到其他的推送,我们的项目在做聊天之前用了极光推送。极光推送和聊天消息推送是不冲突的,一起存在当然没问题。
如果你的项目之前没有做远程推送,那么就需要先去开发者账号中申请推送证书了,怎么申请?这个。。。自行百度吧,融云的文档中也有,很详细。
上传好证书,在AppDelegate中设置一下就OK~
码这么多字,,,不想码了直接去文档看吧,真的很详细!
具体可以参考远程推送文档。
结尾~
一些小设置~
[RCIM sharedRCIM].globalConversationAvatarStyle = RC_USER_AVATAR_CYCLE; // 列表页头像的形状,我设置成了圆形
[RCIM sharedRCIM].globalConversationPortraitSize// 列表页头像的大小,默认是46*46
[RCIM sharedRCIM].globalMessageAvatarStyle // 聊天界面头像的形状
[RCIM sharedRCIM].globalMessagePortraitSize // 聊天界面头像的大小默认是40*40
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"Arrow_back_white"] style:UIBarButtonItemStylePlain target:self action:@selector(returnBtnTapped:)];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"Clear_all_messages"] style:UIBarButtonItemStylePlain target:self action:@selector(clearAllMessage:)];
[self.navigationController.navigationBar setBackgroundImage:[CommonTool imageWithColor:ThemeColor] forBarMetrics:UIBarMetricsDefault];//这都是系统方法喽
还有发送位置和发送照片的时候navigation的颜色有点不太对,我是在AppDelegate里面统一设置的
[[UINavigationBar appearance] setBarTintColor:ThemeColor];
[[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
[[UINavigationBar appearance] setTitleTextAttributes:@{ NSForegroundColorAttributeName : [UIColor whiteColor], NSFontAttributeName : “YouFont”}];
码了半个下午,以后遇到什么了会继续补充~