Pod集成的EaseUI,我这里使用的是不带音视频的skd.(如果使用音视频版本请参考环信文档,但是后面具体操作基本一致)
打开cocoaPod(已安装的情况下,未安装的请自行百度安装) 然后cd到自己的项目工程下,
在pod中复制粘贴,(如果要指定版本,可以在更改后面的tag值,)
pod 'EaseUI', :git => 'https://github.com/easemob/easeui-ios-hyphenate-cocoapods.git', :tag => '3.3.6'
这里主要讲述单聊(自定义名片消息和正常的聊天消息)和聊天列表的集成实现.
一:单聊
自定义一个viewController 继承环信EaseUI中的EaseMessageViewController聊天界面控制器.我这边起名:CahtWithPVControllerViewController.我们主要实现单聊中的类似于淘宝和商家交谈发送某个商品的情况:正常消息和自定义消息的实现:如图
在我们继承好的自定义控制中,我们主要分清两种情况,自己发送和对方发送,自己接收和对方接收,这些我们都是可以根据代理实现的.所以我们要设置代理,然后实现代理.代码如下
继承代理
- (void)viewDidLoad {
[super viewDidLoad];
self.showRefreshHeader = YES;
self.delegate= self;
self.dataSource = self;
}
实现原本的自有的方法
//会话界面的shua xin
- (void)tableViewDidTriggerHeaderRefresh
{
NSString*startMessageId =nil;
if ([self.messsagesSource count] > 0) {
startMessageId = [(EMMessage*)self.messsagesSource.firstObjectmessageId];
}
NSLog(@"startMessageID ------- %@",startMessageId);
[EMClient.sharedClient.chatManager asyncFetchHistoryMessagesFromServer:self.conversation.conversationId
conversationType:self.conversation.type
startMessageId:startMessageId
pageSize:10
completion:^(EMCursorResult*aResult,EMError*aError)
{
[super tableViewDidTriggerHeaderRefresh];
}];
}
先写到这里关键的代码在梳理完流程后在去写.
我在自定的CahtWithPVControllerViewController中为继承过来的tableView添加了一个头视图(根据项目需求来做,视图加在哪里都可以)就是下面这一块,
我的代码
- (void)configureGoodView {
UIView *bgV = [[UIView alloc] initWithFrame:CGRectMake(0, 10, kScreenWidth, 150)];
bgV.backgroundColor = [UIColor whiteColor];
UIImageView *imageV = [[UIImageView alloc] init];
imageV.frame=CGRectMake(15,15,80,80);
[imageVsd_setImageWithURL:[NSURL URLWithString:_goodImage] placeholderImage:[UIImage imageNamed:@"80"]];
[bgVaddSubview:imageV];
UILabel *titleLB = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(imageV.frame)+15, 15, kScreenWidth - CGRectGetWidth(imageV.frame) -30, 45)];
titleLB.font=kFont16;
titleLB.textColor=blackZiti;
titleLB.numberOfLines=0;
titleLB.text=_goodName;
[bgVaddSubview:titleLB];
UILabel *priceLB = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(imageV.frame)+15, CGRectGetMaxY(imageV.frame)- 30, kScreenWidth - CGRectGetWidth(imageV.frame) -30, 30)];
priceLB.font=kFont16;
priceLB.textColor = [UIColor redColor];
priceLB.text = [NSString stringWithFormat:@"¥%@",_goodPrice];
priceLB.numberOfLines=0;
[bgVaddSubview:priceLB];
UIButton *btn = [UIButton buttonWithType:(UIButtonTypeCustom)];
btn.frame=CGRectMake((kScreenWidth-100)/2,CGRectGetMaxY(imageV.frame)+15,100,30);
[btnsetTitle:@"发送宝贝" forState:(UIControlStateNormal)];
[btnsetTitleColor:erqiZiTColor forState:(UIControlStateNormal)];
btn.layer.masksToBounds = YES;
btn.titleLabel.font = kFont15;
btn.layer.cornerRadius = 35/2;
btn.layer.borderColor = erqiZiTColor.CGColor;
btn.layer.borderWidth = 0.8;
[btnaddTarget:self action:@selector(sendRecommendFriend) forControlEvents:(UIControlEventTouchUpInside)];
[bgVaddSubview:btn];
// [self tableViewDidTriggerHeaderRefresh];
self.tableView.tableFooterView = bgV;
}
在我们点击发送宝贝的时候要把这个宝贝发送给商家:类似于下图:
实现点击事件:
- (void)sendRecommendFriend{
[self sendTextMessage:@"商品链接" withExt:@{@"nickname":@"张三自己的名字",@"headimgurl":@"http://iamgename自己的头像",@"goodName":@"商品名字",@"goodPrice":@"商品价格",@"goodImage":@"商品图像",@"goodId":@"商品id"}];
//发送完成后把之前设置的tableFooterView置位空(根据需求来制定)
self.tableView.tableFooterView = nil;
}
这里我们用到EaseUI中的sendTextMessage这个方法,
- (void)sendTextMessage:(NSString*)text withExt:(NSDictionary*)ext;
第一个text是文本消息,这里我们用来作一个和其他消息区分的唯一标识,ext可以理解为消息体,是一个字典,用来存储想要发送的内容参数.在发送和接收端,我们可以根据text去找到ext,再根据自定义的消息cell去显示,具体如下
我们看到这条消息跟正常的聊天消息不一样,所以我们需要自定义一套cell来实现这些参数的显示:EaseUI本身有一套cell我们需要继承他们的cell以至于达到我们想要的目的.所以我们自定义一个继承EaseBaseMessageCell的cell 取名:IMChatBusinessCardCell,然后还需要针对EaseBubbleView写个分类,关联一些名片所需要的控件:取名IMChatBusinessCard .( commend+n选择Objective-C File如下图)
这些完成以后开始写代码,因为在自定义的聊天cell里我们需要显示商品图片,商品名字和商品价格.
所以我们在EaseBubbleView+IMChatBusinessCard.h中写
// 商品图片
@property(strong,nonatomic)UIImageView*userHeaderImageView;
// 商品名字
@property (strong, nonatomic) UILabel *userNameLabel;
// 商品价格
@property (strong, nonatomic) UILabel *userPhoneLabel;
// 设置名片气泡
- (void)setupBusinessCardBubbleView;
// 更新名片间距
- (void)updateBusinessCardMargin:(UIEdgeInsets)margin;
// 设置约束
- (void)_setupConstraintsXX;
然后在EaseBubbleView+IMChatBusinessCard.m中我这里全部复制一下,你们用的时候把重复的和没用的去掉就可以了.
#import "EaseBubbleView+IMChatBusinessCard.h"
#import
staticchar_userHeaderImageView_;
staticchar_userNameLabel_;
staticchar_userPhoneLabel_;
@implementationEaseBubbleView (IMChatBusinessCard)
- (void)_setupConstraintsXX{
[self.marginConstraints removeAllObjects];
//userHeaderImageView
NSLayoutConstraint*userHeaderImageViewTopConstraint =
[NSLayoutConstraint constraintWithItem:self.userHeaderImageView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:10];
NSLayoutConstraint*userHeaderImageViewLeadingConstraint =
[NSLayoutConstraint constraintWithItem:self.userHeaderImageView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:10];
[self.marginConstraintsaddObject:userHeaderImageViewTopConstraint];
[self.marginConstraintsaddObject:userHeaderImageViewLeadingConstraint];
NSLayoutConstraint*userHeaderImageViewHeightConstraint =
[NSLayoutConstraint constraintWithItem:self.userHeaderImageView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0.0
constant:60];
NSLayoutConstraint*userHeaderImageViewWidthConstraint =
[NSLayoutConstraint constraintWithItem:self.userHeaderImageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0.0
constant:60];
[self.userHeaderImageViewaddConstraint:userHeaderImageViewHeightConstraint];
[self.userHeaderImageViewaddConstraint:userHeaderImageViewWidthConstraint];
// userNameLabel
NSLayoutConstraint*userNameLabelWithMarginTopConstraint =
[NSLayoutConstraint constraintWithItem:self.userNameLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.userHeaderImageView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:2];
NSLayoutConstraint*userNameLabelWithMarginRightConstraint =
[NSLayoutConstraint constraintWithItem:self.userNameLabel
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:-self.margin.right];
NSLayoutConstraint*userNameLabelWithMarginLeftConstraint =
[NSLayoutConstraint constraintWithItem:self.userNameLabel
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.userHeaderImageView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:10];
[self.marginConstraintsaddObject:userNameLabelWithMarginRightConstraint];
[self.marginConstraintsaddObject:userNameLabelWithMarginTopConstraint];
[self.marginConstraintsaddObject:userNameLabelWithMarginLeftConstraint];
// userPhoneLabel
NSLayoutConstraint*userPhoneLabelTopConstraint =
[NSLayoutConstraint constraintWithItem:self.userPhoneLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.userHeaderImageView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:1];
NSLayoutConstraint*userPhoneLabelLeftConstraint =
[NSLayoutConstraint constraintWithItem:self.userPhoneLabel
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.userNameLabel
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
NSLayoutConstraint*userPhoneLabelRightConstraint =
[NSLayoutConstraint constraintWithItem:self.userPhoneLabel
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.backgroundImageView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:-self.margin.right];
[self.marginConstraintsaddObject:userPhoneLabelTopConstraint];
[self.marginConstraintsaddObject:userPhoneLabelLeftConstraint];
[self.marginConstraintsaddObject:userPhoneLabelRightConstraint];
[self addConstraints:self.marginConstraints];
NSLayoutConstraint *backImageConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0f constant:260];
[self.superviewaddConstraint:backImageConstraint];
}
#pragma mark - public
- (void)setupBusinessCardBubbleView{
// 头像
self.userHeaderImageView = [UIImageView new];
[self.userHeaderImageView setImage:[UIImage imageNamed:@"默认头像2"]];
self.userHeaderImageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.backgroundImageView addSubview:self.userHeaderImageView];
// 昵称
self.userNameLabel = [UILabel new];
self.userNameLabel.font = [UIFont systemFontOfSize:15.0f];
self.userNameLabel.textColor = [UIColor lightGrayColor];
self.userNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.userNameLabel.numberOfLines = 2;
[self.backgroundImageView addSubview:self.userNameLabel];
// 手机号
self.userPhoneLabel = [UILabel new];
self.userPhoneLabel.font = [UIFont systemFontOfSize:13.0f];
self.userPhoneLabel.textColor = [UIColor lightGrayColor];
self.userPhoneLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.backgroundImageView addSubview:self.userPhoneLabel];
[self _setupConstraintsXX];
}
- (void)updateBusinessCardMargin:(UIEdgeInsets)margin
{
if(_margin.top== margin.top&&_margin.bottom== margin.bottom&&_margin.left== margin.left&&_margin.right== margin.right) {
return;
}
_margin= margin;
[self removeConstraints:self.marginConstraints];
[self _setupConstraintsXX];
}
#pragma mark - getter and setter
- (void)setUserHeaderImageView:(UIImageView*)userHeaderImageView
{
objc_setAssociatedObject(self, &_userHeaderImageView_, userHeaderImageView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIImageView*)userHeaderImageView
{
return objc_getAssociatedObject(self, &_userHeaderImageView_);
}
- (void)setUserNameLabel:(UILabel*)userNameLabel
{
objc_setAssociatedObject(self, &_userNameLabel_, userNameLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UILabel*)userNameLabel
{
return objc_getAssociatedObject(self, &_userNameLabel_);
}
- (void)setUserPhoneLabel:(UILabel*)userPhoneLabel
{
objc_setAssociatedObject(self, &_userPhoneLabel_, userPhoneLabel, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UILabel*)userPhoneLabel
{
return objc_getAssociatedObject(self, &_userPhoneLabel_);
}
@end
这里设置完成后到IMChatBusinessCardCell.m中
也全部复制一下
#import "IMChatBusinessCardCell.h"
#import "EaseBubbleView+IMChatBusinessCard.h"
staticconstCGFloatkCellHeight =110.0f;
@implementationIMChatBusinessCardCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier model:(id)model{
self= [superinitWithStyle:stylereuseIdentifier:reuseIdentifiermodel:model];
if(self) {
self.hasRead.hidden=YES;
self.selectionStyle = UITableViewCellSelectionStyleNone;
}
return self;
}
- (BOOL)isCustomBubbleView:(id)model{
return YES;
}
- (void)setCustomModel:(id)model{
UIImage*image = model.image;
if(!image) {
[self.bubbleView.imageView sd_setImageWithURL:[NSURL URLWithString:model.fileURLPath] placeholderImage:[UIImage imageNamed:model.failImageName]];
}else{
_bubbleView.imageView.image = image;
}
if(model.avatarURLPath) {
[self.avatarView sd_setImageWithURL:[NSURL URLWithString:model.avatarURLPath] placeholderImage:model.avatarImage];
}else{
self.avatarView.image = model.avatarImage;
}
}
- (void)setCustomBubbleView:(id)model{
[_bubbleView setupBusinessCardBubbleView];
_bubbleView.imageView.image = [UIImage imageNamed:@"80"];
}
- (void)updateCustomBubbleViewMargin:(UIEdgeInsets)bubbleMargin model:(id)mode{
[_bubbleView updateBusinessCardMargin:bubbleMargin];
_bubbleView.translatesAutoresizingMaskIntoConstraints = YES;
CGFloatbubbleViewHeight =84;// 气泡背景图高度
CGFloatnameLabelHeight =15;// 昵称label的高度
if(mode.isSender) {
_bubbleView.frame =
CGRectMake([UIScreenmainScreen].bounds.size.width-273.5, nameLabelHeight,213, bubbleViewHeight);
}else{
_bubbleView.frame=CGRectMake(55, nameLabelHeight,213, bubbleViewHeight);
}
// 这里强制调用内部私有方法
[_bubbleView _setupConstraintsXX];
}
- (NSString*)cellIdentifierWithModel:(id)model{
return NSStringFromClass([self class]);
}
- (CGFloat)cellHeightWithModel:(id)model{
return kCellHeight;
}
- (void)setModel:(id)model{
[supersetModel:model];
NSDictionary *ext = [[NSDictionary alloc]initWithDictionary:model.message.ext];
//发送了商品信息的情况
if(ext !=nil) {
self.bubbleView.userNameLabel.text= ext[@"goodName"];
self.bubbleView.userPhoneLabel.text = [NSString stringWithFormat:@"¥%@",ext[@"goodPrice"]];
[self.bubbleView.userHeaderImageView sd_setImageWithURL:[NSURL URLWithString:ext[@"goodImage"]]];
}
_hasRead.hidden = YES;//名片消息不显示已读
}
- (void)layoutSubviews
{
[super layoutSubviews];
NSString*imageName =self.model.isSender?@"bai":@"lam";
UIImage *image = self.model.isSender ? [[UIImage imageNamed:imageName] stretchableImageWithLeftCapWidth:30 topCapHeight:35] :
[[UIImage imageNamed:imageName] stretchableImageWithLeftCapWidth:20 topCapHeight:35];
}
@end
然后我们回到自定义的聊天控制器CahtWithPVControllerViewController
导入刚才的类头文件
#import "IMChatBusinessCardCell.h"
#import "EaseBubbleView+IMChatBusinessCard.h"
我们需要实现- (UITableViewCell*)messageViewController:(UITableView*)tableView
cellForMessageModel:(id)messageModel;这个方法来显示我们自定义的cell
- (UITableViewCell*)messageViewController:(UITableView*)tableView
cellForMessageModel:(id)messageModel
{
if(messageModel.bodyType==EMMessageBodyTypeText&&
[[messageModeltext]hasPrefix:@"商品链接"]) {
NSString *CellIdentifier = [NSString stringWithFormat:@"%@", [NSDate dateTomorrow]];
IMChatBusinessCardCell*cell = (IMChatBusinessCardCell*)[tableViewdequeueReusableCellWithIdentifier:CellIdentifier];
if(cell ==nil) {
cell = [[IMChatBusinessCardCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier model:messageModel];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
cell.model= messageModel;
returncell;
}
return nil;
}
然后更改一下自定义消息的高度
- (CGFloat)messageViewController:(EaseMessageViewController*)viewController
heightForMessageModel:(id)messageModel
withCellWidth:(CGFloat)cellWidth
{
NSDictionary*ext = messageModel.message.ext;
if([extobjectForKey:@"em_recall"]) {
return self.timeCellHeight;
}
if([[messageModeltext]hasPrefix:@"商品链接"]) {
return130;
}
return 0;
}
为显示cell消息赋值的时候我们需要实现- (id)messageViewController:(EaseMessageViewController*)viewController modelForMessage:(EMMessage*)message;这个方法.
- (id)messageViewController:(EaseMessageViewController*)viewController modelForMessage:(EMMessage*)message
{
id model =nil;
model = [[EaseMessageModel alloc] initWithMessage:message];
if(model.isSender) {//自己发送
if([_typeChatisEqualToString:@"store"]) {
if(message.ext!=nil&& [[message.extallKeys]containsObject:@"goodName"]) {
model.message.ext =@{@"nickname":[SingleUserInfoManage shareData].nickname,@"headimgurl":[SingleUserInfoManage shareData].im_headimgurl,@"goodName":message.ext[@"goodName"],@"goodPrice":message.ext[@"goodPrice"],@"goodImage":message.ext[@"goodImage"],@"goodId":message.ext[@"goodId"]};
}else{
model.message.ext= @{@"headimgurl":[SingleUserInfoManage shareData].im_headimgurl,@"nickname":[SingleUserInfoManage shareData].nickname};
}
//头像
model.avatarURLPath = [SingleUserInfoManage shareData].im_headimgurl;
//昵称
model.nickname = [SingleUserInfoManage shareData].nickname;
//头像占位图
model.avatarImage = [UIImage imageNamed:@"默认头像2"];
}else{
if(message.ext!=nil&&[[message.extallKeys]containsObject:@"goodName"]) {
model.message.ext =@{@"nickname":[SingleUserInfoManage shareData].nickname,@"headimgurl":[SingleUserInfoManage shareData].im_headimgurl,@"goodName":message.ext[@"goodName"],@"goodPrice":message.ext[@"goodPrice"],@"goodImage":message.ext[@"goodImage"],@"goodId":message.ext[@"goodId"]};
}else{
model.message.ext= @{@"headimgurl":[SingleUserInfoManage shareData].im_headimgurl,@"nickname":[SingleUserInfoManage shareData].nickname};
}
//头像
model.avatarURLPath = [SingleUserInfoManage shareData].im_headimgurl;
//昵称
model.nickname = [SingleUserInfoManage shareData].nickname;
//头像占位图
model.avatarImage = [UIImage imageNamed:@"默认头像2"];
}
}else{//对方发送
//头像占位图
model.avatarImage = [UIImage imageNamed:@"默认头像2"];
//头像avatar
model.avatarURLPath= message.ext[@"headimgurl"];
//昵称
model.nickname= message.ext[@"nickname"];
}
returnmodel;
}
(附加)如果我们需要点击这个自定义消息,可以实现
我这里是跳转到商品详情:
//消息的点击
- (BOOL)messageViewController:(EaseMessageViewController*)viewController
didSelectMessageModel:(id)messageModel {
if(messageModel.bodyType==EMMessageBodyTypeText&&
[[messageModeltext]hasPrefix:@"商品链接"]) {
NSDictionary *ext = [[NSDictionary alloc]initWithDictionary:messageModel.message.ext];
FenLeiDDetailViewController *vc = [[FenLeiDDetailViewController alloc] init];
vc.goodsID= ext[@"goodId"];
[self.navigationController pushViewController:vc animated:YES];
}
return YES;
}
我们在这里面做很多判断主要用来区分正常消息和自定义消息,自定义消息(我们在发送消息的时候有一个text作为自定义消息的标识)由我们自定义的控件去完成.正常的消息由环信的控件去完成.这里面可能有一些我自己项目中用到你们用不到的东西,你们可以检查一下代码,修改成自己所需要的,有错误的调整一下.原理和代码大概就是这样.
最后附上demo,由于是从项目中抽取出来的,所以有点乱,但是还是能看的,哈哈,
链接:https://pan.baidu.com/s/10HPg3J2bh42VPs7B0mkZVA 密码:eijz