tencent cloud

即时通信 IM

动态与公告
产品动态
公告
产品简介
产品概述
基本概念
应用场景
功能介绍
账号系统
用户资料与关系链
消息管理
群组相关
公众号系统
音视频通话 Call
使用限制
购买指南
计费概述
价格说明
购买指引
续费指引
停服说明
退费说明
开发指引
Demo 专区
开通服务
体验 Demo
快速跑通
下载中心
SDK & Demo 源码
更新日志
聊天互动(含 UI)
TUIKit 组件介绍
快速开始
全功能接入
单功能接入
AI 集成
构建基础界面
更多特性
定义外观
国际化界面语言
推送服务(Push)
服务概述
名词解释
开通服务
快速跑通
厂商通道
数据统计
排查工具
客户端 API
服务端 API
推送回调
高级功能
更新日志
错误码
常见问题
智能客服
功能概述
快速入门
集成指引
管理员操作手册
客服操作手册
更多实践
直播间搭建
AI 聊天机器人方案
超大娱乐协作社群
Discord 实现指南
游戏内集成 Chat 指南
类 WhatsApp Channel 搭建方案
发送红包
Chat 应对防火墙限制相关
无 UI 集成
快速开始
集成 SDK
初始化
登录登出
消息相关
会话相关
群组相关
社群话题
用户管理
离线推送
云端搜索
本地搜索
公众号
客户端 API
JavaScript
Android
iOS & macOS
Swift
Flutter
Electron
Unity
React Native
C 接口
C++
服务端 API
生成 UserSig
REST API
第三方回调
控制台指南
新版控制台介绍
创建并升级应用
基本配置
功能配置
账号管理
群组管理
公众号管理
回调配置
用量统计
资源包查看指南
实时监控
开发辅助工具
访问管理
高级功能
常见问题
uni-app 常见问题
购买相关问题
SDK 相关问题
账号鉴权相关问题
用户资料与关系链相关问题
消息相关问题
群组相关问题
直播群相关问题
昵称头像相关问题
协议与认证
服务等级协议
安全合规认证
IM 政策
隐私政策
数据隐私和安全协议
平滑迁移方案
平滑迁移完整版
平滑迁移简化版
错误码
联系我们

iOS

PDF
聚焦模式
字号
最后更新时间: 2026-03-30 15:34:34
TUIKit 默认实现了文本、图片、语音、视频、文件等基本消息类型的发送和展示,如果这些消息类型满足不了您的需求,您可以新增自定义消息类型。

基本消息类型

消息类型
显示效果图
文本类消息

图片类消息

语音类消息

视频类消息

文件类消息


自定义消息

如果基本消息类型不能满足您的需求,您可以根据实际业务需求自定义消息。下文以发送一条可跳转至浏览器的超文本作为自定义消息为例,帮助您快速了解实现流程。
TUIKit 内置的自定义消息样式如下图所示:

注意:
TUIKit 在 7.4.4643 版本重新设计了一套自定义消息注册机制,新旧方案变动较大,但可以支持不同的 UI 样式,建议您升级到 7.4.4643 版本。本文将以 7.4.4643 版本为例讲解。

展示自定义消息

您可以在 TUIMessageBaseDataProvideronRecvNewMessage 函数内接收自定义消息。 收到的自定义消息最终会以 Cell 的形式展示在消息列表中,Cell 绘制所需的数据我们称之为 CellData
下面我们分步骤讲解下如何展示自定义消息。

创建自定义 CellData

1. TUIChat/BaseCellData/Custom 文件夹下新建 TUILinkCellData,继承自TUIMessageCellData ,用于存储显示的文字和跳转的链接。示例代码如下:
Swift
Objective-C
public class TUILinkCellData: TUIBubbleMessageCellData {
var text: String = ""
var link: String = ""

// ...
}
@interface TUILinkCellData : TUIMessageCellData

@property NSString *text;
@property NSString *link;

@end
2. 重写父类的 getCellData: 方法。用于把 V2TIMMessage 转换成消息列表 Cell 的绘制数据 TUILinkCellData。示例代码如下:
Swift
Objective-C
public class TUILinkCellData: TUIBubbleMessageCellData {
override public class func getCellData(message: V2TIMMessage) -> TUIMessageCellData {
guard let data = message.customElem?.data,
let param = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any]
else {
return TUILinkCellData(direction: .incoming)
}
let cellData = TUILinkCellData(direction: message.isSelf ? .outgoing : .incoming)
cellData.msgID = message.msgID
cellData.text = param["text"] as? String ?? ""
cellData.link = param["link"] as? String ?? ""
cellData.avatarUrl = URL(string: message.faceURL ?? "")
return cellData
}
}
@implementation TUILinkCellData
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
TUILinkCellData *cellData = [[TUILinkCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
cellData.innerMessage = message;
cellData.msgID = message.msgID;
cellData.text = param[@"text"];
cellData.link = param[@"link"];
cellData.avatarUrl = [NSURL URLWithString:message.faceURL];
return cellData;
}
@end
3. 重写父类的 getDisplayString: 方法。用于把 V2TIMMessage 转换成会话列表 lastMsg 的展示文本信息。 会话列表 lastMsg 展示文本指的是当用户停留在会话列表,每个会话 cell 会显示当前会话最后一条消息。如下图所示:

示例代码如下:
Swift
Objective-C
public class TUILinkCellData: TUIBubbleMessageCellData {
override public class func getDisplayString(message: V2TIMMessage) -> String {
guard let data = message.customElem?.data,
let param = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any]
else {
return ""
}
return param["text"] as? String ?? ""
}
}
@implementation TUILinkCellData

+ (NSString *)getDisplayString:(V2TIMMessage *)message {
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
return param[@"text"];
}

@end

创建自定义 Cell

1. TUIChat/UI_Minimalist/Cell/Custom 文件夹下新建 TUILinkCell_Minimalist 文件,继承自 TUIBubbleMessageCell_Minimalist ,用于绘制 TUILinkCellData 数据。
示例代码如下:
Swift
Objective-C
class TUILinkCell_Minimalist: TUIBubbleMessageCell_Minimalist {
var myTextLabel: UILabel!
var myLinkLabel: UILabel!
var customData: TUILinkCellData?
override func fill(with data: TUICommonCellData) {
super.fill(with: data)
if let data = data as? TUILinkCellData {
customData = data
myTextLabel.text = data.text

setNeedsUpdateConstraints()
updateConstraintsIfNeeded()
layoutIfNeeded()
}
}
}
@interface TUILinkCell_Minimalist : TUIBubbleMessageCell_Minimalist
@property UILabel *myTextLabel; // Display text
@property UILabel *myLinkLabel; // Link redirection text
- (void)fillWithData:(TUILinkCellData *)data; // Draw UI
@end
2. 重写父类 initWithStyle:reuseIdentifier: 方法,创建 myTextLabelmyLinkLabel 文本展示对象,并添加至 container 容器。
示例代码如下:
Swift
Objective-C
class TUILinkCell_Minimalist: TUIBubbleMessageCell {
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}

private func setupViews() {
myTextLabel = UILabel()
myTextLabel.numberOfLines = 0
myTextLabel.font = UIFont.systemFont(ofSize: 15)
myTextLabel.textAlignment = TUISwift.isRTL() ? .right : .left
myTextLabel.textColor = TUISwift.tuiChatDynamicColor("chat_link_message_title_color", defaultColor: "#000000")
container.addSubview(myTextLabel)

myLinkLabel = UILabel()
myLinkLabel.text = TUISwift.timCommonLocalizableString("TUIKitMoreLinkDetails")
myLinkLabel.font = UIFont.systemFont(ofSize: 15)
myLinkLabel.textAlignment = TUISwift.isRTL() ? .right : .left
myLinkLabel.textColor = TUISwift.tuiChatDynamicColor("chat_link_message_subtitle_color", defaultColor: "#0000FF")
container.addSubview(myLinkLabel)
}
}
@implementation TUILinkCell_Minimalist
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.myTextLabel = [[UILabel alloc] init];
[self.container addSubview:self.myTextLabel];
self.myLinkLabel = [[UILabel alloc] init];
self.myLinkLabel.text = @"View details >>";
[self.container addSubview:_myLinkLabel];
}
return self;
}
@end
3. 重写父类 fillWithData: 方法,在 TUILinkCell_Minimalist 中自定义展示 TUILinkCellData 数据。
示例代码如下:
Swift
Objective-C
class TUILinkCell_Minimalist: TUIBubbleMessageCell_Minimalist {
override func fill(with data: TUICommonCellData) {
super.fill(with: data)
if let data = data as? TUILinkCellData {
customData = data
myTextLabel.text = data.text

setNeedsUpdateConstraints()
updateConstraintsIfNeeded()
layoutIfNeeded()
}
}
}
@implementation TUILinkCell_Minimalist
// Draw the cell based on cellData
- (void)fillWithData:(TUILinkCellData *)data;
{
[super fillWithData:data];
self.myTextLabel.text = data.text;
}
@end
4. 重写父类 layoutSubviews 方法,自定义控件的布局。
示例代码如下:
Swift
Objective-C
override func layoutSubviews() {
super.layoutSubviews()
// Custimization
}
// Set the control coordinates
- (void)layoutSubviews
{
[super layoutSubviews];
self.myTextLabel.mm_top(10).mm_left(10).mm_flexToRight(10).mm_flexToBottom(50);
self.myLinkLabel.mm_sizeToFit().mm_left(10).mm_bottom(10);
}
@end
5. 重写父类的 getContentSize: 方法,用于计算 cellData 内容所占绘制区域的大小。
示例代码如下:
Swift
Objective-C
override class func getContentSize(_ data: TUIMessageCellData) -> CGSize {
guard let linkCellData = data as? TUILinkCellData else {
assertionFailure("data must be kind of TUILinkCellData")
return .zero
}

let textMaxWidth = 245
let font = UIFont.systemFont(ofSize: 15)
let attributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: font]
var size = CGSize(width: textMaxWidth, height: Int.max)

let rect = linkCellData.text.boundingRect(with: size,
options: [.usesLineFragmentOrigin, .usesFontLeading],
attributes: attributes,
context: nil)
size = CGSize(width: textMaxWidth + 15, height: Int(rect.size.height) + 56)
return size
}
+ (CGSize)getContentSize:(TUIMessageCellData *)data {
NSAssert([data isKindOfClass:TUILinkCellData.class], @"data must be kind of TUILinkCellData");
TUILinkCellData *linkCellData = (TUILinkCellData *)data;
CGFloat textMaxWidth = 245.f;
CGRect rect = [linkCellData.text boundingRectWithSize:CGSizeMake(textMaxWidth, MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:15]}
context:nil];
CGSize size = CGSizeMake(textMaxWidth + 15, rect.size.height + 56);
return size;
}

将您的自定义 Cell 和 CellData 注册进 TUIChat

注意:
每一种自定义消息都必须有唯一的 businessID,区分大小写,不可跟其他自定义消息的 businessID 重复。TUIChat 需要根据此 businessID 找到对应的自定义消息。
新增自定义消息的 businessID 也不能和 TUIKit 内置自定义消息的 businessID 重复。
方式一:当您采用 DevelopPods 源码集成时,并想要在组件内部直接修改需求,您可以在 TUIChat 组件内部按照以下操作直接进行修改。
在 TUIMessageCellConfig_Minimalist.m 的 registerExternalCustomMessageInfo 中注册您自己的自定义 Cell:
Swift
Objective-C
public class TUIMessageCellConfig_Minimalist: NSObject {
// ...
static func registerExternalCustomMessageInfo() {
/*
Insert your own custom message UI here, your businessID can not be same with built-in
Example:
registerCustomMessageCell(#your message cell#, messageCellData: #your message cell data#, forBusinessID: #your id#)
*/
}
}
@implementation TUIMessageCellConfig_Minimalist (CustomMessageRegister)
+ (void)registerExternalCustomMessageInfo {
// Insert your own custom message UI here, your businessID can not be same with built-in //
// Example:
[self registerCustomMessageCell:@"TUILinkCell_Minimalist" messageCellData:@"TUILinkCellData" forBusinessID:BussinessID_TextLink];
}

方式二:通过 Pod 集成 TUIChat。
在 App 初始化时,您也可以通过 TUIChatConfigregisterCustomMessage 函数里主动注册 cellcellData 信息。
示例代码如下:
Swift
Objective-C
// 自定义消息 businessID 不能重复
TUIChatConfig_Minimalist.shared.registerCustomMessage(businessID: "text_link", messageCellClassName: "TUIChat.TUILinkCell", messageCellDataClassName: "TUIChat.TUILinkCellData")

// 自定义消息 businessID(注意不能重复)
#define BussinessID_TextLink @"text_link"

/** 向 TUIChat 注册自定义消息。三个参数分别为
* @param businessID 自定义消息 businessID
* @param messagellClass 自定义消息 NSString 类型
* @param messageCellDataClassName 自定义消息 NSString 类型
*/
- (void)registerCustomMessageCell {
[TUIChatConfig.defaultConfig registerCustomMessage:BussinessID_TextLink
messageCellClassName:@"TUILinkCell"
messageCellDataClassName:@"TUILinkCellData"
];
}

发送自定义消息

如下图所示,自定义消息发送按钮主要由文本title和图片leftMark组成。您可以通过在 TUIChatDataProvidercustomInputMoreActionItemList 属性中新增 TUICustomActionSheetItem 对象来添加自定义按钮。
您可以通过设置TUICustomActionSheetItemtitleleftMark属性来自定义您想展示的文字和图片信息;如果您想调整按钮的展示顺序,可以设置 priority属性,其中priority值越大按钮越靠前;您也可以设置actionHandler来响应该按钮的点击事件,实现自己的业务逻辑。

示例代码如下:
Swift
Objective-C
class TUIChatDataProvider: TUIChatBaseDataProvider {
// ...

private func createCustomInputMoreActionItemList(model: TUIChatConversationModel) -> [TUICustomActionSheetItem] {
if self.customInputMoreActionItemList.isEmpty {
var arrayM: [TUICustomActionSheetItem] = []

let showCustom = TUIChatConfig.shared.enableWelcomeCustomMessage && model.enableWelcomeCustomMessage
if showCustom {
let link = TUICustomActionSheetItem(title: TUISwift.timCommonLocalizableString("TUIKitMoreLink"), leftMark: UIImage.safeImage(TUISwift.tuiChatImagePath_Minimalist("icon_more_custom"))) { [weak self] _ in
guard let self else { return }
let text = TUISwift.timCommonLocalizableString("TUIKitWelcome")
var homePageLink = TUITencentCloudHomePageEN
let language = TUIGlobalization.getPreferredLanguage() ?? ""
if language.contains("zh-") {
homePageLink = TUITencentCloudHomePageCN
}
do {
let param: [String: Any] = ["businessID": "text_link", "text": text, "link": homePageLink]
let data = try JSONSerialization.data(withJSONObject: param, options: [])
let message = TUIMessageDataProvider.getCustomMessageWithJsonData(data, desc: text, extensionInfo: text)
self.delegate?.dataProvider(self, sendMessage: message)
} catch {
print("[\\(self)] Post Json Error")
}
}
link.priority = 100
arrayM.append(link)
}

if let items = model.customizedNewItemsInMoreMenu as? [TUICustomActionSheetItem], items.count > 0 {
arrayM.append(contentsOf: items)
}
self.customInputMoreActionItemList = arrayM
}
return self.customInputMoreActionItemList
}

// For Minimalist Edition.
func getInputMoreActionItemList(userID: String, groupID: String, conversationModel: TUIChatConversationModel, pushVC: UINavigationController?, actionController: TIMInputViewMoreActionProtocol) -> [TUICustomActionSheetItem] {
var result: [TUICustomActionSheetItem] = []
result.append(contentsOf: self.createBuiltInInputMoreActionItemList(model: conversationModel))
result.append(contentsOf: self.createCustomInputMoreActionItemList(model: conversationModel))

// ...
}
}
@implementation TUIChatDataProvider
- (NSArray<TUICustomActionSheetItem *> *)customInputMoreActionItemList {
if (_customInputMoreActionItemList == nil) {
NSMutableArray *arrayM = [NSMutableArray array];
if (TUIChatConfig.defaultConfig.enableWelcomeCustomMessage) {
__weak typeof(self) weakSelf = self;
TUICustomActionSheetItem *link =
[[TUICustomActionSheetItem alloc] initWithTitle:TIMCommonLocalizableString(TUIKitMoreLink)
leftMark:[UIImage imageNamed:TUIChatImagePath_Minimalist(@"icon_more_custom")]
withActionHandler:^(UIAlertAction *_Nonnull action) {
link.priority = 100;
NSString *text = TIMCommonLocalizableString(TUIKitWelcome);
NSString *link = TUITencentCloudHomePageEN;
NSString *language = [TUIGlobalization tk_localizableLanguageKey];
if ([language containsString:@"zh-"]) {
link = TUITencentCloudHomePageCN;
}
NSError *error = nil;
NSDictionary *param = @{BussinessID : BussinessID_TextLink, @"text" : text, @"link" : link};
NSData *data = [NSJSONSerialization dataWithJSONObject:param options:0 error:&error];
if (error) {
NSLog(@"[%@] Post Json Error", [self class]);
return;
}
V2TIMMessage *message = [TUIMessageDataProvider getCustomMessageWithJsonData:data desc:text extension:text];
if ([weakSelf.delegate respondsToSelector:@selector(dataProvider:sendMessage:)]) {
[weakSelf.delegate dataProvider:weakSelf sendMessage:message];
}
}];
[arrayM addObject:link];
}
_customInputMoreActionItemList = [NSArray arrayWithArray:arrayM];
}
return _customInputMoreActionItemList;
}
@end


帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈