最近公司项目要求更新了下环信,从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;
}
}
}
数据处理大家自己看看应该就能搞懂不是很复杂,差不多就先搞这些,如果还有问题的同学可以私信我一起交流下。
网友评论