iOS 環信 EaseUI 的使用一:可傳送類似名片自定義的訊息
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.我們主要實現單聊中的類似於淘寶和商家交談傳送某個商品的情況:正常訊息和自定義訊息的實現:如圖
在我們繼承好的自定義控制中,我們主要分清兩種情況,自己傳送和對方傳送,自己接收和對方接收,這些我們都是可以根據代理實現的.所以我們要設定代理,然後實現代理.程式碼如下
繼承代理<EaseMessageViewControllerDataSource,EaseMessageViewControllerDelegate>
- (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,由於是從專案中抽取出來的,所以有點亂,但是還是能看的,哈哈,
相關文章
- WIN32傳送自定義訊息Win32
- 在python中使用itchat傳送微信訊息Python
- 使用gitlab ci構建IOS包併傳送通知訊息到企業微信GitlabiOS
- 使用 laravel-wechat-notification 傳送微信模板訊息、企業微信應用訊息Laravel
- iOS 訊息傳送與轉發詳解iOS
- 微信小程式 傳送模板訊息的功能實現微信小程式
- RocketMQ中Producer訊息的傳送MQ
- 傳送不同型別的訊息型別
- 使用WxPusher給自己的個人微信傳送提醒訊息(WxPusher微信推送服務)
- 0x2_訊息的傳送
- 傳送kafka訊息的shell指令碼Kafka指令碼
- 微信公眾號如何實現模板訊息傳送的功能
- RocketMQ(八):訊息傳送MQ
- 【RocketMQ】MQ訊息傳送MQ
- 訊息中介軟體—RocketMQ訊息傳送MQ
- 【玩具】使用Python自動化傳送微信訊息進行訂水Python
- 一張圖進階 RocketMQ - 訊息傳送MQ
- 企業微信hook,自定義工具,收發訊息Hook
- 傳送微信公眾號模板訊息(未完成)
- TNW-傳送模板訊息TNW
- RocketMQ(九):訊息傳送(續)MQ
- 鴻蒙傳送訊息通知鴻蒙
- 6-RocketMQ傳送訊息MQ
- 使用Azure Runbook 傳送訊息到Azure Storage Queue
- 用程式碼理解 ObjC 中的傳送訊息和訊息轉發OBJ
- 使用Java客戶端傳送訊息和消費的應用Java客戶端
- ros|自定義訊息型別ROS型別
- 微信程式開發系列教程(二)使用JavaScript給微信使用者傳送訊息JavaScript
- qt傳送自定義signal,直接呼叫也可以,不使用emitQTMIT
- laravel中使用利用訊息佇列傳送郵件Laravel佇列
- gnome-shell 使用 notify-send 傳送桌面訊息
- Runtime備忘-訊息傳送流程
- Python呼叫飛書傳送訊息Python
- 小程式傳送訂閱訊息
- Kafka -- 訊息傳送儲存流程Kafka
- 快速向 Google Chat 傳送訊息Go
- RocketMQ -- 訊息傳送儲存流程MQ
- 排查MQ訊息傳送和接收MQ