上篇文章,我们整理了网易云信的快速集成过程.有兴趣的小伙伴可以移步到:ios集成网易云信IM功能遇到的坑 .简单的实现一对一聊天界面或者查看最近联系人列表.我们只需直接调用NIMKit里面的NIMSessionViewController 和 NIMSessionListViewController这两个类方法.就能实现功能.但是简单的聊天可能满足不了我们项目需要.比如要实现电商平台中,我们浏览商品.突然觉得商品还不错.去找客服聊天.那么这时候我们希望把浏览的商品也给推送过去.好让客服知道我们在看什么.并且,自定义消息设置成可点击的,方便可以点击查看商品详情.
今天我就给大家整理下怎么实现自定义消息发送.
其实具体也可以参考官方demo中实现发送大表情效果过程.毕竟大表情图片其实也是一种自定义消息发送的表现形式.
那么,发送自定义消息,我大概整理了下,觉得可以分为四部分,这样实现起来逻辑会觉得比较清晰.如下:
1.实现自定义消息model类.承接数据部分.
2.实现自定义消息view 类,承接自定义UI部分.
3.实现自定义布局配置类.
4.消息解析器(解析消息,辨别消息类型!)
最后一步,就是在入口类中注册拉起解析器和自定义布局.这样,系统才能识别消息类型,并实现自定义布局.
那!第一步,我们实现自定义消息model类,承接数据部分.
创建一个类,继承与NSObject.并实现NIMKit <NIMCustomAttachment,NTESCustomAttachmentInfo>两个协议方法.
.h类中:
#import
#import "NIMKit.h"
#import "NTESCustomAttachmentDefines.h"
@interface NTESSendShopGoodsAttachment :NSObject<NIMCustomAttachment,NTESCustomAttachmentInfo>
//添加自定义消息元素
@property(nonatomic,copy)NSString*headerImage;
@property(nonatomic,copy)NSString*nameLabel;
@property(nonatomic,copy)NSString*priceLabel;
//数据源承接类
@property(nonatomic,strong)NSDictionary*messageDic;
@end
.m类中:
#import "NTESSendShopGoodsAttachment.h"
@implementation NTESSendShopGoodsAttachment
//对数据源进行类型分配.
- (NSString *)encodeAttachment
{
NSDictionary *dictContent =@{CM_GOODS_IMG:self.headerImage,CM_GOODS_NAME:self.nameLabel,CM_GOODS_PRICE:self.priceLabel};
NSDictionary *dict =@{CMType :@(CustomMessageSendShopGoods),
CMData :dictContent};
NSData *data = [NSJSONSerializationdataWithJSONObject:dict
options:0
error:nil];
NSString *content =nil;
if (data) {
content = [[NSStringalloc] initWithData:data
encoding:NSUTF8StringEncoding];
}
return content;
}
//实例化元素(懒加载...)
-(NSString *)headerImage{
if (!_headerImage) {
_headerImage =nil;
}
return_headerImage;
}
-(NSString *)nameLabel{
if (!_nameLabel) {
_nameLabel =nil;
}
return_nameLabel;
}
-(NSString *)priceLabel
{
if (!_priceLabel) {
_priceLabel =nil;
}
return_priceLabel;
}
//数据源setter方法实现
-(void)setMessageDic:(NSDictionary *)messageDic
{
_messageDic =messageDic;
//给元素赋值
self.headerImage =[_messageDic[@"picture"]substringFromIndex:3];
self.nameLabel =_messageDic[@"name"];
self.priceLabel =_messageDic[@"price"];
}
//设置自定义消息Bounds
- (CGSize)contentSize:(NIMMessage *)message cellWidth:(CGFloat)width{
returnCGSizeMake(200,70);
}
- (UIEdgeInsets)contentViewInsets:(NIMMessage *)message
{
if (message.session.sessionType == NIMSessionTypeChatroom)
{
CGFloat bubbleMarginTopForImage =15.f;
CGFloat bubbleMarginLeftForImage =12.f;
return UIEdgeInsetsMake(bubbleMarginTopForImage,bubbleMarginLeftForImage,0,0);
}
else
{
CGFloat bubbleMarginForImage =3.f;
CGFloat bubbleArrowWidthForImage =5.f;
if (message.isOutgoingMsg) {
return UIEdgeInsetsMake(bubbleMarginForImage,bubbleMarginForImage,bubbleMarginForImage,bubbleMarginForImage + bubbleArrowWidthForImage);
}else{
return UIEdgeInsetsMake(bubbleMarginForImage,bubbleMarginForImage + bubbleArrowWidthForImage, bubbleMarginForImage,bubbleMarginForImage);
}
}
}
//指向自定义view类
- (NSString *)cellContent:(NIMMessage *)message{
return@"NTESSessionSendShopGoodsContentView";
}
#import "NIMSessionMessageContentView.h"
//用于点击事件触发标识符!!!
extern NSString *const SendCustomWithGoodsClick;
@interface NTESSessionSendShopGoodsContentView :NIMSessionMessageContentView
//自定义展示商品信息控件
@property(nonatomic,strong)UIImageView *headerImg;
@property(nonatomic,strong)UILabel*nameLa;
@property(nonatomic,strong)UILabel*priceLa;
@end
#import "NTESSessionSendShopGoodsContentView.h"
#import "NTESSendShopGoodsAttachment.h"
#import "UIView+SL.h"
#import "UIImage+Image.h"
NSString *const SendCustomWithGoodsClick =@"SendCustomWithGoodsClick";
@interface NTESSessionSendShopGoodsContentView()
//添加手势
@property (nonatomic,strong)UITapGestureRecognizer*top;
@end
@implementation NTESSessionSendShopGoodsContentView
- (instancetype)initSessionMessageContentView{
self = [superinitSessionMessageContentView];
if (self) {
self.opaque =YES;
//点击自定义消息cell,添加手势
self.top = [[UITapGestureRecognizeralloc] initWithTarget:selfaction:@selector(tapGesture:)];
[selfaddGestureRecognizer:self.top];
self.headerImg =[[UIImageViewalloc]initWithFrame:CGRectZero];
[selfaddSubview:self.headerImg];
self.nameLa =[[UILabel alloc]initWithFrame:CGRectZero];
_nameLa.font =[UIFontsystemFontOfSize:13];
_nameLa.numberOfLines =0;
[selfaddSubview:self.nameLa];
self.priceLa =[[UILabelalloc]initWithFrame:CGRectZero];
_priceLa.font =[UIFontsystemFontOfSize:12];
_priceLa.textColor =[UIColororangeColor];
[selfaddSubview:self.priceLa];
}
returnself;
}
#pragma mark - 点击手势
- (void)tapGesture:(UITapGestureRecognizer *)recognizer {
if ([self.delegaterespondsToSelector:@selector(onCatchEvent:)]) {
NIMKitEvent *event = [[NIMKitEventalloc] init];
//添加自定义消息标识符
event.eventName =SendCustomWithGoodsClick;
event.messageModel =self.model;
event.data =self;
[self.delegateonCatchEvent:event];
}
}
//赋值
- (void)refresh:(NIMMessageModel *)data{
[superrefresh:data];
NIMCustomObject *customObject = (NIMCustomObject*)data.message.messageObject;
id attachment = customObject.attachment;
if ([attachmentisKindOfClass:[NTESSendShopGoodsAttachmentclass]]) {
self.nameLa.text =[attachmentnameLabel ];
IMAGE_URL(self.headerImg,[attachment headerImage],@"header.jpg");
self.priceLa.text =[attachmentpriceLabel];
}
}
//自定义view元素控件坐标设置
- (UIImage *)chatBubbleImageForState:(UIControlState)state outgoing:(BOOL)outgoing{
self.headerImg.frame =CGRectMake(7,8,54,54);
self.nameLa.frame =CGRectMake(70,5,125,40);
self.priceLa.frame =CGRectMake(70,50,70,15);
//背景颜色
return [UIImageimageWithColor:[UIColorwhiteColor]];
}
@end
#import "NIMCellLayoutConfig.h"
#import "NIMKit.h"
@interface NTESCellLayoutConfig :NIMCellLayoutConfig<NIMCellLayoutConfig>
@end
#import "NTESCellLayoutConfig.h"
#import "NTESSessionCustomContentConfig.h"
@interface NTESCellLayoutConfig ()
@property (nonatomic,strong) NSArray *types;
@property (nonatomic,strong) NTESSessionCustomContentConfig *sessionCustomconfig;
@end
.m文件:
@implementation NTESCellLayoutConfig
- (instancetype)init
{
if (self = [superinit])
{
//添加消息类型!我们写在了第一个
_types = @[
@"NTESSendShopGoodsAttachment",
@"NTESSnapchatAttachment",
@"NTESChartletAttachment",
@"NTESWhiteboardAttachment"
];
_sessionCustomconfig = [[NTESSessionCustomContentConfigalloc] init];
}
returnself;
}
#pragma mark - NIMCellLayoutConfig
- (CGSize)contentSize:(NIMMessageModel *)model cellWidth:(CGFloat)width{
NIMMessage *message = model.message;
//检查是不是当前支持的自定义消息类型
if ([selfisSupportedCustomMessage:message]) {
return [_sessionCustomconfigcontentSize:width message:message];
}
//检查是不是聊天室文本消息
if ([selfisChatroomTextMessage:message]) {
// return [_chatroomTextConfig contentSize:width message:message];
}
//如果没有特殊需求,就走默认处理流程
return [supercontentSize:model
cellWidth:width];
}
- (NSString *)cellContent:(NIMMessageModel *)model{
NIMMessage *message = model.message;
//检查是不是当前支持的自定义消息类型
if ([selfisSupportedCustomMessage:message]) {
return [_sessionCustomconfigcellContent:message];
}
//检查是不是聊天室文本消息
if ([selfisChatroomTextMessage:message]) {
// return [_chatroomTextConfig cellContent:message];
}
//如果没有特殊需求,就走默认处理流程
return [supercellContent:model];
}
- (UIEdgeInsets)contentViewInsets:(NIMMessageModel *)model
{
NIMMessage *message = model.message;
//检查是不是当前支持的自定义消息类型
if ([selfisSupportedCustomMessage:message]) {
return [_sessionCustomconfigcontentViewInsets:message];
}
//检查是不是聊天室文本消息
if ([selfisChatroomTextMessage:message]) {
// return [_chatroomTextConfig contentViewInsets:message];
}
//如果没有特殊需求,就走默认处理流程
return [supercontentViewInsets:model];
}
- (UIEdgeInsets)cellInsets:(NIMMessageModel *)model
{
NIMMessage *message = model.message;
//检查是不是聊天室消息
if (message.session.sessionType ==NIMSessionTypeChatroom) {
returnUIEdgeInsetsZero;
}
//如果没有特殊需求,就走默认处理流程
return [supercellInsets:model];
}
- (BOOL)shouldShowAvatar:(NIMMessageModel *)model
{
if ([selfisSupportedChatroomMessage:model.message]) {
returnNO;
}
return [supershouldShowAvatar:model];
}
- (BOOL)shouldShowLeft:(NIMMessageModel *)model{
if ([selfisSupportedChatroomMessage:model.message]) {
returnYES;
}
return [supershouldShowLeft:model];
}
- (BOOL)shouldShowNickName:(NIMMessageModel *)model{
if ([selfisSupportedChatroomMessage:model.message]) {
returnYES;
}
return [supershouldShowNickName:model];
}
- (CGFloat)nickNameMargin:(NIMMessageModel *)model{
if ([selfisSupportedChatroomMessage:model.message]) {
NSDictionary *ext = model.message.remoteExt;
NIMChatroomMemberType type = [ext[@"type"]integerValue];
switch (type) {
caseNIMChatroomMemberTypeManager:
caseNIMChatroomMemberTypeCreator:
return50.f;
default:
break;
}
return15.f;
}
return [supernickNameMargin:model];
}
- (NSArray *)customViews:(NIMMessageModel *)model
{
if ([selfisSupportedChatroomMessage:model.message]) {
NSDictionary *ext = model.message.remoteExt;
NIMChatroomMemberType type = [ext[@"type"]integerValue];
NSString *imageName;
switch (type) {
caseNIMChatroomMemberTypeManager:
imageName = @"chatroom_role_manager";
break;
caseNIMChatroomMemberTypeCreator:
imageName = @"chatroom_role_master";
break;
default:
break;
}
UIImageView *imageView;
if (imageName.length) {
UIImage *image = [UIImageimageNamed:imageName];
imageView = [[UIImageViewalloc] initWithImage:image];
CGFloat leftMargin =15.f;
CGFloat topMatgin =0.f;
CGRect frame = imageView.frame;
frame.origin =CGPointMake(leftMargin, topMatgin);
imageView.frame = frame;
}
return imageView ?@[imageView] :nil;
}
return [supercustomViews:model];
}
#pragma mark - misc
- (BOOL)isSupportedCustomMessage:(NIMMessage *)message
{
NIMCustomObject *object = message.messageObject;
return [objectisKindOfClass:[NIMCustomObjectclass]] &&
[_typesindexOfObject:NSStringFromClass([object.attachmentclass])] != NSNotFound;
}
- (BOOL)isSupportedChatroomMessage:(NIMMessage *)message
{
return message.session.sessionType == NIMSessionTypeChatroom &&
(message.messageType ==NIMMessageTypeText || [selfisSupportedCustomMessage:message]);
}
- (BOOL)isChatroomTextMessage:(NIMMessage *)message
{
return message.session.sessionType == NIMSessionTypeChatroom &&
message.messageObject ==NIMMessageTypeText;
}
#import "NIMBaseSessionContentConfig.h"
@interface NTESSessionCustomContentConfig :NSObject<NIMSessionContentConfig>
@end
#import "NTESSessionCustomContentConfig.h"
#import "NTESCustomAttachmentDefines.h"
@interface NTESSessionCustomContentConfig()
@end
@implementation NTESSessionCustomContentConfig
- (CGSize)contentSize:(CGFloat)cellWidth message:(NIMMessage *)message
{
NIMCustomObject *object = message.messageObject;
NSAssert([object isKindOfClass:[NIMCustomObject class]],@"message must be custom");
id<NTESCustomAttachmentInfo> info = (id<NTESCustomAttachmentInfo>)object.attachment;
return [infocontentSize:message cellWidth:cellWidth];
}
- (NSString *)cellContent:(NIMMessage *)message
{
NIMCustomObject *object = message.messageObject;
NSAssert([object isKindOfClass:[NIMCustomObject class]],@"message must be custom");
id<NTESCustomAttachmentInfo> info = (id<NTESCustomAttachmentInfo>)object.attachment;
return [infocellContent:message];
}
- (UIEdgeInsets)contentViewInsets:(NIMMessage *)message
{
NIMCustomObject *object = message.messageObject;
NSAssert([object isKindOfClass:[NIMCustomObject class]],@"message must be custom");
id<NTESCustomAttachmentInfo> info = (id<NTESCustomAttachmentInfo>)object.attachment;
return [infocontentViewInsets:message];
}
@end
#import
#import "NIMKit.h"
@interface NTESCustomAttachmentDecoder :NSObject<NIMCustomAttachmentCoding>
@end
#import "NTESCustomAttachmentDecoder.h"
#import "NTESCustomAttachmentDefines.h"
#import "NSDictionary+SLJSON.h"
#import "NTESSendShopGoodsAttachment.h"
#import "NTESChartletAttachment.h"
@implementation NTESCustomAttachmentDecoder
- (id<NIMCustomAttachment>)decodeAttachment:(NSString *)content
{
id<NIMCustomAttachment> attachment =nil;
NSData *data = [contentdataUsingEncoding:NSUTF8StringEncoding];
if (data) {
NSDictionary *dict = [NSJSONSerializationJSONObjectWithData:data
options:0
error:nil];
if ([dictisKindOfClass:[NSDictionaryclass]])
{
NSInteger type = [dictjsonInteger:CMType];
NSDictionary *data = [dictjsonDict:CMData];
switch (type) {
caseCustomMessageTypeChartlet:
{
attachment = [[NTESChartletAttachmentalloc] init];
((NTESChartletAttachment *)attachment).chartletCatalog = [datajsonString:CMCatalog];
((NTESChartletAttachment *)attachment).chartletId = [datajsonString:CMChartlet];
}
break;
caseCustomMessageSendShopGoods:
{
attachment = [[NTESSendShopGoodsAttachmentalloc] init];
((NTESSendShopGoodsAttachment *)attachment).headerImage = [datajsonString:CM_GOODS_IMG];
((NTESSendShopGoodsAttachment *)attachment).nameLabel = [datajsonString:CM_GOODS_NAME];
((NTESSendShopGoodsAttachment *)attachment).priceLabel = [datajsonString:CM_GOODS_PRICE]; }
default:
break;
}
attachment = [selfcheckAttachment:attachment] ? attachment :nil;
}
}
return attachment;
}
- (BOOL)checkAttachment:(id<NIMCustomAttachment>)attachment{
BOOL check =NO;
if ([attachmentisKindOfClass:[NTESSendShopGoodsAttachmentclass]]) {
NSString *headerImg = ((NTESSendShopGoodsAttachment *)attachment).headerImage;
NSString *shopNameLa = ((NTESSendShopGoodsAttachment *)attachment).nameLabel;
NSString *priceLa = ((NTESSendShopGoodsAttachment *)attachment).priceLabel;
check = shopNameLa.length&&priceLa.length&&headerImg.length ?YES : NO;
}elseif ([attachment isKindOfClass:[NTESChartletAttachmentclass]]) {
NSString *chartletCatalog = ((NTESChartletAttachment *)attachment).chartletCatalog;
NSString *chartletId =((NTESChartletAttachment *)attachment).chartletId;
check = chartletCatalog.length&&chartletId.length ?YES : NO;
}
return check;
}
@end
[NIMCustomObjectregisterCustomDecoder:[NTESCustomAttachmentDecodernew]];
//注册 NIMKit自定义排版配置
[[NIMKitsharedKit] registerLayoutConfig:[NTESCellLayoutConfignew]];
在需要发送商品自定义消息的地方:
//自定义消息
-(void)sendManager:(UIButton *)btn
{
//隐藏topView
static int a =80;
[UIViewanimateWithDuration:0.5animations:^{
CGRect rect =self.topView.frame;
rect.origin.y -=a;
self.topView.frame =rect;
} completion:^(BOOL finished) {
[self.topViewremoveFromSuperview];
}];
//发送自定义消息
self.attachment = [[NTESSendShopGoodsAttachmentalloc] init];
_attachment.messageDic =self.messageDict;
NIMMessage *message = [[NIMMessagealloc] init];
NIMCustomObject *customObject = [[NIMCustomObjectalloc] init];
customObject.attachment =_attachment;
message.messageObject = customObject;
[selfsendMessage:message];
}
#pragma mark ---------------- cell点击事件 ------------------
- (NSDictionary *)cellActions
{
staticNSDictionary *actions = nil;
staticdispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
actions = @{@(NIMMessageTypeImage) : @"showImage:",
@(NIMMessageTypeVideo) : @"showVideo:",
@(NIMMessageTypeLocation) :@"showLocation:",
@(NIMMessageTypeFile) : @"showFile:",
@(NIMMessageTypeCustom): @"showCustom:"};
});
return actions;
}
-(BOOL)onTapCell:(NIMKitEvent *)event
{
BOOL handle =[superonTapCell:event];
NSString *eventName = event.eventName;
if ([eventNameisEqualToString:NIMKitEventNameTapAudio])
{
//音频
[self.interactormediaAudioPressed:event.messageModel];
handle = YES;
}
elseif([eventNameisEqualToString:NIMKitEventNameTapContent]){
BOOL handle =NO;
NIMMessage *message = event.messageModel.message;
NSDictionary *actions = [selfcellActions];
NSString *value = actions[@(message.messageType)];
if (value) {
SEL selector =NSSelectorFromString(value);
NSLog(@"value ---- %@,%d",value,(selector && [selfrespondsToSelector:selector]));
if (selector && [selfrespondsToSelector:selector]) {
SuppressPerformSelectorLeakWarning([self performSelector:selector withObject:message]);
handle = YES;
}
}
}
elseif([eventNameisEqualToString:NIMKitEventNameTapLabelLink])
{
NSString *link = event.data;
[self.viewmakeToast:[NSStringstringWithFormat:@"tap link : %@",link]
duration:2
position:CSToastPositionCenter];
handle = YES;
}elseif ([eventName isEqualToString:SendCustomWithGoodsClick]){
//自定义商品注入
NIMCustomObject * customObject =(NIMCustomObject *)event.messageModel.message.messageObject;
handle =YES;
}
// if (!handle) {
// NSAssert(0, @"invalid event ----哈哈哈!垃圾...崩溃了");
// }
return handle;
}