No description, website, or topics provided.
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
NIMDemo
NIMKit
README.md

README.md

说明:

  • 将appDelegate中[[MMEmotionCentre defaultCentre] setAppId:@“your app id” secret:@“your secret”]设置成分配到的idsecret
  • 本Demo在网易云讯官方Demo 4.5的基础上集成了BQMM 2.1,在修改的地方我们都加上了“BQMM集成"的注释,可在项目中全局搜索查看。

表情云SDK接入文档

接入SDK,有以下必要步骤:

  1. 下载与安装
  2. 获取必要的接入信息
  3. 开始集成

##第一步:下载与安装

目前有两种方式安装SDK:

  • 通过CocoaPods管理依赖。
  • 手动导入SDK并管理依赖。

###1. 使用 CocoaPods 导入SDK

在终端中运行以下命令:

pod search BQMM

如果运行以上命令,没有搜到SDK,或者搜不到最新的 SDK 版本,您可以运行以下命令,更新您本地的 CocoaPods 源列表。

pod repo update

在您工程的 Podfile中添加最新版本的SDK(在此以2.1版本为例):

pod 'BQMM', '2.1'

然后在工程的根目录下运行以下命令:

pod install

说明:pod中不包含gif表情的UI模块,可点击下载,解压后,手动导入BQMM_GIF

###2. 手动导入SDK

下载当前最新版本,解压缩后获得3个文件夹

  • BQMM
  • BQMM_EXT
  • BQMM_GIF

BQMM中包含SDK所需的资源文件BQMM.bundle和库文件BQMM.framework;BQMM_EXT提供了SDK的默认消息显示控件和消息默认格式的开源代码,开发者们导入后可按需修改;BQMM_GIF中包含gif表情的UI模块,开发者导入后可按需修改。

###3. 添加系统库依赖

您除了在工程中导入 SDK 之外,还需要添加libz动态链接库。

##第二步:获取必要的接入信息

开发者将应用与SDK进行对接时,必要接入信息如下

  • appId - 应用的App ID
  • appSecret - 应用的App Secret

如您暂未获得以上接入信息,可以在此申请

##第三步:开始集成

###0. 注册AppId&AppSecret、设置SDK语言和区域

AppDelegate-application:didFinishLaunchingWithOptions: 中添加:

// 初始化SDK
[[MMEmotionCentre defaultCentre] setAppId:@“your app idsecret:@“your secret”]

//设置SDK语言和区域
[MMEmotionCentre defaultCentre].sdkLanguage = MMLanguageEnglish;
[MMEmotionCentre defaultCentre].sdkRegion = MMRegionChina;

###1. 在App重新打开时清空session

AppDelegate- (void)applicationWillEnterForeground: 中添加:

[[MMEmotionCentre defaultCentre] clearSession];

###2. 使用表情键盘和GIF搜索模块

####设置SDK代理

NIMSessionViewController.m

- (void)viewWillAppear:(BOOL)animated
    ....
    //BQMM集成
    [MMEmotionCentre defaultCentre].delegate = self;
    ....
}

- (void)viewWillDisappear:(BOOL)animated {
    //BQMM集成
    [MMEmotionCentre defaultCentre].delegate = nil;
    [super viewWillDisappear:animated];
}

####配置GIF搜索模块

NIMSessionViewController.m

- (void)viewDidLoad {
    ....
    //BQMM集成   设置gif搜索相关
    [[MMGifManager defaultManager] setSearchModeEnabled:true withInputView:_inputToolBar.inputTextView];
    [[MMGifManager defaultManager] setSearchUiVisible:true withAttatchedView:_inputToolBar];
        
    __weak typeof(self) weakSelf = self;
    [MMGifManager defaultManager].selectedHandler = ^(MMGif * _Nullable gif) {
        __strong MMChatViewController *strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf didSendGifMessage:gif];
        }
    };
    ....
}

-(void)didSendGifMessage:(MMGif *)gif {
    NSString *sendStr = [@"[" stringByAppendingFormat:@"%@]", gif.text];
    NSDictionary *msgData = @{WEBSTICKER_URL: gif.mainImage, WEBSTICKER_IS_GIF: (gif.isAnimated ? @"1" : @"0"), WEBSTICKER_ID: gif.imageId,WEBSTICKER_WIDTH: @((float)gif.size.width), WEBSTICKER_HEIGHT: @((float)gif.size.height)};
    NSDictionary *extDic = @{TEXT_MESG_TYPE:TEXT_MESG_WEB_TYPE,
                             TEXT_MESG_DATA:msgData
                             };
    NIMMessage *message = [NIMMessageMaker msgWithText:sendStr];
    message.remoteExt = extDic;
    
    [self sendMessage:message];
}

####实现SDK代理方法

NIMSessionViewController.m 实现了SDK的代理方法

//点击表情键盘上的gif tab
- (void)didClickGifTab {
    //点击gif tab 后应该保证搜索模式是打开的 搜索UI是允许显示的
    [[MMGifManager defaultManager] setSearchModeEnabled:true withInputView:_sessionInputView.toolBar.inputTextView.textView];
    [[MMGifManager defaultManager] setSearchUiVisible:true withAttatchedView:_sessionInputView.toolBar];
    [[MMGifManager defaultManager] showTrending];
    
    [_sessionInputView refreshStatus:NIMInputStatusText];
}

//点击键盘中大表情的代理
- (void)didSelectEmoji:(MMEmoji *)emoji
{
    [self sendMMFace:emoji];
}

//点击联想表情的代理 (`deprecated`)
- (void)didSelectTipEmoji:(MMEmoji *)emoji
{
    [self sendMMFace:emoji];
    _sessionInputView.toolBar.inputTextView.text = @"";
}

//点击小表情键盘上发送按钮的代理
- (void)didSendWithInput:(UIResponder<UITextInput> *)input
{
    UITextView *textView = (UITextView *)input;
    [self onSendText:textView.text atUsers:@[]];
    [textView layoutIfNeeded];
    textView.text = @"";
    [textView layoutIfNeeded];
    _sessionInputView.toolBar.contentText = @"";
    [_sessionInputView.toolBar layoutIfNeeded];
}

//点击输入框切换表情按钮状态
- (void)tapOverlay
{
    [_sessionInputView refreshStatus:NIMInputStatusText];
}

####表情键盘和普通键盘的切换

NIMInputView

- (void)onTouchEmoticonBtn:(id)sender
{
    if (self.status != NIMInputStatusEmoticon) {
        //切换成表情键盘
        [[MMEmotionCentre defaultCentre] attachEmotionKeyboardToInput:self.toolBar.inputTextView.textView];
        if (!self.toolBar.showsKeyboard)
        {
            self.toolBar.showsKeyboard = YES;
        }
        [self.moreContainer setHidden:YES];
        [self refreshStatus:NIMInputStatusEmoticon];
        [self sizeToFit];
    }
    else
    {
        //切换成系统键盘
        [[MMEmotionCentre defaultCentre] switchToDefaultKeyboard];
        [self refreshStatus:NIMInputStatusText];
        self.toolBar.showsKeyboard = YES;
    }
}

###3. 使用表情消息编辑控件 SDK提供UITextView+BQMM作为表情编辑控件的扩展实现,可以以图文混排方式编辑,并提取编辑内容。 消息编辑框需要使用此控件,在适当位置引入头文件

#import <BQMM/BQMM.h>

###4.消息的编码及发送

表情相关的消息需要编码成extData放入IM的普通文字消息的扩展字段,发送到接收方进行解析。 extData是SDK推荐的用于解析的表情消息发送格式,格式是一个二维数组,内容为拆分完成的textemojiCode,并且说明这段内容是否是一个emojiCode

#####大表情消息

NIMSessionViewController

-(void)sendMMFace:(MMEmoji *)emoji {
    NSDictionary *mmExt = @{@"txt_msgType":@"facetype",
                            @"msg_data":@[@[emoji.emojiCode, [NSString stringWithFormat:@"%d", emoji.isEmoji ? 1 : 2]]]};
    NSString *sendStr = [NSString stringWithFormat:@"[%@]", emoji.emojiName];
    NIMMessage *message = [NIMMessageMaker msgWithText:sendStr];
    message.remoteExt = mmExt;
    
    [self sendMessage:message];
}

#####Gif表情消息

NIMSessionViewController

-(void)didSendGifMessage:(MMGif *)gif {
    NSString *sendStr = [@"[" stringByAppendingFormat:@"%@]", gif.text];
    NSDictionary *msgData = @{WEBSTICKER_URL: gif.mainImage, WEBSTICKER_IS_GIF: (gif.isAnimated ? @"1" : @"0"), WEBSTICKER_ID: gif.imageId,WEBSTICKER_WIDTH: @((float)gif.size.width), WEBSTICKER_HEIGHT: @((float)gif.size.height)};
    NSDictionary *extDic = @{TEXT_MESG_TYPE:TEXT_MESG_WEB_TYPE,
                             TEXT_MESG_DATA:msgData
                             };
    NIMMessage *message = [NIMMessageMaker msgWithText:sendStr];
    message.remoteExt = extDic;
    
    [self sendMessage:message];
}

###5. 表情消息的解析

单个大表情解析

从消息的扩展中解析出大表情(MMEmoji)的emojiCode

NIMSessionEmojiContentView

- (void)refresh:(NIMMessageModel *)data{
    [super refresh:data];
    self.bubbleImageView.hidden = YES;
    //BQMM集成
    self.imageView.image = [UIImage imageNamed:@"mm_emoji_loading"];
    NSDictionary *ext = data.message.remoteExt;
    if ([ext[TEXT_MESG_TYPE] isEqualToString:TEXT_MESG_FACE_TYPE]) {
        NSString *emojiCode = nil;
        if (ext[TEXT_MESG_DATA]) {
            emojiCode = ext[TEXT_MESG_DATA][0][0];
        }
        if (emojiCode != nil && emojiCode.length > 0) {
            self.imageView.errorImage = [UIImage imageNamed:@"mm_emoji_error"];
            self.imageView.image = [UIImage imageNamed:@"mm_emoji_loading"];
            [self.imageView setImageWithEmojiCode:emojiCode];
        }else {
            self.imageView.image = [UIImage imageNamed:@"mm_emoji_error"];
        }
        //BQMM集成
    }
    ...
}

Gif表情解析

从消息的扩展中解析出Gif表情(MMGif)的imageId和mainImage

NIMSessionEmojiContentView

- (void)refresh:(NIMMessageModel *)data{
    ...
    else if([ext[TEXT_MESG_TYPE] isEqualToString:TEXT_MESG_WEB_TYPE]) {
        self.imageView.image = [UIImage imageNamed:@"mm_emoji_loading"];
        self.imageView.errorImage = [UIImage imageNamed:@"mm_emoji_error"];
        
        NSDictionary *msgData = ext[TEXT_MESG_DATA];
        NSString *webStickerUrl = msgData[WEBSTICKER_URL];
        NSString *webStickerId = msgData[WEBSTICKER_ID];
        float height = [msgData[WEBSTICKER_HEIGHT] floatValue];
        float width = [msgData[WEBSTICKER_WIDTH] floatValue];
        
        [self.imageView setImageWithUrl:webStickerUrl gifId:webStickerId];
    }
    
    ...
}

###6. 表情消息显示

大表情消息 && gif表情消息

SDK 提供 MMImageView 来显示单个大表情及gif表情

NTESChatroomEmojiContentView.hNIMSessionEmojiContentView中的UIImageView相应的改成了MMImageView

NIMSessionEmojiContentView

- (void)refresh:(NIMMessageModel *)data{
    [super refresh:data];
    self.bubbleImageView.hidden = YES;
    //BQMM集成
    self.imageView.image = [UIImage imageNamed:@"mm_emoji_loading"];
    NSDictionary *ext = data.message.remoteExt;
    if ([ext[TEXT_MESG_TYPE] isEqualToString:TEXT_MESG_FACE_TYPE]) {
        NSString *emojiCode = nil;
        if (ext[TEXT_MESG_DATA]) {
            emojiCode = ext[TEXT_MESG_DATA][0][0];
        }
        if (emojiCode != nil && emojiCode.length > 0) {
            self.imageView.errorImage = [UIImage imageNamed:@"mm_emoji_error"];
            self.imageView.image = [UIImage imageNamed:@"mm_emoji_loading"];
            [self.imageView setImageWithEmojiCode:emojiCode];
        }else {
            self.imageView.image = [UIImage imageNamed:@"mm_emoji_error"];
        }
        //BQMM集成
    }else if([ext[TEXT_MESG_TYPE] isEqualToString:TEXT_MESG_WEB_TYPE]) {
        self.imageView.image = [UIImage imageNamed:@"mm_emoji_loading"];
        self.imageView.errorImage = [UIImage imageNamed:@"mm_emoji_error"];
        
        NSDictionary *msgData = ext[TEXT_MESG_DATA];
        NSString *webStickerUrl = msgData[WEBSTICKER_URL];
        NSString *webStickerId = msgData[WEBSTICKER_ID];
        float height = [msgData[WEBSTICKER_HEIGHT] floatValue];
        float width = [msgData[WEBSTICKER_WIDTH] floatValue];
        
        [self.imageView setImageWithUrl:webStickerUrl gifId:webStickerId];
    }
    
    self.progressView.hidden     = self.model.message.isOutgoingMsg ? (self.model.message.deliveryState != NIMMessageDeliveryStateDelivering) : (self.model.message.attachmentDownloadState != NIMMessageAttachmentDownloadStateDownloading);
    if (!self.progressView.hidden) {
        [self.progressView setProgress:[[[NIMSDK sharedSDK] chatManager] messageTransportProgress:self.model.message]];
    }
}

布局: NIMSessionEmojiContentView

- (void)layoutSubviews{
    [super layoutSubviews];
    UIEdgeInsets contentInsets = self.model.contentViewInsets;
    CGFloat tableViewWidth = self.superview.nim_width;
    CGSize contentSize         = [self.model contentSize:tableViewWidth];
    CGRect imageViewFrame = CGRectMake(contentInsets.left, contentInsets.top, contentSize.width, contentSize.height);
    self.imageView.frame  = imageViewFrame;
    _progressView.frame   = self.bounds;
    
    CALayer *maskLayer = [CALayer layer];
//    maskLayer.cornerRadius = 13.0;
    maskLayer.backgroundColor = [UIColor blackColor].CGColor;
    maskLayer.frame = self.imageView.bounds;
    self.imageView.layer.mask = maskLayer;
}

NIMTextContentConfig

- (CGSize)contentSize:(CGFloat)cellWidth message:(NIMMessage *)message
{
    //BQMM集成
    NSDictionary *ext = message.remoteExt;
    if ([ext[TEXT_MESG_TYPE] isEqualToString:TEXT_MESG_FACE_TYPE]) {
        return [MMImageView sizeForImageSize:CGSizeMake(120, 120) imgMaxSize:CGSizeMake(120, 120)];
    }else if([ext[TEXT_MESG_TYPE] isEqualToString:TEXT_MESG_WEB_TYPE]) {
        
        NSDictionary *msgData = ext[TEXT_MESG_DATA];
        float height = [msgData[WEBSTICKER_HEIGHT] floatValue];
        float width = [msgData[WEBSTICKER_WIDTH] floatValue];
        //宽最大200 高最大 150
        return [MMImageView sizeForImageSize:CGSizeMake(width, height) imgMaxSize:CGSizeMake(200, 150)];
    }else{
        NSString *text = message.text;
        [self.label nim_setText:text];
        
        CGFloat msgBubbleMaxWidth    = (cellWidth - 130);
        CGFloat bubbleLeftToContent  = 14;
        CGFloat contentRightToBubble = 14;
        CGFloat msgContentMaxWidth = (msgBubbleMaxWidth - contentRightToBubble - bubbleLeftToContent);
        return [self.label sizeThatFits:CGSizeMake(msgContentMaxWidth, CGFLOAT_MAX)];
    }
}

###7. demo中的其他修改 0. 相应的类中引用头文件。

  1. 设置表情键盘的高度 NTESAppDelegate
MMTheme *theme = [[MMTheme alloc] init];
theme.keyboardHeight = 216;
  1. NIMTextContentConfig中的cellContent用于判断消息是普通的文字消息还是表情消息。
- (NSString *)cellContent:(NIMMessage *)message
{
    //BQMM集成
    NSDictionary *ext = message.remoteExt;
    if ([ext[TEXT_MESG_TYPE] isEqualToString:TEXT_MESG_FACE_TYPE] || [ext[TEXT_MESG_TYPE] isEqualToString:TEXT_MESG_WEB_TYPE]) {
        return @"NIMSessionEmojiContentView";
    }else{
        return @"NIMSessionTextContentView";
    }
}

###8. gif搜索模块UI定制

BQMM_GIF是一整套gif搜索UI模块的实现源码,可用于直接使用或用于参考实现gif搜索,及gif消息的发送解析。 ####gif搜索源码说明 gif相关的功能由MMGifManager集中管理:

1.设置搜索模式的开启和关闭;指定输入控件

- (void)setSearchModeEnabled:(BOOL)enabled withInputView:(UIResponder<UITextInput> *_Nullable)input;

2.设置是否显示搜索出的表情内容;指定表情内容的显示位置

- (void)setSearchUiVisible:(BOOL)visible withAttatchedView:(UIView *_Nullable)attachedView;

3.通过MMSearchModeStatus管理搜索模式的开启和关闭及搜索内容的展示和收起(MMSearchModeStatus可自由调整)

typedef NS_OPTIONS (NSInteger, MMSearchModeStatus) {
    MMSearchModeStatusKeyboardHide = 1 << 0,         //收起键盘
    MMSearchModeStatusInputEndEditing = 1 << 1,         //收起键盘
    MMSearchModeStatusInputBecomeEmpty = 1 << 2,     //输入框清空
    MMSearchModeStatusInputTextChange = 1 << 3,      //输入框内容变化
    MMSearchModeStatusGifMessageSent = 1 << 4,       //发送了gif消息
    MMSearchModeStatusShowTrendingTriggered = 1 << 5,//触发流行表情
    MMSearchModeStatusGifsDataReceivedWithResult = 1 << 6,     //收到gif数据
    MMSearchModeStatusGifsDataReceivedWithEmptyResult = 1 << 7,     //搜索结果为空
};
- (void)updateSearchModeAndSearchUIWithStatus:(MMSearchModeStatus)status;

###9. UI定制

SDK通过MMTheme提供一定程度的UI定制。具体参考类说明MMTheme

创建一个MMTheme对象,设置相关属性, 然后[[MMEmotionCentre defaultCentre] setTheme:]即可修改商店和键盘样式。

###10. 清除缓存

调用clearCache方法清除缓存,此操作会删除所有临时的表情缓存,已下载的表情包不会被删除。建议在- (void)applicationWillTerminate:(UIApplication *)application 方法中调用。

###11. 设置APP UserId

开发者可以用setUserId方法设置App UserId,以便在后台统计时跟踪追溯单个用户的表情使用情况。