集成环信的即时通讯

现在即时通讯比较火,很多公司会用到即时通讯,所以今天给大家讲讲即时通讯,因为没有服务器,所以使用了环信的SDK写了个即时通讯的demo,由于界面比较多,我不会像之前一样直接上代码,而是手把手一点点给大家说,用最简单的代码完成基本的即时通讯功能,尽量使大家看了这篇博客后都能写出一个简单的即时通讯的demo.github地址

上官网注册账号

首先来到环信的官网,然后登陆.没有账号先注册一个.
进去之后创建应用,如图

创建应用界面

点击确定后,来到这个界面,只需要记住应用标示(APPKey)就行,待会儿会在代码里用到它.
屏幕快照 2016-01-04 下午7.18.38.png

然后用cocoapods导入环信SDK,大家可以通过这篇博客来安装cocoapods.

创建项目

打开终端,输入cd,然后将项目入进去回车,就跳到项目地址,输入命令:pod init,然后会生成一个Podfile,双击这个文件,将里面的东西全删了,然后输入:pod 'EaseMobSDK',然后在终端输入命令:pod install(如果不行可以试试:pod install --verbose --no-repo-update).接下来就等着SDK下载安装到项目里了,大概几分钟后就好了.这时候需要双击.xcworkspace的那个文件进去.SDK集成就完成了(不知道为什么官方文档里面写的集成特别复杂,需要导入各种框架,修改很多东西,其实只要终端一条指令就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;
    
    //注册环信
    [[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) {
            //如果验证用户名和密码没有问题就跳转到好友列表界面
            [self.navigationController pushViewController:[[FriendListViewController alloc]init] animated:YES];
            
        } else {
            
            // 显示错误信息的警告
            NSLog(@"%@",error);
        }
    } onQueue:dispatch_get_main_queue()];

}

-(void)jumpToRegister
{
//跳转到注册界面
    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

这个界面很简单,大部分都是界面搭建,当点击登陆时会调用

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

这个方法有2个参数,一个用户名,一个是密码,很容易猜到这就是验证你的用户名和密码是否正确,如果正确就跳转到好友列表界面,如果不对就会在控制台打印相应的错误.
点击注册就跳转到注册界面.

注册界面
注册界面

由于第一次登陆时没有账号,所以先得注册.先看看代码:

#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回调的异步方法。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) 

一共两个参数,就是用户名和密码,然后判断用户名和密码是否可用,如果可用就返回到登陆界面.

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

这里我只加了一个好友叫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];
    //左侧注销按钮
    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];
    //签协议
    [ [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(@"发送同意成功");
            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(@"发送拒绝成功");
        }
    }];
    [alertController addAction:acceptAction];
    [alertController addAction:rejectAction];
    [self showDetailViewController:alertController sender:nil];
}

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

首先,使用下面这个方法获取到所有的好友,然后将好友放入你的数组,这样好友的信息都在数组里了

[[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没行的信息通过下面这个方法,刚才已经把所有好友的信息方法数组里了,那么就可以通过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;
}

当别人想加你为好友时,会调用这个方法.先弹出一个提示框,然后根据你的选择而去接受或者拒绝.如果接受,就重新导入一遍数据,然后刷新tableView.如果拒绝就调用拒绝的方法.这并不难理解吧.



-(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(@"发送同意成功");
                [[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(@"发送拒绝成功");
        }
    }];
    [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:@"发送" 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没什么好说的,关键是他的.h文件

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

这里用到一个block,当点击按钮时会调用这个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
/**
 *  使用草稿发送一条信息
 *
 *  @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; // 设置为单聊消息
    
    [[EaseMob sharedInstance].chatManager asyncSendMessage:message progress:nil prepare:^(EMMessage *message, EMError *error) {
        
        // 准备发送
    } onQueue:dispatch_get_main_queue() completion:^(EMMessage *message, EMError *error) {
        
        [self reloadChatRecords];
        // 发送完成
    } onQueue:dispatch_get_main_queue()];
}

# pragma mark - Receive Message
/**
 *  当收到了一条消息时
 *
 *  @param message 消息构造体
 */
- (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 注册观察当键盘要出现时
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didKeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    
    // 使用NSNotificationCenter 注册观察当键盘要隐藏时
    [[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];
}

/**
 *  开始执行键盘改变后对应视图的变化
 *
 *  @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];
    
    //判断发送的人是否是当前聊天的人,左边是对面发过来的,右边是自己发过去的
    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

这里面也没什么难点,也有注释,相信大家能懂.
最后看看最后的成果图吧.

成品图

好了,今天就到这里,祝大家天天开心

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 上官网注册账号 首先来到环信的官网,然后登陆.没有账号先注册一个. 进去之后创建应用,如图 创建应用界面 点击确定...
    loneWolf01阅读 500评论 0 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,519评论 25 707
  • 在做App开发的时候,我们往往会用到即时通讯,而其中最常用的就是单聊了。今天就给大家分享一下最新的环信3.x单聊集...
    帅只是表象阅读 3,724评论 11 53
  • 忙、颈椎很不好,一天里还是拿出了近一个多小时练字。有些地方,不得要领,一头雾水,可是自己觉得下面这个字,不太像一个...
    mw568阅读 528评论 0 0
  • 风雨阁下一章 目录 痴儿习文不习武 大明帝国,神武府中。 一名少年正在花园中捧着一本书细...
    田曰天阅读 728评论 21 7