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

SwiftUI如何实现类似UIKit的全局InputAccessoryView让输入区跟随键盘移动

SwiftUI 全局 inputAccessoryView 实现方案

SwiftUI 原生未直接提供全局 inputAccessoryView 的公开API,你可以通过桥接 UIKit 根视图控制器的方式实现和之前 UIKit 开发完全一致的效果,满足输入视图全局可见、跟随键盘拖拽同步移动的需求,不需要绑定到特定TextField。

实现步骤

  • 第一步:创建重写了inputAccessoryView的自定义UIViewController子类
class InputAccessoryHostingController<Content: View>: UIViewController {
    private let accessoryView = UIView()
    private let rootView: Content
    
    init(rootView: Content) {
        self.rootView = rootView
        super.init(nibName: nil, bundle: nil)
        setupAccessoryView()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override var canBecomeFirstResponder: Bool {
        true
    }
    
    override var inputAccessoryView: UIView? {
        accessoryView
    }
    
    private func setupAccessoryView() {
        // 把SwiftUI自定义输入视图嵌入到accessoryView中
        let hostingController = UIHostingController(rootView: rootView)
        addChild(hostingController)
        accessoryView.addSubview(hostingController.view)
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            hostingController.view.topAnchor.constraint(equalTo: accessoryView.topAnchor),
            hostingController.view.leadingAnchor.constraint(equalTo: accessoryView.leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: accessoryView.trailingAnchor),
            hostingController.view.bottomAnchor.constraint(equalTo: accessoryView.bottomAnchor)
        ])
        hostingController.didMove(toParent: self)
        // 自动计算accessoryView的高度
        accessoryView.autoresizingMask = .flexibleHeight
    }
}
  • 第二步:用UIViewControllerRepresentable桥接为SwiftUI视图
struct InputAccessoryHost<Content: View>: UIViewControllerRepresentable {
    let content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    func makeUIViewController(context: Context) -> InputAccessoryHostingController<Content> {
        let vc = InputAccessoryHostingController(rootView: content)
        // 页面加载后立刻成为第一响应者,保证inputAccessory全局生效
        DispatchQueue.main.async {
            vc.becomeFirstResponder()
        }
        return vc
    }
    
    func updateUIViewController(_ uiViewController: InputAccessoryHostingController<Content>, context: Context) {}
}
  • 第三步:在你的根SwiftUI视图中引入该组件,放在background层级即可,不会影响原有视图布局
struct ChatRootView: View {
    var body: some View {
        NavigationStack {
            // 你的聊天列表、输入框等原有内容
            List {
                // 聊天消息cell
            }
        }
        .background {
            // 全局inputAccessory,传入你自定义的输入视图
            InputAccessoryHost {
                CustomChatInputView() // 你自己实现的SwiftUI输入视图
            }
            .ignoresSafeArea()
        }
    }
}

效果说明

该实现完全复用UIKit系统的inputAccessory逻辑,不需要手动监听键盘通知做位移动画,输入视图会跟随键盘的交互式拖拽动作实时同步移动,且始终悬浮在键盘顶部,全局生效不绑定任何特定TextField。如果需要在输入框激活时做状态同步,你可以直接在CustomChatInputView中使用@FocusState管理输入框的焦点状态即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 05:03:00