You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apple Business Chat消息扩展:交互消息移至应用区域实现方案

Hey Aaron, let's break down how to implement both of your desired behaviors for your Apple Business Chat Messages Extension, plus fix that tricky TableViewController issue in message bubbles.

实现静态气泡点击展开交互区

This is the most common (and Apple-recommended) pattern for interactive Business Chat experiences, since it keeps the conversation clean until the user chooses to engage. Here's how to set it up:

  • First, create a static message bubble using MSMessageTemplateLayout for the initial display:

    let staticLayout = MSMessageTemplateLayout()
    staticLayout.caption = "Available Options"
    staticLayout.subcaption = "Tap to explore"
    staticLayout.image = UIImage(named: "options-preview") // Optional: add a visual hint
    
    let triggerMessage = MSMessage()
    triggerMessage.templateLayout = staticLayout
    // Use a custom URL scheme to identify this message later
    triggerMessage.url = URL(string: "your-app-scheme://action=showOptions")
    
    // Insert the message into the conversation
    activeConversation?.insert(triggerMessage, completionHandler: { error in
        if let error = error {
            print("Failed to insert trigger message: \(error.localizedDescription)")
        }
    })
    
  • Next, handle the bubble tap in your MessagesViewController:

    override func didSelectMessage(_ message: MSMessage, conversation: MSConversation) {
        guard message.url?.absoluteString.contains("showOptions") == true else { return }
    
        // Load your TableViewController and present it in the extension
        let optionsVC = OptionsTableViewController()
        // If you want to replace the extension's root view instead of presenting:
        // optionsVC.view.frame = self.view.bounds
        // self.addChild(optionsVC)
        // self.view.addSubview(optionsVC.view)
        // optionsVC.didMove(toParent: self)
    
        // Or present it modally
        self.present(optionsVC, animated: true)
    }
    
直接显示交互区(无气泡)

If you don't need a static bubble and want the interactive UI to appear immediately when the user opens your extension:

  • Skip sending a trigger message entirely. Instead, load your TableViewController directly in the viewDidLoad of your MessagesViewController:
    override func viewDidLoad() {
        super.viewDidLoad()
        setupInteractiveTableView()
    }
    
    private func setupInteractiveTableView() {
        let optionsVC = OptionsTableViewController()
        // Pin the table view to the extension's bounds
        optionsVC.view.translatesAutoresizingMaskIntoConstraints = false
        self.addChild(optionsVC)
        self.view.addSubview(optionsVC.view)
        optionsVC.didMove(toParent: self)
    
        NSLayoutConstraint.activate([
            optionsVC.view.topAnchor.constraint(equalTo: self.view.topAnchor),
            optionsVC.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            optionsVC.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            optionsVC.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
        ])
    }
    

This works best for immediate, context-specific interactions where you don't need to leave a trace in the conversation thread.

Fixing the TableViewController in Message Bubbles

The core issue here is that message bubbles have strict size constraints imposed by the Messages app—they're not designed to host interactive components like table views. Here's how to work around this:

  • Avoid embedding TableViews in bubbles entirely: Stick to the first pattern (static bubble + tap to expand) to give your table view enough space in the extension's dedicated UI area.
  • If you must show list content in a bubble: Generate a static image of your options (using a graphics context) and set it as the image property of MSMessageTemplateLayout. This keeps the bubble clean, and users still tap to open the interactive table view.
  • Optimize table view for narrow spaces: If you end up using a table view in a constrained area, tweak its layout for better usability:
    class OptionsTableViewController: UITableViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            tableView.rowHeight = UITableView.automaticDimension
            tableView.estimatedRowHeight = 60 // Helps with dynamic sizing
            tableView.contentInset = UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0) // Add breathing room
            tableView.separatorInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) // Clean up separators
        }
    }
    

内容的提问来源于stack exchange,提问作者Aaron Bratcher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:26:30