2016年03月03日更新
使用id A進入聊天,然后返回登陸界面使用id B進入聊天,然后對A發起會話會導致崩潰,原因是沒有關閉A的客戶端,會導致自己和自己發起會話。解決方法是在開啟聊天客戶端之前先關閉之前的客戶端,即改寫『登陸』的事件為下列代碼
- (IBAction)loginBtnClicked:(id)sender {
[[CDChatManager manager]closeWithCallback:^(BOOL succeeded, NSError *error) {
[[CDChatManager manager]openWithClientId:_textField.text callback:^(BOOL succeeded, NSError *error) {
ChatListViewController * chatList = [[ChatListViewController alloc]init];
[self.navigationController pushViewController:chatList animated:YES];
}];
}];
}
寫在最前面
更新了最新版的LeanCloud的SDK
靜態庫的體積確實小了很多,但是有一些LeanCloud改了一小部分方法的名稱
如果按照我的攻略發現代碼有問題的,請把所有的Conv改為Conversation不管這個Conv是在開頭還是結尾還是方法名中間
比如老版的是
[[CDChatManager manager] fetchConvWithOtherId:_textField.text callback:^(AVIMConversation *conversation, NSError *error) {
}];
現在就是
[[CDChatManager manager]fetchConversationWithOtherId:@"78" callback:^(AVIMConversation *conversation, NSError *error) {
}];
其他方法同上,如有其它發現我都會寫在這里
另外老版的CDUserModel協議已經更名為CDUserModelDelegate
- LeanCloud的程序員一定是強迫癥晚期(╯‵□′)╯︵┻━┻
寫在前面
首先附上之前文章的鏈接
[LeanCloud實時通信模塊IOS端快速接入指南(一)]
廢話少說,這一節我們來實現一個相對完整的聊天功能。
功能描述
還記得上一節說過的LeanCloud如何創建聊天嗎?沒錯,你自己的id加上你要聊天的人的id就可以創建一個對話。
那么我們今天做這么一個東西,項目啟動時輸入一個自己的id,確認后彈出類似QQ的最近聊天界面,點擊最近聊天的任意會話進行聊天或對任意指定id發起聊天。
正文
準備項目
新建一個項目,起一個你喜歡的名字
然后是導入SDK,在AppDelegate中注冊,和前邊一樣
搭建頁面結構
這里我們準備的項目在下一節還要繼續用,所以我們使用StoryBoard搭建一個相對完整的項目
*為了盡量照顧大多數人,我這里把構建StoryBoard的過程也貼出來,已經熟練使用的請忽略
刪除自帶的ViewController和Main.storyboard中對應的控件
在Main.storyboard中添加一個標簽欄控制器(TabBarViewController)并設置為根視圖控制器(勾選Is Initial View Controller選項,被設置為根視圖控制器的控件左邊會有箭頭)
去掉標簽欄自帶的兩個視圖控制器,更換成導航視圖控制器(NavigationController)
按住鼠標左鍵不動從標簽欄拖動到導航欄
松開鼠標后選擇ViewControlers
另一個導航欄一樣照做
運行項目,如下圖所示
搭建登陸界面
前一篇說過,我們使用[CDChatManager manager] openWithClientId:@"Tom" callback:^(BOOL succeeded, NSError *error) {}];方法只需要一個自定義的ClientId就可以登陸一個聊天客戶端
因此第一個界面我們就用來輸入這個Id。
由于在StoryBoard中導航欄自帶的根視圖控制器默認是UITableVIewController,所以我們刪掉它,換成普通的視圖控制器
并給它一個標題--『登陸』
登陸界面很簡單,如下所示
新建一個視圖控制器并將其和StroyBoard中的登陸界面關聯起來
實現聊天
給登陸按鈕添加事件之前我們先創建接下來要跳轉的界面--最近聊天列表ChatListViewController,繼承于CDChatListVC
在LoginViewController中引入頭文件
#import <CDChatManager.h>
#import"ChatListViewController.h"
按鈕的點擊事件這么寫
- (IBAction)loginBtnClicked:(id)sender {
[[CDChatManager manager]openWithClientId:_textField.text callback:^(BOOL succeeded, NSError *error) {
ChatListViewController * chatList = [[ChatListViewController alloc]init];
[self.navigationController pushViewController:chatList animated:YES];
}];
}
- 這里解釋一下這個CDChatListVC,和前一篇用到的CDChatRoomVC一樣,這也是LeanChatLib中寫好的一個類,繼承于UITableVIewController,每個cell代表一個會話,第一次進入或者刷新會拉取最近的會話,獲取到一個叫做conversations的數組,然后將conversation作為數據源讀出每一個會話然后展示。點擊會話則進入聊天。我們將在下一節詳細講解他的方法并按照我們的需求加以改造,所以這里我們只需要先知道它是干嘛的就夠了。
剛才說了,這個ChatListViewController是一個UITableVIewController,所以我們對它稍加改造,給它的TableView加一個HeaderView。
在它的viewDidLoad中這么寫
- (void)viewDidLoad {
[super viewDidLoad];
UIView * headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 60)];
self.tableView.tableHeaderView = headerView;
_textField = [[UITextField alloc]initWithFrame:CGRectMake(15, 5, self.view.bounds.size.width - 30, 25)];
_textField.layer.borderColor = [UIColor lightGrayColor].CGColor;
_textField.layer.borderWidth = 1;
[headerView addSubview:_textField];
UIButton * startChat = [UIButton buttonWithType:UIButtonTypeSystem];
startChat.frame = CGRectMake(60, 35, self.view.bounds.size.width - 120, 20);
[startChat setTitle:@"開始聊天" forState:UIControlStateNormal];
[headerView addSubview:startChat];
[startChat addTarget:self action:@selector(startChatClicked:) forControlEvents:UIControlEventTouchUpInside];
}
在添加按鈕的點擊事件之前我們要向之前一樣,去創建一個繼承于CDChatRoomVC的ChatRoomViewController。
把聊天室引入到聊天列表
#import "ChatRoomViewController.h"
然后是按鈕的點擊事件
- (void)startChatClicked:(UIButton *)btn
{
if(_textField.text.length > 0)
{
[[CDChatManager manager] fetchConvWithOtherId:_textField.text callback:^(AVIMConversation *conversation, NSError *error) {
ChatRoomViewController * chatRoom = [[ChatRoomViewController alloc]initWithConv:conversation];
[self.navigationController pushViewController:chatRoom animated:YES];
}];
}
}
因為標簽欄會遮住聊天室下方的輸入控件,所以我們這么做
在ChatListViewController中添加方法
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.tabBarController.tabBar.hidden = NO;
}
在ChatRoomViewController中添加方法
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.tabBarController.tabBar.hidden = YES;
}
到此為止我們已經實現了前面的需求,運行項目
在登錄界面輸入你的id
點擊登陸后輸入你想聊天的人的id,然后點擊開始聊天
出現如下界面
點擊返回
然后你會發現,臥槽?說好的最近聊天呢? 怎么一篇空白?別急,接下來我們就解決這個問題。
顯示最近聊天
剛才點返回的時候,控制臺會給你報一堆警告
前面說過,你第一次進入最近聊天頁或者刷新最近聊天的時候會拉取到最近的會話,但是你拉取的是會話,和你聊天的對象的頭像昵稱等信息并沒有包含在會話對象中,你需要從CDChatManager的代理中獲取到這些信息。因此我們需要實現CDChatManager的CDUserDelegate。這個代理會返回一個遵循CDUserModel協議的對象。
創建一個繼承于NSObject的UserFactory類
引入頭文件
#import <LeanChatLib/LeanChatLib.h>
遵守協議
@interface UserFactory : NSObject <CDUserDelegate>
@end
在UserFactory.m中實現代理方法
為了方便我們把遵循CDUserModel協議的類也在這里聲明
#import "CDUserFactory.h"
@interface CDUser : NSObject <CDUserModel>
@property (nonatomic, strong) NSString *userId;
@property (nonatomic, strong) NSString *username;
@property (nonatomic, strong) NSString *avatarUrl;
@end
@implementation CDUser
@end
@implementation UserFactory
#pragma mark - UserDelegate
- (void)cacheUserByIds:(NSSet *)userIds block: (AVBooleanResultBlock)block {
block(YES, nil);
}
- (id <CDUserModel> )getUserById:(NSString *)userId {
CDUser *user = [[CDUser alloc] init];
user.userId = userId;
//在cell中顯示的聊天對象的名字
user.username = userId;
//聊天對象的頭像
user.avatarUrl = @"http://tp2.sinaimg.cn/5088940253/180/5745995456/1";
return user;
}
@end
然后在Appdelegate.m中添加代理
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
[CDChatManager manager].userDelegate = [[UserFactory alloc]init];
...
return YES;
}
然后再次運行項目,輸入剛才的ID,這個時候我們的最近聊天列表就出現了
- 頭像不顯示的同學請檢查是否打開了ATS。iOS 9 之后默認開啟ATS,無法使用Http請求
關閉方法為在info.plist中添加字段
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
接下來我們要做的是實現點擊最近會話進入聊天
首先讓ChatListViewController遵循協議
interface ChatListViewController ()<CDChatListVCDelegate>
然后在viewDidLoad方法中添加加上一句
self.chatListDelegate = self;
然后實現代理方法
- (void)viewController:(UIViewController *)viewController didSelectConv:(AVIMConversation *)conv
{
ChatRoomViewController * chatRoom = [[ChatRoomViewController alloc]initWithConv:conv];
[self.navigationController pushViewController:chatRoom animated:YES];
}
再運行試試,點擊最近的聊天也可以進入聊天了
總結
到此為止,我們就利用LeanCloud搭建了一個相對完整的聊天,你已經可以用這個東西跟你的小伙伴聊天了。
雖然我寫了這么多亂七八糟的東西,其實總計一下我們只用了三個類就實現了這全部的東西。
這里我們回顧一下
三個類:
- CDChatRoomVC:LeanChatLib提供的聊天室。擁有帶Conversation參數的構造方法,聲明后直接跳轉進來即可。
- CDChatListVC:LeanChatLib提供的聊天列表類,登陸聊天Client后直接跳轉進入或者進入后再登陸Client都可以。
- CDChatManager:LeanChatLib的核心管理類,通過[CDChatManager manager]獲取單例。它的兩個重要方法:
//登陸一個聊天客戶端 openWithClientId:_textField.text callback:^(BOOL succeeded, NSError *error) { };
//獲取一個單聊會話 fetchConvWithOtherId:@"78" callback:^(AVIMConversation *conversation, NSError *error) { }
為了方便理解,這里再多嘴提一句,第一個方法,openClientId。既然有open,對應的也有一個close方法用來關閉聊天客戶端。我們所開啟的這個Client,不像我們學的Button,Label一樣,依附于父視圖,父視圖銷毀,它們也跟著銷毀。Client除非你主動關閉或者直接關掉程序,執行open之后它會一直在后臺處于開啟狀態。這也就意味著,只要你執行了openClient,你可以在程序的任意一個界面執行fetchConv獲取到一個會話然后跳轉到聊天,并不一定是在最近聊天列表才能這么做。這樣就能很方便靈活的使用即時通訊模塊。
這一節到此結束,下一節,我們來學習按照自己的需求改造我們的聊天功能。