美文网首页iOSiOSiOS开发常用知识点
环信iOS端3.0版本集成记录--聊天界面篇

环信iOS端3.0版本集成记录--聊天界面篇

作者: LonelyBanana | 来源:发表于2016-11-16 16:47 被阅读4915次
孤独的香蕉.png

最近公司项目要求更新了下环信,从2.0更新到了3.0,遇到了一些坑,所以记录下备忘,并且大家一起学习下,这篇会重点在聊天界面上(因为公司这次版本更新主要改动就在这里)


首先说下环信版本,我项目中目前集成的是环信的3.2.0版本,也就是10月>15日更新的版本

环信中的一些依赖库我就不说了,官方文档内都是有的@!@


775217E1-2B9C-43EB-8DA7-0CE085B6B041.png E7C113E4-0863-427B-B701-43508BE87AA7.png

如果第一次集成环信的兄弟有可能会很头疼,拖进项目一个文件会有大量的报错,所以我建议先建个空项目,在把环信里面你需要的界面拖进去进行调试,然后跑通在往你的项目中拖。

这次主要介绍聊天的界面

897F4BF0-0B6A-4CDC-8F6D-40E3ADB57125.png

这个VC就是气泡聊天的页面啦

ChatViewController.h

/************************************************************
 *  * Hyphenate CONFIDENTIAL
 * __________________
 * Copyright (C) 2016 Hyphenate Inc. All rights reserved.
 *
 * NOTICE: All information contained herein is, and remains
 * the property of Hyphenate Inc.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Hyphenate Inc.
 */

#define KNOTIFICATIONNAME_DELETEALLMESSAGE @"RemoveAllMessages"

@interface ChatViewController : EaseMessageViewController <EaseMessageViewControllerDelegate, EaseMessageViewControllerDataSource>

- (void)showMenuViewController:(UIView *)showInView
                  andIndexPath:(NSIndexPath *)indexPath
                   messageType:(EMMessageBodyType)messageType;

@end

养成好习惯先看看父类,不要一上来就在这个.m里面看半天找方法,效率极低。
注意下EaseMessageViewController这个类的父类并不是UIViewController它还有父类我们点进去看一眼EaseRefreshTableViewController这就是基类了,里面其实就是一些下拉上拉和数据源容器之类的东西。所以还是主要看EaseMessageViewController


一、EaseMessageViewController
其实仔细看下EaseMessageViewController.h你会发现如果你想高度自定义的话你直接继承这个类就行了,环信已经把所有的回调都帮你搞定了。这应该也是环信的本意。

初始化

211652DF-5650-4B7C-9C6E-6A82BB2EE7D4.png

看一下.m中的实现方法,在70行,.m一共有1993行,不知道环信维护起来酸爽不

- (instancetype)initWithConversationChatter:(NSString *)conversationChatter
                           conversationType:(EMConversationType)conversationType
{
    if ([conversationChatter length] == 0) {
        return nil;
    }
    
    self = [super initWithStyle:UITableViewStylePlain];
    if (self) {
        //这里是环信SDK中的方法,如果以前使用的是2.0的同学会发现EMClient这个玩意你没见过,
//那就对了环信升级,以前用的用不了了。
//conversationType这个东西是个枚举 主要就是看你聊天的方式,单聊,群聊,和聊天室。
        _conversation = [[EMClient sharedClient].chatManager getConversation:conversationChatter type:conversationType createIfNotExist:YES];
        
        _messageCountOfPage = 10;//一页有多少条数据
        _timeCellHeight = 30;//显示间隔时间的高度
        _deleteConversationIfNull = YES;//当前聊天为空是否删除
        _scrollToBottomWhenAppear = YES;//进入页面是否滑到最下面
        _messsagesSource = [NSMutableArray array];//消息数据源
        
        [_conversation markAllMessagesAsRead:nil];//标记当前会话消息为已读
    }
    
    return self;
}

输入文字的区域

B73F9735-D0D1-42A5-A937-580A098D6B03.png
C0F6AD1F-C02A-46E3-A9C2-E1FEB409D5A3.png B699398C-D97A-4BA5-B916-AF7B4191DFDB.png

找到以后你们懂得,随便祸祸吧,任你玩弄。


聊天显示区域

首先找到TableView的代理方法 899行 主要看Cell的方法

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  //这里是要拿到数据源,这个数据源处理大家可以自己找一下,dataArray里面 存着是两种类型的数据,一种是NSString这种事时间,还有一种就是咱们的聊天数据IMessageModel这种类型的数据模型,所以接收数据是要用id类型。
    id object = [self.dataArray objectAtIndex:indexPath.row];
    
    //在这里判断如果类型为NSString类型就证明cell的类型是要选择时间类型的cell如果不是就是现实气泡。
    if ([object isKindOfClass:[NSString class]]) {
        NSString *TimeCellIdentifier = [EaseMessageTimeCell cellIdentifier];
        EaseMessageTimeCell *timeCell = (EaseMessageTimeCell *)[tableView dequeueReusableCellWithIdentifier:TimeCellIdentifier];
        
        if (timeCell == nil) {
            timeCell = [[EaseMessageTimeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TimeCellIdentifier];
            timeCell.selectionStyle = UITableViewCellSelectionStyleNone;
        }
        
        timeCell.title = object;
        return timeCell;
    }
    else{
//这里是实现的气泡实现方法应该是在ChatViewController里面实现,环信定义了一个代理方法,就是他下面判断的几个,根据你的情况去在你的界面里面去实现就可以。
        id<IMessageModel> model = object;
        if (_delegate && [_delegate respondsToSelector:@selector(messageViewController:cellForMessageModel:)]) {
            UITableViewCell *cell = [_delegate messageViewController:tableView cellForMessageModel:model];
            if (cell) {
                if ([cell isKindOfClass:[EaseMessageCell class]]) {
                    EaseMessageCell *emcell= (EaseMessageCell*)cell;
                    if (emcell.delegate == nil) {
                        emcell.delegate = self;
                    }
                }
                return cell;
            }
        }
        
        if (_dataSource && [_dataSource respondsToSelector:@selector(isEmotionMessageFormessageViewController:messageModel:)]) {
            BOOL flag = [_dataSource isEmotionMessageFormessageViewController:self messageModel:model];
            if (flag) {
                NSString *CellIdentifier = [EaseCustomMessageCell cellIdentifierWithModel:model];
                //send cell
                EaseCustomMessageCell *sendCell = (EaseCustomMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
                
                // Configure the cell...
                if (sendCell == nil) {
                    sendCell = [[EaseCustomMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier model:model];
                    sendCell.selectionStyle = UITableViewCellSelectionStyleNone;
                }
                
                if (_dataSource && [_dataSource respondsToSelector:@selector(emotionURLFormessageViewController:messageModel:)]) {
                    EaseEmotion *emotion = [_dataSource emotionURLFormessageViewController:self messageModel:model];
                    if (emotion) {
                        model.image = [UIImage sd_animatedGIFNamed:emotion.emotionOriginal];
                        model.fileURLPath = emotion.emotionOriginalURL;
                    }
                }
                sendCell.model = model;
                sendCell.delegate = self;
                return sendCell;
            }
        }
        
        NSString *CellIdentifier = [EaseMessageCell cellIdentifierWithModel:model];
        
        EaseBaseMessageCell *sendCell = (EaseBaseMessageCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        
        // Configure the cell...
        if (sendCell == nil) {
            sendCell = [[EaseBaseMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier model:model];
            sendCell.selectionStyle = UITableViewCellSelectionStyleNone;
            sendCell.delegate = self;
        }
        
        sendCell.model = model;
        return sendCell;
    }
}

可以看到上面就是环信处理Cell的方法,如果想自定义直接遵守他的协议,然后在他的代理方法里面实现你想要的cell就可以了,然后咱们在来看看环信的cell是如何实现的看一下环信最基础的cell EaseMessageCell这个类应该是环信使用气泡cell的基类,咱们先看下它的初始化方法+ (void)initialize里面会有些设置,这个没啥说的,看看就明白啥意思,主要看的是108行

//这个方法是cell初始化的时候调用的会初始化UI
- (void)_setupSubviewsWithType:(EMMessageBodyType)messageType
                      isSender:(BOOL)isSender
                         model:(id<IMessageModel>)model
{
    _statusButton = [[UIButton alloc] init];
    _statusButton.translatesAutoresizingMaskIntoConstraints = NO;
    _statusButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
    [_statusButton setImage:[UIImage imageNamed:@"EaseUIResource.bundle/messageSendFail"] forState:UIControlStateNormal];
    [_statusButton addTarget:self action:@selector(statusAction) forControlEvents:UIControlEventTouchUpInside];
    _statusButton.hidden = YES;
    [self.contentView addSubview:_statusButton];
    //这个就是cell上的气泡 isSender是判断是否是本人。
    _bubbleView = [[EaseBubbleView alloc] initWithMargin:isSender?_rightBubbleMargin:_leftBubbleMargin isSender:isSender];
    _bubbleView.translatesAutoresizingMaskIntoConstraints = NO;
    _bubbleView.backgroundColor = [UIColor clearColor];
    [self.contentView addSubview:_bubbleView];
    
    _avatarView = [[UIImageView alloc] init];
    _avatarView.translatesAutoresizingMaskIntoConstraints = NO;
    _avatarView.backgroundColor = [UIColor clearColor];
    _avatarView.clipsToBounds = YES;
    _avatarView.userInteractionEnabled = YES;
    [self.contentView addSubview:_avatarView];
    
    _hasRead = [[UILabel alloc] init];
    _hasRead.translatesAutoresizingMaskIntoConstraints = NO;
    _hasRead.text = NSEaseLocalizedString(@"hasRead", @"Read");
    _hasRead.textAlignment = NSTextAlignmentCenter;
    _hasRead.font = [UIFont systemFontOfSize:12];
    _hasRead.hidden = YES;
    [_hasRead sizeToFit];
    [self.contentView addSubview:_hasRead];
    
    _activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    _activity.translatesAutoresizingMaskIntoConstraints = NO;
    _activity.backgroundColor = [UIColor clearColor];
    _activity.hidden = YES;
    [self.contentView addSubview:_activity];
    
    if ([self respondsToSelector:@selector(isCustomBubbleView:)] && [self isCustomBubbleView:model]) {
        [self setCustomBubbleView:model];
    } else {
      //这里是判断消息类型的,什么消息显示什么类型都是在这里判断,EaseBubbleView环信建了很多Category来拓展EaseBubbleView的类型,位置我会在下面贴图出来。
        switch (messageType) {
            case EMMessageBodyTypeText:
            {
                [_bubbleView setupTextBubbleView];
                
                _bubbleView.textLabel.font = _messageTextFont;
                _bubbleView.textLabel.textColor = _messageTextColor;
            }
                break;
            case EMMessageBodyTypeImage:
            {
                [_bubbleView setupImageBubbleView];
                
                _bubbleView.imageView.image = [UIImage imageNamed:@"EaseUIResource.bundle/imageDownloadFail"];
            }
                break;
            case EMMessageBodyTypeLocation:
            {
                [_bubbleView setupLocationBubbleView];
                
                _bubbleView.locationImageView.image = [[UIImage imageNamed:@"EaseUIResource.bundle/chat_location_preview"] stretchableImageWithLeftCapWidth:10 topCapHeight:10];
                _bubbleView.locationLabel.font = _messageLocationFont;
                _bubbleView.locationLabel.textColor = _messageLocationColor;
            }
                break;
            case EMMessageBodyTypeVoice:
            {
                [_bubbleView setupVoiceBubbleView];
                
                _bubbleView.voiceDurationLabel.textColor = _messageVoiceDurationColor;
                _bubbleView.voiceDurationLabel.font = _messageVoiceDurationFont;
            }
                break;
            case EMMessageBodyTypeVideo:
            {
                [_bubbleView setupVideoBubbleView];
                
                _bubbleView.videoTagView.image = [UIImage imageNamed:@"EaseUIResource.bundle/messageVideo"];
            }
                break;
            case EMMessageBodyTypeFile:
            {
                [_bubbleView setupFileBubbleView];
                
                _bubbleView.fileNameLabel.font = _messageFileNameFont;
                _bubbleView.fileNameLabel.textColor = _messageFileNameColor;
                _bubbleView.fileSizeLabel.font = _messageFileSizeFont;
            }
                break;
            default:
                break;
        }
    }
    
    [self _setupConstraints];
    
    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(bubbleViewTapAction:)];
    [_bubbleView addGestureRecognizer:tapRecognizer];
    
    UITapGestureRecognizer *tapRecognizer2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(avatarViewTapAction:)];
    [_avatarView addGestureRecognizer:tapRecognizer2];
}
190542D3-5625-42DC-B41E-E38E620F655D.png
- (void)setModel:(id<IMessageModel>)model
{
    _model = model;
    if ([self respondsToSelector:@selector(isCustomBubbleView:)] && [self isCustomBubbleView:model]) {
        [self setCustomModel:model];
    } else {
      //这里应该都看的明白,判断下信息类型
        switch (model.bodyType) {
            case EMMessageBodyTypeText:
            {
                //文字信息的话环信自己处理了下,应该是他这个版本支持@功能,我没细看所以不说了,我的项目自己写的@功能和##功能,所以就没看他的。
                _bubbleView.textLabel.attributedText = [[EaseEmotionEscape sharedInstance] attStringFromTextForChatting:model.text textFont:self.messageTextFont];
            }
                break;
            case EMMessageBodyTypeImage:
            {
              //这里就是判断如果是图片做的处理,觉得丑大家自己改就行了
                UIImage *image = _model.thumbnailImage;
                if (!image) {
                    image = _model.image;
                    if (!image) {
                        [_bubbleView.imageView sd_setImageWithURL:[NSURL URLWithString:_model.fileURLPath] placeholderImage:[UIImage imageNamed:_model.failImageName]];
                    } else {
                        _bubbleView.imageView.image = image;
                    }
                } else {
                    _bubbleView.imageView.image = image;
                }
            }
                break;
            case EMMessageBodyTypeLocation:
            {
                _bubbleView.locationLabel.text = _model.address;
            }
                break;
            case EMMessageBodyTypeVoice:
            {
                if (_bubbleView.voiceImageView) {
                    if ([self.sendMessageVoiceAnimationImages count] > 0 && [self.recvMessageVoiceAnimationImages count] > 0) {
                        self.bubbleView.voiceImageView.image = self.model.isSender ?[self.sendMessageVoiceAnimationImages objectAtIndex:0] : [self.recvMessageVoiceAnimationImages objectAtIndex:0];
                        _bubbleView.voiceImageView.animationImages = self.model.isSender ? self.sendMessageVoiceAnimationImages:self.recvMessageVoiceAnimationImages;
                    } else {
                        self.bubbleView.voiceImageView.image = self.model.isSender ?[UIImage imageNamed:@"EaseUIResource.bundle/chat_sender_audio_playing_full"]: [UIImage imageNamed:@"EaseUIResource.bundle/chat_receiver_audio_playing_full"];
                    }
                }
                if (!self.model.isSender) {
                    if (self.model.isMediaPlayed){
                        _bubbleView.isReadView.hidden = YES;
                    } else {
                        _bubbleView.isReadView.hidden = NO;
                    }
                }
                
                if (_model.isMediaPlaying) {
                    [_bubbleView.voiceImageView startAnimating];
                }
                else{
                    [_bubbleView.voiceImageView stopAnimating];
                }
                
                _bubbleView.voiceDurationLabel.text = [NSString stringWithFormat:@"%d''",(int)_model.mediaDuration];
            }
                break;
            case EMMessageBodyTypeVideo:
            {
                UIImage *image = _model.isSender ? _model.image : _model.thumbnailImage;
                if (!image) {
                    image = _model.image;
                    if (!image) {
                        [_bubbleView.videoImageView sd_setImageWithURL:[NSURL URLWithString:_model.fileURLPath] placeholderImage:[UIImage imageNamed:_model.failImageName]];
                    } else {
                        _bubbleView.videoImageView.image = image;
                    }
                } else {
                    _bubbleView.videoImageView.image = image;
                }
            }
                break;
            case EMMessageBodyTypeFile:
            {
                _bubbleView.fileIconView.image = [UIImage imageNamed:_model.fileIconName];
                _bubbleView.fileNameLabel.text = _model.fileName;
                _bubbleView.fileSizeLabel.text = _model.fileSizeDes;
            }
                break;
            default:
                break;
        }
    }
}

数据处理大家自己看看应该就能搞懂不是很复杂,差不多就先搞这些,如果还有问题的同学可以私信我一起交流下。

相关文章

  • 环信iOS端3.0版本集成记录--聊天界面篇

    最近公司项目要求更新了下环信,从2.0更新到了3.0,遇到了一些坑,所以记录下备忘,并且大家一起学习下,这篇会重点...

  • iOS 环信集成

    1、流程类: 最新环信V3.3.7单聊集成与使用 iOS环信3.0集成 (三)单聊集成有视频 iOS 环信3.0D...

  • 环信集成EaseUI报Undefined symbols for

    集成环信SDK3.0没有问题(HyphenateFullSDK带语音版本,非cocoapods集成),但是只要一拖...

  • 环信

    iOS 集成环信(四) 群聊iOS 环信集成(五) 加载会话列表

  • IPV6适配之环信SDK

    项目中集成了环信,在IPV6 下无法登录环信的聊天服务器,无法聊天,因此需要适配环信 ,去年7月集成的版本应该是...

  • 环信聊天室demo

    开发一个直播项目需要用到环信聊天室,折腾后自己集成环信聊天不带语音版本,集成环信UI删减一些不要的东西1.需要在x...

  • Flutter和原生iOS交互

    具体集成请看下面文章此篇记录的是集成之后的iOS界面和flutter的交互,iOS界面怎么跳转到不同的flutte...

  • vue集成环信sdk实现聊天

    最近由app开发分配到了前端组,负责PC端的聊天功能,现在记录一下当时集成环信sdk遇到的坑 环境: npm集成 ...

  • iOS 之环信的使用

    一.环信简介 二.集成环信iOS SDK 三.初始化环信SDK 四.注册 五.登录 六.好友 七.消息 八.聊天 ...

  • 环信源码分析---自定义消息工具条和表情键盘以及集成环信遇到的坑

    今天看了下环信SDK聊天模块下封装的自定义消息工具条以及表情键盘,了解了下iOS自带表情的转码。顺带记录下集成环信...

网友评论

  • 錒伟:您好, 想请教一下环信群聊修改名称后 , 会话列表刷新后不是最新数据 , 也就是说聊天页面群名已经修改,但是会话列表的群名没有修改, 希望赐教
  • coder袁:楼主好人 精华 详细 ,mark
  • 心至靜行至遠:环信的UI和业务是分离的吗?可以只使用他们的UI组件吗?看腾讯云通信的demo有种想死的感觉!
  • F麦子:对方跟你聊天的同时修改头像后,再给你发消息,此时对方的头像上下不一样,这个咋处理
  • 安静守护你:请问,怎么在聊天界面里面从环信获取聊天记录并展示出来?
  • 下雨就好:请问,3.0的聊天记录一旦退出以后再进去就没有了,您那边是怎么处理的
    Despacito_bb7d:@下雨就好 你好,这个问题怎么解决的,我今天也碰到了
    下雨就好:@rangoo 本地有个方法你看下是不是写了,他们的聊天记录是存的本地的
    rangoo:请问 你这个问题解决了吗?
  • GOOGxu:我把more view里的Button全注掉掉了 还会有转账功能按钮 请问怎么去掉呢
  • 陌南尘Y:你这个是几点几版本的sdk
  • 忧伤玩偶:为什么我自定义一个控制器继承EaseMessageViewController,但是push过来之后没有导航栏是这么回事
    菜鸟晋升路:我也遇到这个问题了 你解决没?
  • lifeLL:环信里面,聊天页面要是发送GIF图片,你是怎么处理的?
    LonelyBanana:@公子辛 好嘞。不客气!我之前看环信 它发送表情那里用的好像是gif的!
    lifeLL:@LonelyBanana 多谢,在图库里面取GIF然后发送,这个环信没有直接支持,需要用文件的方式发送才可以,昨晚已搞定!thanks
    LonelyBanana:@公子辛 我们项目并没有做这个!不过第三方的库很多的!如果你要做表情的话!环信自己那一套应该有的 !直接套用
  • 好棒大大:demo 来一发吗
    LonelyBanana:@颜啊伟 就是环信的官方Demo
  • 好棒大大:真棒
    c46f9175a038:@LonelyBanana 我自定义的cell布局,想用它的菊花,怎么设置都没用
    LonelyBanana:@颜啊伟 谢谢:pray::stuck_out_tongue_winking_eye:
  • 黎希:如何做角标提醒呢?跪求思路
    LonelyBanana:@Palingenesis_阿豪 我是这么做的 我会创建一个会话管理单利!在进入app时初始化!并拿到环信的代理!当有消息进来时直接就可以操作角标等等!方法很多 看你理解了!
  • 斯文9:我们inputview和环信的不一样还有单聊和聊天室的inpuview也不一样是自定义的好还是修改环信的
    LonelyBanana:好明天加你好吗?今天有些晚啦
    斯文9:@LonelyBanana 能加我qq吗821714095
    LonelyBanana:最好自定义吧!

本文标题:环信iOS端3.0版本集成记录--聊天界面篇

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