帮你快速理解、总结文档立即下载

iOS

最近更新时间:2026-10-28 16:12:47
本文档已由 AI 辅助审校
我的收藏
TUIChatKit 默认实现了文本、图片、语音、视频、文件等基本消息类型的发送和展示,如果这些消息类型满足不了您的需求,您可以新增自定义消息类型。

自定义消息

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


展示自定义消息

在 TUIChatKit 中,您只需要实现一个遵循 MessageContentView 协议的气泡内容视图,并在 App 初始化时通过 MessageListView.registerCustomMessageCell 注册即可,聊天页 ChatPage 无需额外配置。
自定义消息的内容以 String 格式存储在 CustomMessagePayload.customData 中,建议使用 JSON 格式,并通过 businessID 字段来唯一标识这条消息类型。

定义自定义消息数据结构

定义一个结构体,用于描述自定义消息解析后的结构,并提供从 JSON String 解析的方法。以上述包含链接及文本的自定义消息格式举例:
struct CustomLinkMessage {
static let businessID = "text_link"

let text: String?
let link: String?

static func from(customData: String?) -> CustomLinkMessage? {
guard let customData = customData, !customData.isEmpty,
let data = customData.data(using: .utf8),
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
return nil
}
return CustomLinkMessage(text: dict["text"] as? String, link: dict["link"] as? String)
}

static func from(message: MessageInfo) -> CustomLinkMessage? {
guard case .custom(let payload) = message.messagePayload else { return nil }
return from(customData: payload.customData)
}
}

实现自定义消息渲染器

实现一个遵循 MessageContentView 协议的 UIView,在 bind(message:context:) 中解析 CustomLinkMessage 并把文字和链接渲染到视图上,同时为气泡添加点击跳转事件。bind 的 MessageContentContext 提供了 isSelf(是否自己发送)、isMultiSelectMode(是否多选模式)等上下文,主题颜色可通过 TUIChatKitTheme.colors 获取。
示例代码如下:
import AtomicXCore
import SnapKit
import TUIChatKit
import UIKit

final class CustomLinkMessageContentView: UIView, MessageContentView {
private static let maxBubbleWidth: CGFloat = 245
private static let horizontalPadding: CGFloat = 16
private static let verticalPadding: CGFloat = 12
private static let linkTopSpacing: CGFloat = 8

private let textLabel = UILabel()
private let linkLabel = UILabel()
private var linkURL: URL?

init() {
super.init(frame: .zero)
addSubview(textLabel)
addSubview(linkLabel)

textLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(Self.verticalPadding)
make.leading.equalToSuperview().offset(Self.horizontalPadding)
make.trailing.lessThanOrEqualToSuperview().offset(-Self.horizontalPadding)
make.width.lessThanOrEqualTo(Self.maxBubbleWidth - Self.horizontalPadding * 2)
}
linkLabel.snp.makeConstraints { make in
make.top.equalTo(textLabel.snp.bottom).offset(Self.linkTopSpacing)
make.leading.equalToSuperview().offset(Self.horizontalPadding)
make.trailing.lessThanOrEqualToSuperview().offset(-Self.horizontalPadding)
make.bottom.equalToSuperview().offset(-Self.verticalPadding)
}

textLabel.numberOfLines = 0
textLabel.font = .systemFont(ofSize: 16)
textLabel.preferredMaxLayoutWidth = Self.maxBubbleWidth - Self.horizontalPadding * 2
linkLabel.numberOfLines = 1
linkLabel.font = .systemFont(ofSize: 14)

addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func bind(message: MessageInfo, context: MessageContentContext) {
let linkMessage = CustomLinkMessage.from(message: message)
let colors = TUIChatKitTheme.colors

textLabel.text = linkMessage?.text
textLabel.textColor = message.isSentBySelf ? colors.textColorAntiPrimary : colors.textColorPrimary

let link = linkMessage?.link?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
linkURL = URL(string: link)
linkLabel.isHidden = link.isEmpty
linkLabel.text = "查看详情"
linkLabel.textColor = colors.textColorLink

isUserInteractionEnabled = !link.isEmpty && !context.isMultiSelectMode
}

@objc private func handleTap() {
guard let url = linkURL else { return }
UIApplication.shared.open(url)
}
}

注册渲染器并自定义会话摘要

在 App 初始化时调用一次 MessageListView.registerCustomMessageCell 完成注册。其中 businessID 必须与消息 customData 中的 businessID 字段一致,TUIChatKit 会据此找到对应的渲染器;summaryProvider 用于生成该消息的文字摘要,会同时应用到会话列表的"最后一条消息"预览和消息列表内引用回复的预览。
示例代码如下:
import AtomicXCore
import TUIChatKit

// App 启动时调用一次即可,例如在 AppDelegate 的 didFinishLaunchingWithOptions 中
MessageListView.registerCustomMessageCell(
businessID: CustomLinkMessage.businessID,
summaryProvider: { payload in
CustomLinkMessage.from(customData: payload.customData)?.text
},
makeContentView: {
CustomLinkMessageContentView()
}
)
实现后的会话列表预览效果如下:


发送自定义消息

自定义消息发送逻辑

把要发送的内容转成 JSON String 放入 CustomSendMessagePayload 的 customData 中,再调用 MessageInputStore 的 sendMessage 接口发送。核心代码如下:
import AtomicXCore

let payload: [String: Any] = [
"businessID": "text_link",
"text": "欢迎加入云通信IM大家庭!",
"link": "https://cloud.tencent.com/document/product/269/3794"
]
if let data = try? JSONSerialization.data(withJSONObject: payload),
let json = String(data: data, encoding: .utf8) {
let customPayload = CustomSendMessagePayload(customData: json, description: "欢迎加入云通信IM大家庭!")
MessageInputStore.create(conversationID: conversationID)
.sendMessage(payload: .custom(customPayload), option: nil, completion: nil)
}
注意:
自定义消息内容必须为 JSON 格式。其中 "businessID" 字段为必填项。可根据业务需求添加其他字段,单条消息大小上限为 12KB。例如:
{
"businessID":"text_link",
"link":"https://cloud.tencent.com/document/product/269/3794",
"text":"欢迎加入云通信IM大家庭!"
}
每一种自定义消息都必须有唯一的 businessID(区分大小写),不可与其他自定义消息重复,也不能和 TUIChatKit 内置自定义消息的 businessID 重复。

在输入框的“更多”面板中增加一个自定义消息发送按钮

您可以在输入框的"更多"面板中增加一个自定义消息发送按钮,点击时创建并发送上述自定义消息。如下图所示:

通过 ChatMessageInputConfig 的 actionCustomizer 追加一个自定义功能项,并把配置传入 ChatPage 即可。代码如下:
import AtomicXCore
import TUIChatKit
import UIKit

let inputConfig = ChatMessageInputConfig(actionCustomizer: { editor in
editor.add(MessageInputMenuAction(
ID: "customLink",
title: "自定义消息",
iconName: "",
icon: UIImage(systemName: "paperplane.fill"),
onClick: {
// 组装并发送自定义消息(代码同上)
let payload: [String: Any] = [
"businessID": "text_link",
"text": "欢迎加入云通信IM大家庭!",
"link": "https://cloud.tencent.com/document/product/269/3794"
]
guard let data = try? JSONSerialization.data(withJSONObject: payload),
let json = String(data: data, encoding: .utf8) else { return }
let customPayload = CustomSendMessagePayload(customData: json, description: "欢迎加入云通信IM大家庭!")
MessageInputStore.create(conversationID: conversationID)
.sendMessage(payload: .custom(customPayload), option: nil, completion: nil)
}
))
})

let chatPage = ChatPage(conversation: conversation, messageInputConfig: inputConfig)
navigationController.pushViewController(chatPage, animated: true)
MessageInputMenuAction 的 icon 支持直接传入 UIImage(例如 SF Symbols 图标),也可以通过 iconName 指定资源包中的图片名;ID 为该功能项的唯一标识,不可与内置功能项重复。