美文网首页第三方应用iOS即时通讯第三方重要集成工具
基于环信SDK的仿微信聊天app【每日更新】

基于环信SDK的仿微信聊天app【每日更新】

作者: 冷洪林 | 来源:发表于2016-11-04 15:13 被阅读588次
    研究环信SDK也有一段时间了,之前看了下环信的官方文档,写的很详细,我们只需要照葫芦画瓢就可以了,但是要独立的完成以款即时通讯的app难度还是很大的,因为需要考虑的地方太多,工作量也很大,楼主上个月辞职了,正好可以静下心来好好研究一下技术,当然做完这个项目还是要继续找工作的(说到这里 不得不吐槽一下,找工作的人真多呀),话不多说,开干,我会每天在简书上更新,希望在成长自己的同时能帮到一些朋友~
    • 集成环信SDK,集成这一步官方文档都写的很清楚了,我就不再赘述:
    • SDK下载地址http://www.easemob.com/download/im
    • 官方文档:http://docs.easemob.com/start/300iosclientintegration/20iossdkimport
    • 集成完后我们需要删除两个东西,这两个是轻量级的SDK,里面功能有限,适合简单的测试,我们这里用完整版本的,所以我们进入项目原文件把这两个东西干掉:


    • 这里要注意一下,如果删除了轻量级的SDK,前面的配置就不能用这个轻量级的,必须用完整版的


    • 万事俱备,可以开干!,首先导入头文件然后再初始化SDK,这些操作都在AppDelegate.m中:
    • 导入头文件
    #import "EaseMob.h"
    
    • 初始化
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        /**
         *  @prama registerSDKWithAppKey 环信官网的appKey
         *  @prama apnsCertName 苹果官网注册的推送证书,楼主没有申请99刀,嘿嘿,这里就不用啦
         *  @prama otherConfig
         */
        [[EaseMob sharedInstance] registerSDKWithAppKey:kEaseMobAppKey
                                  apnsCertName:nil
                                  otherConfig:nil];
        return YES;
    }
    
    • 集成完SDK后,先做生命周期的跟踪
    • 启动
    • 进入后台
    • 从后台进入前台
    • 销毁
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        /**
         *  @prama registerSDKWithAppKey 环信官网的appKey
         *  @prama apnsCertName 苹果官网注册的推送证书,楼主没有申请99刀,嘿嘿,这里就不用啦
         *  @prama otherConfig
         */
        [[EaseMob sharedInstance] registerSDKWithAppKey:kEaseMobAppKey
                                  apnsCertName:nil
                                  otherConfig:nil];
        // 启动
        [[EaseMob sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
        return YES;
    }
    - (void)applicationDidEnterBackground:(UIApplication *)application {
        // 进入后台
        [[EaseMob sharedInstance] applicationDidEnterBackground:application];
    }
    - (void)applicationWillEnterForeground:(UIApplication *)application {
        // 从后台进入前台
        [[EaseMob sharedInstance] applicationWillEnterForeground:application];
    }
    - (void)applicationWillTerminate:(UIApplication *)application {
        // 销毁
        [[EaseMob sharedInstance] applicationWillTerminate:application];
    }
    
    • 搭建登录注册界面,完成拖线:


    注册

    • 注册模式分两种,开放注册和授权注册。只有开放注册时,才可以客户端注册。
    • 开放注册是为了测试使用,正式环境中不推荐使用该方式注册环信账号,授权注册的流程应该是您服务器通过环信提供的 REST API 注册,之后保存到您的服务器或返回给客户端。
    • 注册提供了三种方法,在这里我们选择第三种(IChatManagerDelegate 回调方法).
    • 注册步骤: 调用注册接口 -> 添加代理 -> 遵守协议 -> 监听回调方法,ViewController.m中代码如下:
    //
    //  ViewController.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/4.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "ViewController.h"
    #import "EaseMob.h"
    
    @interface ViewController () <EMChatManagerDelegate>
    @property (weak, nonatomic) IBOutlet UITextField *usernameLabel;
    @property (weak, nonatomic) IBOutlet UITextField *pwdLabel;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 添加代理
        [[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];
    }
    
    // 登录
    - (IBAction)loginBtn:(id)sender {
        
    }
    
    // 注册(代理回调方法注册)
    - (IBAction)registerBtn:(id)sender {
        
        // 接口调用
        [[EaseMob sharedInstance].chatManager asyncRegisterNewAccount:self.usernameLabel.text password:self.pwdLabel.text];
    
    }
    
    // 监听回调方法
    - (void)didRegisterNewAccount:(NSString *)username password:(NSString *)password error:(EMError *)error
    {
        NSLog(@"%s", __func__);
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    
    • 运行程序,输入注册的账号和密码:
    • 我们再去环信后台看看是否注册成功


    登录

    • 登录:调用 SDK 的登录接口进行的操作。
    • 提供了三种方法,这里我们采用第二种方法(block 异步方法):
    // 登录
    - (IBAction)loginBtn:(id)sender {
        [[EaseMob sharedInstance].chatManager asyncLoginWithUsername:self.usernameLabel.text password:self.pwdLabel.text completion:^(NSDictionary *loginInfo, EMError *error) {
            if (!error && loginInfo) {
                NSLog(@"登录成功");
            }
        } onQueue:nil];
    }
    
    • 登录过后而已看到登录成功,但是这里也打印了一些xmpp控制台的输出信息,我们可以把它屏蔽掉
    • 屏蔽方法:来到AppDelegate.m:我们初始化SDK的时候有个参数是otherConfig,点击该方法可以看到右边的解释:
    • 把该参数改为@NO,就可以屏蔽啦~
    清除控制台输出 登录成功

    退出登录

    退出登录分两种类型:主动退出登录和被动退出登录。

    主动退出登录:调用SDK的退出接口;
    被动退出登录:1. 正在登录的账号在另一台设备上登录;2. 正在登录的账号被从服务器端删除。
    
    • 退出登录提供了三种方法,这里用第二种(block 异步方法)

    • logoffWithUnbindDeviceToken:是否解除 device token 的绑定,在被动退出时传 NO,在主动退出时传 YES。

    #pragma mark - 退出登录
    - (IBAction)loginOut:(id)sender {
        
        /*
         asyncLogoffWithUnbindDeviceToken:
         主动退出的时候,传YES
         被动退出的时候,传NO
         
         被动退出:
         其他设备登录
         被服务器移除
         // 退出,传入YES,会解除device token绑定,不再收到群消息;传NO,不解除device token
        */
        [[EaseMob sharedInstance].chatManager asyncLogoffWithUnbindDeviceToken:YES completion:^(NSDictionary *info, EMError *error) {
            if (!error) {
                NSLog(@"退出成功");
            }
        } onQueue:nil];
    }
    // 从其他设备登录
    - (void)didLoginFromOtherDevice
    {
        [[EaseMob sharedInstance].chatManager asyncLogoffWithUnbindDeviceToken:NO completion:^(NSDictionary *info, EMError *error) {
            if (!error) {
                NSLog(@"从其他设备登录");
            }
        } onQueue:nil];
    }
    
    // 被服务器移除
    - (void)didRemovedFromServer
    {
        [[EaseMob sharedInstance].chatManager asyncLogoffWithUnbindDeviceToken:NO completion:^(NSDictionary *info, EMError *error) {
            if (!error) {
                NSLog(@"被服务器移除");
            }
        } onQueue:nil];
    }
    

    自动重连

    • 当掉线时,iOS SDK 会自动重连,只需要监听重连相关的回调,无需进行任何操作。
    #pragma mark - 自动重连
    /*!
     @method
     @brief 将要发起自动重连操作时发送该回调
     @discussion
     @result
     */
    - (void)willAutoReconnect
    {
        NSLog(@"将要自动重连");
    }
    
    /*!
     @method
     @brief 自动重连操作完成后的回调(成功的话,error为nil,失败的话,查看error的错误信息)
     @discussion
     @result
     */
    - (void)didAutoReconnectFinishedWithError:(NSError *)error
    {
        if (!error) {
            NSLog(@"自动重连成功");
        }
    }
    
    • 在这里呢,我连接成功后就拔掉电脑网线模拟掉线的情况,然后再插上网线,可以看到控制台打印:

    自动登录

    • 自动登录:即首次登录成功后,不需要再次调用登录方法,在下次 APP 启动时,SDK 会自动为您登录。并且如果您自动登录失败,也可以读取到之前的会话信息。
    • SDK 中自动登录属性默认是关闭的,需要您在登录成功后设置,以便您在下次 APP 启动时不需要再次调用环信登录,并且能在没有网的情况下得到会话列表。
    • 在登录成功后设置自动登录
    // 登录
    - (IBAction)loginBtn:(id)sender {
        [[EaseMob sharedInstance].chatManager asyncLoginWithUsername:self.usernameLabel.text password:self.pwdLabel.text completion:^(NSDictionary *loginInfo, EMError *error) {
            if (!error && loginInfo) {
                NSLog(@"登录成功");
                // 设置自动登录
                [[EaseMob sharedInstance].chatManager setIsAutoLoginEnabled:YES];
            }
        } onQueue:nil];
    }
    
    • 自动登录在以下几种情况下会被取消:

      用户调用了 SDK 的登出动作;
      用户在别的设备上更改了密码,导致此设备上自动登录失败;
      用户的账号被从服务器端删除;
      用户从另一个设备登录,把当前设备上登录的用户踢出。

    • 所以,在您调用登录方法前,应该先判断是否设置了自动登录,如果设置了,则不需要您再调用。

    • 我们在AppDelegate.m- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中判断:

    BOOL isAutoLogin = [[EaseMob sharedInstance].chatManager isAutoLoginEnabled];
        if (isAutoLogin) {
            NSLog(@"切换根控制器");
        }
    
    • SDK中,如果发生自动登录,会有以下回调:
    #pragma mark - 自动登录
    - (void)willAutoLoginWithInfo:(NSDictionary *)loginInfo error:(EMError *)error
    {
        NSLog(@"将要自动登录");
    }
    
    - (void)didAutoLoginWithInfo:(NSDictionary *)loginInfo error:(EMError *)error
    {
        NSLog(@"已经自动登录");
    }
    

    接下来我们就要进行微信框架的搭建了

    • 首先导入素材,设置AppIcon和LaunchImage, 这两步比较简单,我就不细讲了,如果有不清楚的朋友可以留言.如果大家有需要,后面我会讲一下怎样获取到微信里面的图片素材,之前有很多网友说Assets.car里面的素材拿不到,别担心,总是有方法的,后面再一一讲解,我们现在主要把精力放在即时通讯上面,素材我也会贴上来的~
    • 微信架构搭建,这里我采用纯代码,storyboard适合页面多的时候,像这种复杂的界面建议用纯代码加xib.

    • 进入文件夹,重构一下文件,大家个人重构习惯自由发挥


    • 把文件拖入到工程中,注意我们之前在build setting中设置了Other Linker Flags,在我们重构文件夹的时候改变了路径,所以运行会报错:找不到libEaseMobClientSDK.a文件,所以我们需要再来到Other Linker Flags重新指定一下路径:

    • 创建文件:
    (tabBar)LHLTabBarController->UITabBarController
    (导航条)LHLNavViewController->UINavigationController
    (微信模块)LHLChatViewController->UITableViewController
    (联系人模块)LHLContactViewController->UITableViewController
    (发现模块)LHLDiscoverViewController->UITableViewController
    (我模块)LHLMeViewController->UITableViewController
    (登录)LHLLoginViewController->UIViewController
    
    • 之前测试的功能我们全部重新写一遍(比如登录注册...):
      AppDelegate.m中:
    //
    //  AppDelegate.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/4.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "AppDelegate.h"
    
    #define kEaseMobAppKey @"lengleng#lengleng"
    #import "LHLTabBarController.h"
    #import "LHLLoginViewController.h"
    
    @interface AppDelegate () <EMChatManagerDelegate>
    
    @end
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        
        /**
         *  @prama registerSDKWithAppKey 环信官网的appKey
         *  @prama apnsCertName 苹果官网注册的推送证书,楼主没有申请99刀,嘿嘿,这里就不用啦
         *  @prama otherConfig
         */
        // 1.注册APPKey
        [[EaseMob sharedInstance] registerSDKWithAppKey:kEaseMobAppKey
                                  apnsCertName:nil
                                            otherConfig:@{kSDKConfigEnableConsoleLogger : @NO}];
        // 2.跟踪app生命周期
        [[EaseMob sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
        
        // 3.添加监听代理
        [[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];
        
        // 4.判断是否是自动登录
        BOOL isAutoLogin = [[EaseMob sharedInstance].chatManager isAutoLoginEnabled];
        if (isAutoLogin) {
            NSLog(@"已经设置自动登录,切换根控制器");
            // 1.显示正在自动登录
            [SVProgressHUD showWithStatus:@"正在自动登录中..."];
            // 2.在 didAutoLoginWithInfo 方法中切换至主页面
        }
        
        return YES;
    }
    
    - (void)applicationWillResignActive:(UIApplication *)application {
    }
    
    - (void)applicationDidEnterBackground:(UIApplication *)application {
        // 进入后台
        [[EaseMob sharedInstance] applicationDidEnterBackground:application];
    }
    
    - (void)applicationWillEnterForeground:(UIApplication *)application {
        // 从后台进入前台
        [[EaseMob sharedInstance] applicationWillEnterForeground:application];
    }
    
    - (void)applicationDidBecomeActive:(UIApplication *)application {
    }
    
    - (void)applicationWillTerminate:(UIApplication *)application {
        // 销毁
        [[EaseMob sharedInstance] applicationWillTerminate:application];
        // 移除代理
        [[EaseMob sharedInstance].chatManager removeDelegate:self];
    }
    
    #pragma mark - 自动登录
    - (void)didAutoLoginWithInfo:(NSDictionary *)loginInfo error:(EMError *)error
    {
        [SVProgressHUD dismiss];
        if (error) { // 显示错误信息,不登录
            [JDStatusBarNotification showWithStatus:error.description dismissAfter:2.0];
        }else // 切换窗口根控制器
        {
            LHLTabBarController *tabBarVc = [[LHLTabBarController alloc] init];
            self.window.rootViewController = tabBarVc;
            [self.window makeKeyAndVisible];
        }
    }
    
    #pragma mark - 监听被动退出
    - (void)didRemovedFromServer
    {
        NSLog(@"账号被服务器删除");
        [self lhl_LogOffPassively];
    }
    
    - (void)didLoginFromOtherDevice
    {
        NSLog(@"从其他设备登录");
        [self lhl_LogOffPassively];
    }
    #pragma mark - 被动LogOff
    - (void)lhl_LogOffPassively
    {
        [[EaseMob sharedInstance].chatManager asyncLogoffWithUnbindDeviceToken:NO completion:^(NSDictionary *info, EMError *error) {
            
            // 被动退出后回调, 切换根控制器
            LHLLoginViewController *loginVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"LHLLoginViewController"];
            self.window.rootViewController = loginVC;
            
        } onQueue:nil];
    }
    @end
    

    LHLTabBarController.m 中:

    //
    //  LHLTabBarController.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/6.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "LHLTabBarController.h"
    #import "LHLChatViewController.h"
    #import "LHLContactViewController.h"
    #import "LHLDiscoverViewController.h"
    #import "LHLMeViewController.h"
    #import "LHLNavViewController.h"
    
    @interface LHLTabBarController ()
    
    @end
    
    @implementation LHLTabBarController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 创建所有子控制器
        [self setUpChildViewControllers];
        
        // 设置tabBar按钮和标题
        [self setUpAllTitles];
    }
    
    - (void)setUpChildViewControllers
    {
        // 微信
        LHLChatViewController *chatVC = [[LHLChatViewController alloc] init];
        LHLNavViewController *nav = [[LHLNavViewController alloc] initWithRootViewController:chatVC];
        [self addChildViewController:nav];
        
        // 通讯录
        LHLContactViewController *contactVc = [[LHLContactViewController alloc] init];
        LHLNavViewController *nav1 = [[LHLNavViewController alloc] initWithRootViewController:contactVc];
        [self addChildViewController:nav1];
        
        // 发现
        LHLDiscoverViewController *discoverVC = [[LHLDiscoverViewController alloc] init];
        LHLNavViewController *nav2 = [[LHLNavViewController alloc] initWithRootViewController:discoverVC];
        [self addChildViewController:nav2];
        
        // 我
        LHLMeViewController *meVC = [[UIStoryboard storyboardWithName:@"LHLMeViewController" bundle:nil] instantiateViewControllerWithIdentifier:@"LHLMeViewController"];
        LHLNavViewController *nav3 = [[LHLNavViewController alloc] initWithRootViewController:meVC];
        [self addChildViewController:nav3];
    }
    
    - (void)setUpAllTitles
    {
        // 设置按钮的标题和图片
        LHLNavViewController *nav = self.childViewControllers[0];
        [nav setTabBarItemImage:@"tabbar_mainframe" selectImage:@"tabbar_mainframeHL" title:@"微信"];
        
        LHLNavViewController *nav1 = self.childViewControllers[1];
        [nav1 setTabBarItemImage:@"tabbar_contacts" selectImage:@"tabbar_contactsHL" title:@"通讯录"];
        
        LHLNavViewController *nav2 = self.childViewControllers[2];
        [nav2 setTabBarItemImage:@"tabbar_discover" selectImage:@"tabbar_discoverHL" title:@"发现"];
        
        LHLNavViewController *nav3 = self.childViewControllers[3];
        [nav3 setTabBarItemImage:@"tabbar_me" selectImage:@"tabbar_meHL" title:@"我"];
    }
    
    
    @end
    
    

    LHLNavViewController.m 中:

    //
    //  LHLNavViewController.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/6.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "LHLNavViewController.h"
    
    @interface LHLNavViewController ()
    
    @end
    
    @implementation LHLNavViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 设置导航栏背景颜色
        [self.navigationBar lhl_setBackgroundColor:[UIColor blackColor]];
        // 修改左右UIBarButtonItem主题色
        self.navigationBar.tintColor = [UIColor whiteColor];
        // 修改标题颜色
        [self.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]}];
        
    }
    
    // 设置状态栏颜色
    - (UIStatusBarStyle)preferredStatusBarStyle
    {
        return UIStatusBarStyleLightContent;
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    - (void)setTabBarItemImage:(NSString *)image selectImage:(NSString *)selectImage title:(NSString *)title
    {
        self.tabBarItem.image = [UIImage imageOriginalWithName:image];
        self.tabBarItem.selectedImage = [UIImage imageOriginalWithName:selectImage];
        self.title = title;
        [self.tabBarItem setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor colorWithRed:9 green:187 blue:7]} forState:UIControlStateSelected];
    }
    
    @end
    
    
    • 到这里,基本架构已经搭建完毕我们现在做登录模块,这里直接用了系统的storyboard搭建登录界面

    LHLLoginViewController.m 中:
    //
    //  ViewController.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/4.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "LHLLoginViewController.h"
    #import "LHLTabBarController.h"
    
    @interface LHLLoginViewController ()
    @property (weak, nonatomic) IBOutlet UITextField *usernameLabel;
    @property (weak, nonatomic) IBOutlet UITextField *pwdLabel;
    
    @end
    
    @implementation LHLLoginViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 获取到保存的用户名
        NSString *lastUser = [[NSUserDefaults standardUserDefaults] valueForKeyPath:@"username"];
        if (lastUser) {
            self.usernameLabel.text = lastUser;
        }
    }
    
    // 登录
    - (IBAction)loginBtn:(id)sender {
        
        [SVProgressHUD showWithStatus:@"登录中..."];
        [[EaseMob sharedInstance].chatManager asyncLoginWithUsername:self.usernameLabel.text password:self.pwdLabel.text completion:^(NSDictionary *loginInfo, EMError *error) {
            
            [SVProgressHUD dismiss];
            if (!error) {
                NSLog(@"登录成功");
                // 1.设置自动登录
                [[EaseMob sharedInstance].chatManager setIsAutoLoginEnabled:YES];
                [JDStatusBarNotification showWithStatus:@"登录成功!" dismissAfter:2.0 styleName:JDStatusBarStyleSuccess];
                // 2.切换至主页面
                LHLTabBarController *tabBarVc = [[LHLTabBarController alloc] init];
                [UIApplication sharedApplication].keyWindow.rootViewController = tabBarVc;
            }else
            {
                NSLog(@"error: %@", error);
                [JDStatusBarNotification showWithStatus:[NSString stringWithFormat:@"登录失败!"] dismissAfter:2.0 styleName:JDStatusBarStyleError];
            }
        } onQueue:nil];
    }
    
    // 注册(代理回调方法注册)
    - (IBAction)registerBtn:(id)sender {
        
        [SVProgressHUD showWithStatus:@"注册中..."];
        // 接口调用
        [[EaseMob sharedInstance].chatManager asyncRegisterNewAccount:self.usernameLabel.text password:self.pwdLabel.text withCompletion:^(NSString *username, NSString *password, EMError *error) {
            [SVProgressHUD dismiss];
            if (!error) {
                NSLog(@"注册成功 : username = %@ password = %@", error, password);
                [JDStatusBarNotification showWithStatus:@"注册成功,请登录!" dismissAfter:2.0 styleName:JDStatusBarStyleSuccess];
            }else
            {
                NSLog(@"error : %@", error);
                [JDStatusBarNotification showWithStatus:[NSString stringWithFormat:@"注册失败!"] dismissAfter:2.0 styleName:JDStatusBarStyleError];
            }
        } onQueue:nil];
    
    }
    
    @end
    
    • 微信模块 LHLChatViewController.m 中:
    //
    //  LHLChatViewController.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/6.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "LHLChatViewController.h"
    
    /**
     在微信中,它的微信界面的标题切换的是 titleView
     除了下面4种状态,还有
     听筒模式
     未读消息数量展示
     等等
     
     此处我们通过简单的模仿,来了解 连接状态改变,以及消息接收带来的对标题view的影响
     */
    NSString * const LHLWeChatTitleNormal = @"微信";
    NSString * const LHLWeChatTitleWillConnect = @"连接中...";
    NSString * const LHLWeChatTitleDisconnect = @"微信(未连接)";
    NSString * const LHLWeChatTitleWillReceiveMsg = @"收取中...";
    
    @interface LHLChatViewController () <EMChatManagerDelegate>
    
    @end
    
    @implementation LHLChatViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.title = LHLWeChatTitleNormal;
        
        [[EaseMob sharedInstance].chatManager addDelegate:self delegateQueue:nil];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    #pragma mark - Table view data source
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    #warning Incomplete implementation, return the number of sections
        return 0;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    #warning Incomplete implementation, return the number of rows
        return 0;
    }
    
    /*
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<#@"reuseIdentifier"#> forIndexPath:indexPath];
        
        // Configure the cell...
        
        return cell;
    }
    */
    
    
    
    #pragma mark - 自动重连
    /*!
     @method
     @brief 将要发起自动重连操作时发送该回调
     @discussion
     @result
     */
    - (void)willAutoReconnect
    {
        NSLog(@"将要自动重连");
        self.title = LHLWeChatTitleWillConnect;
    }
    
    /*!
     @method
     @brief 自动重连操作完成后的回调(成功的话,error为nil,失败的话,查看error的错误信息)
     @discussion
     @result
     */
    - (void)didAutoReconnectFinishedWithError:(NSError *)error
    {
        if (!error) {
            NSLog(@"自动重连成功");
            self.title = LHLWeChatTitleNormal;
        }else
        {
            NSLog(@"自动重连失败");
            self.title = LHLWeChatTitleDisconnect;
        }
    }
    
    #pragma mark - 连接状态改变
    - (void)didConnectionStateChanged:(EMConnectionState)connectionState
    {
        switch (connectionState) {
            case eEMConnectionConnected: // 连接成功
            {
                self.title = LHLWeChatTitleNormal;
            }
                break;
            case eEMConnectionDisconnected: // 未连接
            {
                self.title = LHLWeChatTitleDisconnect;
            }
                break;
                
            default:
                break;
        }
    }
    
    
    #pragma mark - 移除代理
    - (void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        [[EaseMob sharedInstance].chatManager removeDelegate:self];
    }
    
    
    @end
    
    
    • 通讯录模块 LHLContactViewController.m 中:
    //
    //  LHLContactViewController.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/6.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "LHLContactViewController.h"
    
    @interface LHLContactViewController ()
    
    /** 本地的好友列表 */
    @property (nonatomic, strong) NSMutableArray *friends;
    /** 服务器获取的好友列表 */
    @property (nonatomic, strong) NSArray *buddies;
    
    @end
    
    @implementation LHLContactViewController
    
    static NSString *cellID = @"UITableViewCell";
    - (NSMutableArray *)friends
    {
        // 好友列表(由EMBuddy对象组成)
        if (_friends == nil) {
            
            _friends = [NSMutableArray array];
            _buddies = [[EaseMob sharedInstance].chatManager buddyList];
            if (_buddies.count) {
                [_friends addObjectsFromArray:_buddies];
            }
        }
        return _friends;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.navigationItem.title = @"通讯录";
        self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"contacts_add_friend"] style:UIBarButtonItemStylePlain target:self action:@selector(addFriend)];
        [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellID];
    }
    
    - (void)addFriend
    {
        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"添加好友" message:nil preferredStyle:UIAlertControllerStyleAlert];
        [alertVC addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            textField.placeholder = @"请输入账号";
        }];
        [alertVC addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
            textField.placeholder = @"请输入理由";
        }];
        [alertVC addAction:[UIAlertAction actionWithTitle:@"发送" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            
            EMError *error = nil;
            BOOL isSuccess = [[EaseMob sharedInstance].chatManager addBuddy:alertVC.textFields.firstObject.text message:alertVC.textFields.lastObject.text error:&error];
            if (!error) {
                NSLog(@"发送好友请求成功 - %d", isSuccess);
            }
        }]];
        [alertVC addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            // 取消添加
        }]];
        [self presentViewController:alertVC animated:YES completion:^{
            
        }];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    #pragma mark - Table view data source
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
        return self.friends.count;
    }
    
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath];
        
        EMBuddy *buddy = self.friends[indexPath.row];
        cell.textLabel.text = buddy.username;
        
        return cell;
    }
    
    @end
    
    
    • 我的模块也是结合了storyboard,利用静态cell搭建一些固定不变的界面真的很便捷
    • LHLMeViewController.m 中:
    //
    //  LHLMeViewController.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/6.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "LHLMeViewController.h"
    #import "LHLSettingViewController.h"
    
    @interface LHLMeViewController ()
    
    @property (weak, nonatomic) IBOutlet UILabel *username;
    
    @end
    
    @implementation LHLMeViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.navigationItem.title = @"我";
        self.username.text = [[EaseMob sharedInstance].chatManager loginInfo][@"username"];
        
    }
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.section == 3) {
            LHLSettingViewController *settingVC = [[UIStoryboard storyboardWithName:@"LHLSettingViewController" bundle:nil] instantiateViewControllerWithIdentifier:@"LHLSettingViewController"];
            self.hidesBottomBarWhenPushed = YES;
            [self.navigationController pushViewController:settingVC animated:YES];
            self.hidesBottomBarWhenPushed = NO;
        }
    }
    
    @end
    
    • ```LHLSettingViewController.m`` 中
    //
    //  LHLSettingViewController.m
    //  EMWeiChat
    //
    //  Created by admin on 16/11/6.
    //  Copyright © 2016年 冷洪林. All rights reserved.
    //
    
    #import "LHLSettingViewController.h"
    #import "LHLLoginViewController.h"
    
    @interface LHLSettingViewController ()
    
    @end
    
    @implementation LHLSettingViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
    }
    
    // 退出登录
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.section == 3) {
            
            [[EaseMob sharedInstance].chatManager asyncLogoffWithUnbindDeviceToken:YES completion:^(NSDictionary *info, EMError *error) {
                if (!error) {
                    NSLog(@"退出登录");
                    // 1.记录退出的用户名(为了用户再次登录的时候不用重新输入用户名.optional)
                    [[NSUserDefaults standardUserDefaults] setObject: [[EaseMob sharedInstance].chatManager loginInfo][@"username"] forKey:@"username"];
                    
                    // 2.切换窗口根控制器
                    LHLLoginViewController *loginVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"LHLLoginViewController"];
                    [UIApplication sharedApplication].keyWindow.rootViewController = loginVC;
                }else
                {
                    NSLog(@"error : %@", error);
                }
            } onQueue:nil];
            
        }
    }
    
    
    
    @end
    
    • 有网友提到官方SDK集成的问题,我之前以为大家跟着官方文档一步步来就会没问题的,所以文章开头就直接放了官方文档的连接,鉴于网友们的问题,我接下来就从零开始集成环信SDK 2.x 和 3.x,希望能帮到遇到困难的网友~

    相关文章

      网友评论

      • Zszen:环信的full.a文件很大但是发布后又不占体积,为什么
      • 非特异Code:APP,海量商业实例,助您快速上线/集成/实践,来自[b][color=Red]www.7coding.top[/color][/b]
      • Vampire丶黑狐:详细
        冷洪林:@Vampire丶黑狐 3Q~
      • Aprilx:现在环信官网上还能下载下来EaseMobSDK? 它好像已经变了~成HyphenateSDK了,前面的导入部分是老版的吧??? :fearful:
        Aprilx:@冷洪林 :sob:我导了一下午了,硬是没导进去。作为菜鸟,我表示环信的官方文档像狗屎,我脑子快炸了。大神,你再写篇新的导入嘛!好跟你这篇配套嘛:sob::sob::sob:
        冷洪林:@Aprilx 嗯嗯,这里是用的2.x的SDK
      • 大Z哥:好想知道现在有多少人用swift
        冷洪林:@zerver 我也只会一点点,但是肯定是要学的
      • YxxxHao:你用极光im,直接给你一个完整的可使用的demo
        YxxxHao:@冷洪林 环信也是闭源的:relieved::relieved:你应该去学SDK开发,:smirk::smirk::smirk:比如看我写的SDK文章
        冷洪林:@DengYonghao 谢谢了哈,我主要是想学学环信的SDK
        冷洪林:@DengYonghao

      本文标题:基于环信SDK的仿微信聊天app【每日更新】

      本文链接:https://www.haomeiwen.com/subject/xbfwuttx.html