集成環(huán)信的即時通訊

現(xiàn)在即時通訊比較火,很多公司會用到即時通訊,所以今天給大家講講即時通訊,因為沒有服務(wù)器,所以使用了環(huán)信的SDK寫了個即時通訊的demo,由于界面比較多,我不會像之前一樣直接上代碼,而是手把手一點點給大家說,用最簡單的代碼完成基本的即時通訊功能,盡量使大家看了這篇博客后都能寫出一個簡單的即時通訊的demo.github地址

上官網(wǎng)注冊賬號

首先來到環(huán)信的官網(wǎng),然后登陸.沒有賬號先注冊一個.
進去之后創(chuàng)建應(yīng)用,如圖

創(chuàng)建應(yīng)用界面

點擊確定后,來到這個界面,只需要記住應(yīng)用標(biāo)示(APPKey)就行,待會兒會在代碼里用到它.
屏幕快照 2016-01-04 下午7.18.38.png

然后用cocoapods導(dǎo)入環(huán)信SDK,大家可以通過這篇博客來安裝cocoapods.

創(chuàng)建項目

打開終端,輸入cd,然后將項目入進去回車,就跳到項目地址,輸入命令:pod init,然后會生成一個Podfile,雙擊這個文件,將里面的東西全刪了,然后輸入:pod 'EaseMobSDK',然后在終端輸入命令:pod install(如果不行可以試試:pod install --verbose --no-repo-update).接下來就等著SDK下載安裝到項目里了,大概幾分鐘后就好了.這時候需要雙擊.xcworkspace的那個文件進去.SDK集成就完成了(不知道為什么官方文檔里面寫的集成特別復(fù)雜,需要導(dǎo)入各種框架,修改很多東西,其實只要終端一條指令就OK了).

AppDelegate

大家還記得剛才的APPKey吧,在AppDelegate里面需要注冊使用.

#import "AppDelegate.h"
#import "ViewController.h"
#import <EaseMob.h>
@interface AppDelegate ()

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    _window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    _window.backgroundColor = [UIColor whiteColor];
    [_window makeKeyAndVisible];
    
    UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:[[ViewController alloc]init]];
    _window.rootViewController = nav;
    
    //注冊環(huán)信
    [[EaseMob sharedInstance]registerSDKWithAppKey:@"xmh123#cdxmh" apnsCertName:@""];
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    [[EaseMob sharedInstance] applicationDidEnterBackground:application];
}

- (void)applicationWillEnterForeground:(UIApplication *)application {

    [[EaseMob sharedInstance] applicationWillEnterForeground:application];
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application {
    [[EaseMob sharedInstance]applicationWillTerminate:application];
}

@end
登陸界面
登陸界面

看看這部分的代碼吧.

#import "ViewController.h"
#import "RegisterViewController.h"
#import "FriendListViewController.h"
#import <EaseMob.h>
@interface ViewController ()
@property(nonatomic, strong)UITextField *userNameTextField;//用戶名
@property(nonatomic, strong)UITextField *passwordTextField;//密碼
@property(nonatomic, strong)UIButton *loginButton;          //登陸按鈕
@property(nonatomic, strong)UIButton *registerButton;      //注冊按鈕
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.navigationController.navigationBar.translucent = NO;
    self.title = @"登陸界面";
    
    UILabel *usernameLabel = [[UILabel alloc]initWithFrame:CGRectMake(20, 100, 80, 50)];
    usernameLabel.text = @"用戶名";
    usernameLabel.font = [UIFont systemFontOfSize:25];
    [self.view addSubview:usernameLabel];
    
    _userNameTextField = [[UITextField alloc]initWithFrame:CGRectMake(usernameLabel.frame.origin.x + usernameLabel.frame.size.width + 10, usernameLabel.frame.origin.y, 250, 50)];
    _userNameTextField.borderStyle = 3;
    _userNameTextField.placeholder = @"請輸入用戶名";
    [self.view addSubview:_userNameTextField];
    
    UILabel *passwordLabel = [[UILabel alloc]initWithFrame:CGRectMake(usernameLabel.frame.origin.x, usernameLabel.frame.origin.y + usernameLabel.frame.size.height + 10, usernameLabel.frame.size.width, usernameLabel.frame.size.height)];
    passwordLabel.text = @"密碼";
    passwordLabel.font = [UIFont systemFontOfSize:25];
    [self.view addSubview:passwordLabel];
    
    _passwordTextField = [[UITextField alloc]initWithFrame:CGRectMake(_userNameTextField.frame.origin.x, passwordLabel.frame.origin.y, _userNameTextField.frame.size.width, _userNameTextField.frame.size.height)];
    _passwordTextField.placeholder = @"請輸入密碼";
    _passwordTextField.borderStyle = 3;
    [self.view addSubview:_passwordTextField];
    
    _loginButton = [UIButton buttonWithType:UIButtonTypeSystem];
    _loginButton.frame = CGRectMake(170, 300, 50, 50);
    _loginButton.titleLabel.font = [UIFont systemFontOfSize:25];
    [_loginButton setTitle:@"登陸" forState:UIControlStateNormal];
    [_loginButton addTarget:self action:@selector(didClickLoginButton) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_loginButton];
    
    _registerButton = [UIButton buttonWithType:UIButtonTypeSystem];
    _registerButton.frame = CGRectMake(170, 410, 50, 50);
    _registerButton.titleLabel.font = [UIFont systemFontOfSize:25];
    [_registerButton setTitle:@"注冊" forState:UIControlStateNormal];
    [_registerButton addTarget:self action:@selector(jumpToRegister) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_registerButton];
}

-(void)didClickLoginButton
{
    [[EaseMob sharedInstance].chatManager asyncLoginWithUsername:_userNameTextField.text password:_passwordTextField.text completion:^(NSDictionary *loginInfo, EMError *error) {
        
        if (!error) {
            //如果驗證用戶名和密碼沒有問題就跳轉(zhuǎn)到好友列表界面
            [self.navigationController pushViewController:[[FriendListViewController alloc]init] animated:YES];
            
        } else {
            
            // 顯示錯誤信息的警告
            NSLog(@"%@",error);
        }
    } onQueue:dispatch_get_main_queue()];

}

-(void)jumpToRegister
{
//跳轉(zhuǎn)到注冊界面
    RegisterViewController *registerVC = [[RegisterViewController alloc]init];
    [self.navigationController presentViewController:registerVC animated:YES completion:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

這個界面很簡單,大部分都是界面搭建,當(dāng)點擊登陸時會調(diào)用

[[EaseMob sharedInstance].chatManager asyncLoginWithUsername:_userNameTextField.text password:_passwordTextField.text completion:^(NSDictionary *loginInfo, EMError *error)

這個方法有2個參數(shù),一個用戶名,一個是密碼,很容易猜到這就是驗證你的用戶名和密碼是否正確,如果正確就跳轉(zhuǎn)到好友列表界面,如果不對就會在控制臺打印相應(yīng)的錯誤.
點擊注冊就跳轉(zhuǎn)到注冊界面.

注冊界面
注冊界面

由于第一次登陸時沒有賬號,所以先得注冊.先看看代碼:

#import "RegisterViewController.h"
#import <EaseMob.h>
@interface RegisterViewController ()
@property(nonatomic, strong)UITextField *userNameTextField;//用戶名
@property(nonatomic, strong)UITextField *passwordTextField;//密碼
@property(nonatomic, strong)UIButton *registerButton;      //注冊按鈕
@end

@implementation RegisterViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.navigationController.navigationBar.translucent = NO;
    self.title = @"登陸界面";
    
    UILabel *usernameLabel = [[UILabel alloc]initWithFrame:CGRectMake(20, 100, 80, 50)];
    usernameLabel.text = @"用戶名";
    usernameLabel.font = [UIFont systemFontOfSize:25];
    [self.view addSubview:usernameLabel];
    
    _userNameTextField = [[UITextField alloc]initWithFrame:CGRectMake(usernameLabel.frame.origin.x + usernameLabel.frame.size.width + 10, usernameLabel.frame.origin.y, 250, 50)];
    _userNameTextField.borderStyle = 3;
    _userNameTextField.placeholder = @"請輸入用戶名";
    [self.view addSubview:_userNameTextField];
    
    UILabel *passwordLabel = [[UILabel alloc]initWithFrame:CGRectMake(usernameLabel.frame.origin.x, usernameLabel.frame.origin.y + usernameLabel.frame.size.height + 10, usernameLabel.frame.size.width, usernameLabel.frame.size.height)];
    passwordLabel.text = @"密碼";
    passwordLabel.font = [UIFont systemFontOfSize:25];
    [self.view addSubview:passwordLabel];
    
    _passwordTextField = [[UITextField alloc]initWithFrame:CGRectMake(_userNameTextField.frame.origin.x, passwordLabel.frame.origin.y, _userNameTextField.frame.size.width, _userNameTextField.frame.size.height)];
    _passwordTextField.placeholder = @"請輸入密碼";
    _passwordTextField.borderStyle = 3;
    [self.view addSubview:_passwordTextField];
    
    _registerButton = [UIButton buttonWithType:UIButtonTypeSystem];
    _registerButton.frame = CGRectMake(170, 330, 50, 50);
    _registerButton.titleLabel.font = [UIFont systemFontOfSize:25];
    [_registerButton setTitle:@"注冊" forState:UIControlStateNormal];
    [_registerButton addTarget:self action:@selector(didClickedRegisterButton:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:_registerButton];
    
    UIButton *backButton = [UIButton buttonWithType:UIButtonTypeSystem];
    backButton.frame = CGRectMake(170, 280, 50, 50);
    backButton.titleLabel.font = [UIFont systemFontOfSize:25];
    [backButton setTitle:@"返回" forState:UIControlStateNormal];
    [backButton addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:backButton];
}

-(void)backAction
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//點擊屏幕時讓鍵盤回收
    [_passwordTextField resignFirstResponder];
    [_userNameTextField resignFirstResponder];
}

-(void)didClickedRegisterButton:(id)sender
{
//  登陸和注冊有3種方法: 1. 同步方法 2. 通過delegate回調(diào)的異步方法晒衩。3.block異步方法
    //其中官方推薦使用block異步方法,所以我這里就用block異步方法

    //開始注冊
    [[EaseMob sharedInstance].chatManager asyncRegisterNewAccount:_userNameTextField.text password:_passwordTextField.text withCompletion:^(NSString *username, NSString *password, EMError *error) {
        if (!error) {
            NSLog(@"注冊成功");
            
            [self dismissViewControllerAnimated:YES completion:nil];
        }
        else
        {
            NSLog(@"%@",error);
        }
    } onQueue:dispatch_get_main_queue()];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
}

@end

這個界面也特別簡單,除了界面搭建,就只有這個方法:

[[EaseMob sharedInstance].chatManager asyncRegisterNewAccount:_userNameTextField.text password:_passwordTextField.text withCompletion:^(NSString *username, NSString *password, EMError *error) 

一共兩個參數(shù),就是用戶名和密碼,然后判斷用戶名和密碼是否可用,如果可用就返回到登陸界面.

好友列表界面
好友列表界面

這里我只加了一個好友叫xiaomei.
好友界面是怎么樣的呢,大家先看看代碼:

#import "FriendListViewController.h"
#import "AddFriendViewController.h"
#import "ChatViewController.h"
#import <EaseMob.h>
@interface FriendListViewController ()<UITableViewDataSource,UITableViewDelegate,EMChatManagerDelegate,EMChatManagerBuddyDelegate>
@property(nonatomic, strong)NSMutableArray *listArray;
@property(nonatomic, strong)UITableView *tableView;
@end

@implementation FriendListViewController

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    }
-(void)loadView
{
    [super loadView];
    self.view.backgroundColor = [UIColor whiteColor];
    //左側(cè)注銷按鈕
    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"注銷" style:UIBarButtonItemStylePlain target:self action:@selector(didClickedCancelButton)];
    self.title = @"好友";
    
    [[EaseMob sharedInstance].chatManager asyncFetchBuddyListWithCompletion:^(NSArray *buddyList, EMError *error) {
        
        if (!error) {
            NSLog(@"獲取成功 -- %@", buddyList);
            
            [_listArray removeAllObjects];
            [_listArray addObjectsFromArray:buddyList];
            [_tableView reloadData];
        }
    } onQueue:dispatch_get_main_queue()];

}

- (void)viewDidLoad {
    [super viewDidLoad];
    _listArray = [NSMutableArray new];
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addbuttonAction)];
    _tableView = [[UITableView alloc]initWithFrame:self.view.frame];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    _tableView.tableFooterView = [[UIView alloc]init];
    [self.view addSubview:_tableView];
    //簽協(xié)議
    [ [EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:dispatch_get_main_queue()];
}

-(void)didClickedCancelButton
{
    //注銷用戶
    [[EaseMob sharedInstance].chatManager asyncLogoffWithUnbindDeviceToken:YES];
    [self.navigationController popViewControllerAnimated:YES];
}

-(void)addbuttonAction
{
    [self.navigationController pushViewController:[[AddFriendViewController alloc]init] animated:YES];
}

# pragma mark - Table View Data Source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return _listArray.count;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 50;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *identifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
    }
    
    EMBuddy * buddy = _listArray[indexPath.row];
    
    cell.textLabel.text = buddy.username;
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    ChatViewController * chatVC = [[ChatViewController alloc]init];
    
    EMBuddy * buddy = _listArray[indexPath.row];
    
    chatVC.name = buddy.username;
    
    [self.navigationController pushViewController:chatVC animated:YES];
}
-(void)didReceiveBuddyRequest:(NSString *)username message:(NSString *)message
{
    UIAlertController * alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"收到來自%@的請求", username] message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction * acceptAction = [UIAlertAction actionWithTitle:@"好" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *  action) {
        EMError * error;
        // 同意好友請求的方法
        if ([[EaseMob sharedInstance].chatManager acceptBuddyRequest:username error:&error] && !error) {
            NSLog(@"發(fā)送同意成功");
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [[EaseMob sharedInstance].chatManager asyncFetchBuddyListWithCompletion:^(NSArray *buddyList, EMError *error) {
                    
                    if (!error) {
                        NSLog(@"獲取成功 -- %@", buddyList);
                        
                        [_listArray removeAllObjects];
                        [_listArray addObjectsFromArray:buddyList];
                        [_tableView reloadData];
                    }
                } onQueue:dispatch_get_main_queue()];
            });
        }
    }];
    UIAlertAction * rejectAction = [UIAlertAction actionWithTitle:@"滾" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
        EMError * error;
        // 拒絕好友請求的方法
        if ([[EaseMob sharedInstance].chatManager rejectBuddyRequest:username reason:@"滾, 快滾!" error:&error] && !error) {
            NSLog(@"發(fā)送拒絕成功");
        }
    }];
    [alertController addAction:acceptAction];
    [alertController addAction:rejectAction];
    [self showDetailViewController:alertController sender:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

首先,使用下面這個方法獲取到所有的好友,然后將好友放入你的數(shù)組,這樣好友的信息都在數(shù)組里了

[[EaseMob sharedInstance].chatManager asyncFetchBuddyListWithCompletion:^(NSArray *buddyList, EMError *error) {
        
        if (!error) {
            NSLog(@"獲取成功 -- %@", buddyList);
            
            [_listArray removeAllObjects];
            [_listArray addObjectsFromArray:buddyList];
            [_tableView reloadData];
        }
    } onQueue:dispatch_get_main_queue()];

tableView沒行的信息通過下面這個方法,剛才已經(jīng)把所有好友的信息方法數(shù)組里了,那么就可以通過EMBuddy * buddy = _listArray[indexPath.row]這個方法獲取單個好友信息,用 cell.textLabel.text = buddy.username給每個cell的text賦值.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *identifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
    }
    
    EMBuddy * buddy = _listArray[indexPath.row];
    
    cell.textLabel.text = buddy.username;
    
    return cell;
}

當(dāng)別人想加你為好友時,會調(diào)用這個方法.先彈出一個提示框,然后根據(jù)你的選擇而去接受或者拒絕.如果接受,就重新導(dǎo)入一遍數(shù)據(jù),然后刷新tableView.如果拒絕就調(diào)用拒絕的方法.這并不難理解吧.



-(void)didReceiveBuddyRequest:(NSString *)username message:(NSString *)message
{
 UIAlertController * alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"收到來自%@的請求", username] message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction * acceptAction = [UIAlertAction actionWithTitle:@"好" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *  action) {
        EMError * error;
        // 同意好友請求的方法
        if ([[EaseMob sharedInstance].chatManager acceptBuddyRequest:username error:&error] && !error) {
            NSLog(@"發(fā)送同意成功");
                [[EaseMob sharedInstance].chatManager asyncFetchBuddyListWithCompletion:^(NSArray *buddyList, EMError *error) {
                    
                    if (!error) {
                        NSLog(@"獲取成功 -- %@", buddyList);
                        
                        [_listArray removeAllObjects];
                        [_listArray addObjectsFromArray:buddyList];
                        [_tableView reloadData];
                    }
                } onQueue:dispatch_get_main_queue()];
        
        }
    }];
    UIAlertAction * rejectAction = [UIAlertAction actionWithTitle:@"滾" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
        EMError * error;
        // 拒絕好友請求的方法
        if ([[EaseMob sharedInstance].chatManager rejectBuddyRequest:username reason:@"滾, 快滾!" error:&error] && !error) {
            NSLog(@"發(fā)送拒絕成功");
        }
    }];
    [alertController addAction:acceptAction];
    [alertController addAction:rejectAction];
    [self showDetailViewController:alertController sender:nil];
聊天界面
聊天界面

在看聊天界面之前先需要自定義一個聊天輸入框,就是下面那個帶一個textfield和button的.

DialogBoxView.m
#import "DialogBoxView.h"
@interface DialogBoxView ()
@property (nonatomic, strong) UITextField * draftTextField;
@property (nonatomic, strong) UIButton * sendButton;

@end

@implementation DialogBoxView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
        [self initView];
    }
    return self;
}

- (void)initView{
    
    [self setBackgroundColor:[UIColor colorWithWhite:0.9 alpha:1]];
    
    _draftTextField = [[UITextField alloc] initWithFrame:CGRectMake(5, 5, self.frame.size.width - 100, self.frame.size.height - 10)];
    [_draftTextField setBorderStyle:(UITextBorderStyleRoundedRect)];
    [_draftTextField setPlaceholder:@"說點什么呢"];
    [_draftTextField setFont:[UIFont systemFontOfSize:13]];
    [self addSubview:_draftTextField];
    
    _sendButton = [UIButton buttonWithType:(UIButtonTypeCustom)];
    [_sendButton setFrame:CGRectMake(self.frame.size.width - 90, 5, 85, self.frame.size.height - 10)];
    [_sendButton setBackgroundColor:[UIColor colorWithRed:1 green:0 blue:128 / 255.0 alpha:1]];
    [_sendButton setTitle:@"發(fā)送" forState:(UIControlStateNormal)];
    [_sendButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [_sendButton.titleLabel setFont:[UIFont boldSystemFontOfSize:15]];
    [_sendButton.layer setMasksToBounds:YES];
    [_sendButton.layer setCornerRadius:4];
    [_sendButton addTarget:self action:@selector(didSendButtonClicked:) forControlEvents:(UIControlEventTouchUpInside)];
    [self addSubview:_sendButton];
}

- (void)didSendButtonClicked:(UIButton *)sender {
    
    if (self.buttonClicked) {
        self.buttonClicked(_draftTextField.text);
    }
    _draftTextField.text = @"";
}

- (NSString *)draftText {
    
    return _draftTextField.text;
}

/*
 // Only override drawRect: if you perform custom drawing.
 // An empty implementation adversely affects performance during animation.
 - (void)drawRect:(CGRect)rect {
 // Drawing code
 }
 */

@end

這個簡單的自定義view沒什么好說的,關(guān)鍵是他的.h文件

DialogBoxView.h
#import <UIKit/UIKit.h>
typedef void(^ButtonClicked)(NSString * draftText);
@interface DialogBoxView : UIView
@property (nonatomic, copy) ButtonClicked buttonClicked;
@end

這里用到一個block,當(dāng)點擊按鈕時會調(diào)用這個block.

接下看看聊天界面的代碼吧.


#import "ChatViewController.h"
#import "DialogBoxView.h"
#import <EaseMob.h>

@interface ChatViewController ()<EMChatManagerDelegate,UITableViewDelegate,UITableViewDataSource>
@property(nonatomic, strong)UITableView *tableView;
@property(nonatomic, strong)EMConversation *conversation;
@property(nonatomic, strong)DialogBoxView *dialogBoxView;
@end

@implementation ChatViewController

-(void)loadView
{
    [super loadView];
    self.title = _name;
    self.navigationController.navigationBar.translucent = NO;
    _tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 50)];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    _tableView.tableFooterView = [[UIView alloc]init];
    [self.view addSubview:_tableView];
}

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    [_tableView setAllowsSelection:NO];
    
    [self registerForKeyboardNotifications];
    
    _dialogBoxView = [[DialogBoxView alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - 114, self.view.frame.size.width, 50)];
    
    __weak typeof(self) weakSelf = self;
    
    _dialogBoxView.buttonClicked = ^(NSString * draftText){
        
        [weakSelf sendMessageWithDraftText:draftText];
    };
    
    [self.view addSubview:_dialogBoxView];
    
    [[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    [self reloadChatRecords];
}

- (void)viewWillDisappear:(BOOL)animated {
    
    [super viewWillDisappear:animated];
    
    // 移除通知中心
    [self removeForKeyboardNotifications];
    
    // 移除代理
    [[EaseMob sharedInstance].chatManager removeDelegate:self];
}

# pragma mark - Send Message
/**
 *  使用草稿發(fā)送一條信息
 *
 *  @param draftText 草稿
 */
- (void)sendMessageWithDraftText:(NSString *)draftText {
    
    EMChatText * chatText = [[EMChatText alloc] initWithText:draftText];
    EMTextMessageBody * body = [[EMTextMessageBody alloc] initWithChatObject:chatText];
    
    // 生成message
    EMMessage * message = [[EMMessage alloc] initWithReceiver:self.name bodies:@[body]];
    message.messageType = eMessageTypeChat; // 設(shè)置為單聊消息
    
    [[EaseMob sharedInstance].chatManager asyncSendMessage:message progress:nil prepare:^(EMMessage *message, EMError *error) {
        
        // 準(zhǔn)備發(fā)送
    } onQueue:dispatch_get_main_queue() completion:^(EMMessage *message, EMError *error) {
        
        [self reloadChatRecords];
        // 發(fā)送完成
    } onQueue:dispatch_get_main_queue()];
}

# pragma mark - Receive Message
/**
 *  當(dāng)收到了一條消息時
 *
 *  @param message 消息構(gòu)造體
 */
- (void)didReceiveMessage:(EMMessage *)message {
    
    [self reloadChatRecords];
}

# pragma mark - Reload Chat Records
/**
 *  重新加載TableView上面顯示的聊天信息, 并移動到最后一行
 */
- (void)reloadChatRecords {
    
    _conversation = [[EaseMob sharedInstance].chatManager conversationForChatter:self.name conversationType:eConversationTypeChat];
    
    [_tableView reloadData];
    
    if ([_conversation loadAllMessages].count > 0) {
        
        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[_conversation loadAllMessages].count - 1 inSection:0] atScrollPosition:(UITableViewScrollPositionBottom) animated:YES];
    }
}

# pragma mark - Keyboard Method
/**
 *  注冊通知中心
 */
- (void)registerForKeyboardNotifications
{
    // 使用NSNotificationCenter 注冊觀察當(dāng)鍵盤要出現(xiàn)時
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didKeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    
    // 使用NSNotificationCenter 注冊觀察當(dāng)鍵盤要隱藏時
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didKeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}

/**
 *  移除通知中心
 */
- (void)removeForKeyboardNotifications {
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

/**
 *  鍵盤將要彈出
 *
 *  @param notification 通知
 */
- (void)didKeyboardWillShow:(NSNotification *)notification {
    
    NSDictionary * info = [notification userInfo];
    
    CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;
    
    NSLog(@"%f", keyboardSize.height);
    
    //輸入框位置動畫加載
    [self begainMoveUpAnimation:keyboardSize.height];
}

/**
 *  鍵盤將要隱藏
 *
 *  @param notification 通知
 */
- (void)didKeyboardWillHide:(NSNotification *)notification {
    
    [self begainMoveUpAnimation:0];
}

/**
 *  開始執(zhí)行鍵盤改變后對應(yīng)視圖的變化
 *
 *  @param height 鍵盤的高度
 */
- (void)begainMoveUpAnimation:(CGFloat)height {
    
    [UIView animateWithDuration:0.3 animations:^{
        
        [_dialogBoxView setFrame:CGRectMake(0, self.view.frame.size.height - (height + 40), _dialogBoxView.frame.size.width, _dialogBoxView.frame.size.height)];
    }];
    
    
    [_tableView layoutIfNeeded];
    
    if ([_conversation loadAllMessages].count > 1) {
        
        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_conversation.loadAllMessages.count - 1 inSection:0] atScrollPosition:(UITableViewScrollPositionMiddle) animated:YES];
    }
}

# pragma mark - Table View Data Source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return _conversation.loadAllMessages.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString *identifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
    }
    
    EMMessage * message = _conversation.loadAllMessages[indexPath.row];
    
    EMTextMessageBody * body = [message.messageBodies lastObject];
    
    //判斷發(fā)送的人是否是當(dāng)前聊天的人,左邊是對面發(fā)過來的,右邊是自己發(fā)過去的
    if ([message.to isEqualToString:self.name]) {
        
        cell.detailTextLabel.text = body.text;
        cell.detailTextLabel.textColor = [UIColor redColor];
        cell.textLabel.text = @"";
        cell.textLabel.textColor = [UIColor blueColor];
        
    } else {
        
        cell.detailTextLabel.text = @"";
        cell.textLabel.text = body.text;
        cell.detailTextLabel.textColor = [UIColor redColor];
        cell.textLabel.textColor = [UIColor blueColor];
    }
    
    return cell;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

這里面也沒什么難點,也有注釋,相信大家能懂.
最后看看最后的成果圖吧.

成品圖

好了,今天就到這里,祝大家天天開心

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怔毛,一起剝皮案震驚了整個濱河市伐厌,隨后出現(xiàn)的幾起案子捏悬,更是在濱河造成了極大的恐慌瓢颅,老刑警劉巖救巷,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幌墓,死亡現(xiàn)場離奇詭異,居然都是意外死亡陶夜,警方通過查閱死者的電腦和手機凛驮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來条辟,“玉大人辐烂,你說我怎么就攤上這事∥婊撸” “怎么了纠修?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長厂僧。 經(jīng)常有香客問我扣草,道長,這世上最難降的妖魔是什么颜屠? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任辰妙,我火速辦了婚禮,結(jié)果婚禮上甫窟,老公的妹妹穿的比我還像新娘密浑。我一直安慰自己,他們只是感情好粗井,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布尔破。 她就那樣靜靜地躺著,像睡著了一般浇衬。 火紅的嫁衣襯著肌膚如雪懒构。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天耘擂,我揣著相機與錄音胆剧,去河邊找鬼。 笑死醉冤,一個胖子當(dāng)著我的面吹牛秩霍,可吹牛的內(nèi)容都是我干的篙悯。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼铃绒,長吁一口氣:“原來是場噩夢啊……” “哼鸽照!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起匿垄,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎归粉,沒想到半個月后椿疗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡糠悼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年届榄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倔喂。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡铝条,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出席噩,到底是詐尸還是另有隱情班缰,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布悼枢,位于F島的核電站埠忘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏馒索。R本人自食惡果不足惜莹妒,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绰上。 院中可真熱鬧旨怠,春花似錦、人聲如沸蜈块。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽百揭。三九已至拘哨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間信峻,已是汗流浹背倦青。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盹舞,地道東北人产镐。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓隘庄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親癣亚。 傳聞我的和親對象是個殘疾皇子丑掺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • 上官網(wǎng)注冊賬號 首先來到環(huán)信的官網(wǎng),然后登陸.沒有賬號先注冊一個. 進去之后創(chuàng)建應(yīng)用,如圖 創(chuàng)建應(yīng)用界面 點擊確定...
    loneWolf01閱讀 500評論 0 0
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,522評論 25 707
  • 在做App開發(fā)的時候,我們往往會用到即時通訊述雾,而其中最常用的就是單聊了街州。今天就給大家分享一下最新的環(huán)信3.x單聊集...
    帥只是表象閱讀 3,724評論 11 53
  • 忙、頸椎很不好玻孟,一天里還是拿出了近一個多小時練字唆缴。有些地方,不得要領(lǐng)黍翎,一頭霧水面徽,可是自己覺得下面這個字,不太像一個...
    mw568閱讀 528評論 0 0
  • 風(fēng)雨閣下一章 目錄 癡兒習(xí)文不習(xí)武 大明帝國,神武府中碰酝。 一名少年正在花園中捧著一本書細(xì)...
    田曰天閱讀 728評論 21 7