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

SwiftUI项目安全区机型UITextView inputAccessoryView异常求解

问题根因

异常由两处核心逻辑错误导致:

  • UIHostingController 默认会为根视图自动添加底部安全区内边距,而 inputAccessoryView 的布局本身已经由系统接管安全区适配,两层偏移叠加后就会出现黄色SwiftUI视图超出红框标记的inputAccessoryView自身bounds的问题
  • 直接将UIHostingController的view返回作为inputAccessoryView时,没有将hostingController挂载到当前控制器的响应链中,也没有关闭自动安全区适配,导致交互式滑动关闭键盘时,SwiftUI视图的布局更新无法和系统键盘动画同步,出现动画错位。
修复方案

按以下步骤调整即可解决问题:

  1. 不要直接返回UIHostingController的view作为inputAccessoryView,自定义一个重写了intrinsicContentSize的容器视图作为inputAccessoryView的载体,开启flexibleHeight自动适配
  2. 初始化UIHostingController后关闭自动安全区适配:iOS16+设置ignoresSafeAreaEdges = .all,低版本清空additionalSafeAreaInsets,同时关闭系统最小边距适配
  3. 通过响应链找到输入框所在的父控制器,将UIHostingController添加为该控制器的子控制器,保证布局、动画事件正常传递
  4. 给作为工具栏的SwiftUI视图添加.ignoresSafeArea(),避免内部布局额外产生安全区偏移
  5. 放弃手动计算frame的方式,用Auto Layout将hostingController的view贴紧容器四边,由系统自动计算尺寸
修正后完整代码

首先添加UIView的父控制器查找扩展、自定义inputAccessoryView容器:

extension UIView {
    var parentViewController: UIViewController? {
        var responder: UIResponder? = self
        while let currentResponder = responder {
            if let viewController = currentResponder as? UIViewController {
                return viewController
            }
            responder = currentResponder.next
        }
        return nil
    }
}

class KeyboardAccessoryContainer: UIView {
    override var intrinsicContentSize: CGSize {
        let contentHeight = subviews.first?.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height ?? 0
        return CGSize(width: UIView.noIntrinsicMetric, height: contentHeight)
    }
}

修改UITextViewWrapper实现:

struct UITextViewWrapper: UIViewRepresentable {
    @Binding var text: String
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.text = text
        textView.keyboardDismissMode = .interactive
        textView.alwaysBounceVertical = true
        textView.inputAccessoryView = makeKeyboardBar(for: textView)
        
        textView.delegate = context.coordinator
        textView.becomeFirstResponder()
        
        return textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {}
    
    class Coordinator: NSObject, UITextViewDelegate {
        let parent: UITextViewWrapper
        
        init(_ parent: UITextViewWrapper) {
            self.parent = parent
        }
        
        func textViewDidChange(_ textView: UITextView) {
            parent.text = textView.text
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    private func makeKeyboardBar(for textView: UITextView) -> UIView {
        let container = KeyboardAccessoryContainer()
        container.backgroundColor = .clear
        container.autoresizingMask = .flexibleHeight
        
        let toolbarView = ColorView().ignoresSafeArea()
        let hostingController = UIHostingController(rootView: toolbarView)
        
        // 关闭HostingController自动安全区适配
        hostingController.viewRespectsSystemMinimumLayoutMargins = false
        hostingController.additionalSafeAreaInsets = .zero
        if #available(iOS 16.0, *) {
            hostingController.ignoresSafeAreaEdges = .all
        }
        
        guard let hostView = hostingController.view else { return container }
        hostView.backgroundColor = .clear
        hostView.translatesAutoresizingMaskIntoConstraints = false
        
        // 挂载子控制器保证事件正常传递
        if let parentVC = textView.parentViewController {
            parentVC.addChild(hostingController)
            container.addSubview(hostView)
            NSLayoutConstraint.activate([
                hostView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
                hostView.trailingAnchor.constraint(equalTo: container.trailingAnchor),
                hostView.topAnchor.constraint(equalTo: container.topAnchor),
                hostView.bottomAnchor.constraint(equalTo: container.bottomAnchor)
            ])
            hostingController.didMove(toParent: parentVC)
        }
        
        // 测试边框
        hostView.layer.borderWidth = 1
        hostView.layer.borderColor = UIColor.red.cgColor
        
        return container
    }
}

ColorView和ContentView无需额外修改,调整后黄色工具栏视图会完全贴合inputAccessoryView的范围,交互式下滑关闭键盘时,工具栏动画会和键盘完全同步,不会出现偏移、错位问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 07:51:24