集成网易云信实现自定义消息(类似淘宝聊天发送商品信息)

        上篇文章,我们整理了网易云信的快速集成过程.有兴趣的小伙伴可以移步到:ios集成网易云信IM功能遇到的坑 .简单的实现一对一聊天界面或者查看最近联系人列表.我们只需直接调用NIMKit里面的NIMSessionViewController 和 NIMSessionListViewController这两个类方法.就能实现功能.但是简单的聊天可能满足不了我们项目需要.比如要实现电商平台中,我们浏览商品.突然觉得商品还不错.去找客服聊天.那么这时候我们希望把浏览的商品也给推送过去.好让客服知道我们在看什么.并且,自定义消息设置成可点击的,方便可以点击查看商品详情.

      今天我就给大家整理下怎么实现自定义消息发送.

      其实具体也可以参考官方demo中实现发送大表情效果过程.毕竟大表情图片其实也是一种自定义消息发送的表现形式.

       那么,发送自定义消息,我大概整理了下,觉得可以分为四部分,这样实现起来逻辑会觉得比较清晰.如下:

      1.实现自定义消息model类.承接数据部分.

      2.实现自定义消息view 类,承接自定义UI部分.

      3.实现自定义布局配置类.

      4.消息解析器(解析消息,辨别消息类型!)


      最后一步,就是在入口类中注册拉起解析器和自定义布局.这样,系统才能识别消息类型,并实现自定义布局.

 

      那!第一步,我们实现自定义消息model类,承接数据部分.

      创建一个类,继承与NSObject.并实现NIMKit  <NIMCustomAttachment,NTESCustomAttachmentInfo>两个协议方法.  

    .h类中:

#import <Foundation/Foundation.h>

#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";

}


   那!我们现在创建自定义view类.自定义view类继承与父类: NIMSessionMessageContentView
   .h文件:

#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


  .m文件:

#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


自定义cell布局类继承父类: NIMCellLayoutConfig
.h文件:

#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;

}


以及自定义消息配置类:
.h文件:

#import "NIMBaseSessionContentConfig.h"


@interface NTESSessionCustomContentConfig :NSObject<NIMSessionContentConfig>


@end


.m文件:

#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

 
好了,到这里,自定义view和model类以及自定义cell布局类都设置完成了.

接下来我们实现自定义消息解析处理类:
.h文件:

#import <Foundation/Foundation.h>

#import "NIMKit.h"

@interface NTESCustomAttachmentDecoder :NSObject<NIMCustomAttachmentCoding>


@end



.m文件:

#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



以上四部分处理我们都做完了.现在去入口类注册解析器和自定义cell配置:

    // 注册自定义消息的解析器

    [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];

}


在cell点击事件中,我们添加自定义消息类型判断(方法中文字为黑色部分).通过点击手势,进入其他界面.

#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;
        id attachment =customObject.attachment;
        NSString * goodsId =[attachment goodsAttaId];
        //自定义商品注入
        JKNewGoodsDetailVC * goodVC =[[JKNewGoodsDetailVC alloc]init];
        //添加商品id
        goodVC.goodsId = [goodsId intValue];
        [self.navigationController pushViewController:goodVC animated:YES];

        handle =YES;

    }

//    if (!handle) {

//        NSAssert(0, @"invalid event ----哈哈哈!垃圾...崩溃了");

//    }


    return handle;

}

到此为止,我们的代码算是写完了!运行我们的模拟器!效果如下:



 代码还不太完整.不过整体步骤和思路很清晰了.大家可以做个参考.如果有地方不明白.可以私聊我咨询.或者加入One Team:234713941 技术交流群







 




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值