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

如何在UIKit与SwiftUI混合导航中使@FocusState生效

SwiftUI TextField @FocusState在UIHostingController嵌套UINavigationController时失效的解决方法

问题场景

项目采用SwiftUI作为视图层、UIKit负责导航,当UIHostingController被UINavigationController管理时,TextField的@FocusState属性包装器完全无法响应(既不能捕获焦点变化,也无法程序化设置焦点),但纯SwiftUI项目中该功能正常。曾尝试用Button包裹TextField模拟聚焦效果,但无法实现程序化触发聚焦。

解决方案

方法1:通过UIViewRepresentable桥接UITextField,手动管理焦点

直接利用UIKit的第一响应者机制,结合SwiftUI状态同步,绕过SwiftUI焦点系统在UIKit导航下的冲突:

struct FocusableTextField: UIViewRepresentable {
    @Binding var text: String
    @Binding var isFocused: Bool
    let placeholder: String
    
    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField()
        textField.placeholder = placeholder
        textField.borderStyle = .none
        // 同步文本变化到SwiftUI
        textField.addTarget(context.coordinator, action: #selector(Coordinator.textChanged), for: .editingChanged)
        // 同步焦点状态到SwiftUI
        textField.delegate = context.coordinator
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
        // 根据SwiftUI状态控制第一响应者
        if isFocused != uiView.isFirstResponder {
            isFocused ? uiView.becomeFirstResponder() : uiView.resignFirstResponder()
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        let parent: FocusableTextField
        
        init(_ parent: FocusableTextField) {
            self.parent = parent
        }
        
        @objc func textChanged(_ textField: UITextField) {
            parent.text = textField.text ?? ""
        }
        
        func textFieldDidBeginEditing(_ textField: UITextField) {
            parent.isFocused = true
        }
        
        func textFieldDidEndEditing(_ textField: UITextField) {
            parent.isFocused = false
        }
    }
}

在SwiftUI视图中使用该包装器:

@State private var isFocused: Bool = false

@ViewBuilder
func phoneTextField() -> some View {
    FocusableTextField(
        text: $viewModel.phoneTextfieldNumber,
        isFocused: $isFocused,
        placeholder: "Введите номер"
    )
    .frame(minWidth: 0, maxWidth: .infinity)
    .background(isFocused ? .clear : Resources.Colors.backgroundGrey100.swiftUIColor)
    .cornerRadius(12)
    .overlay(
        RoundedRectangle(cornerRadius: 12)
            .stroke(Resources.Colors.buttonRed100.swiftUIColor, lineWidth: isFocused ? 2 : 0)
    )
    .animation(.default, value: isFocused)
    .onChange(of: isFocused) { newValue in
        print("NEWFOCUS", newValue)
    }
}

方法2:禁用UIHostingController的自动滚动视图调整(可选尝试)

部分场景下,UINavigationController的滚动视图Insets调整会干扰SwiftUI焦点系统,可在初始化UIHostingController时关闭该特性:

let swiftUIView = YourSwiftUIView()
let hostingController = UIHostingController(rootView: swiftUIView)
hostingController.automaticallyAdjustsScrollViewInsets = false
navigationController.pushViewController(hostingController, animated: true)

方法3:迁移到SwiftUI原生NavigationStack(长期方案)

如果项目允许,替换UIKit的UINavigationController为SwiftUI的NavigationStack,可彻底避免跨框架的响应链冲突,让@FocusState恢复正常工作。

问题原因

当UIHostingController嵌套在UINavigationController中时,UIKit的响应链优先级高于SwiftUI的焦点系统,导致@FocusState无法正确绑定TextField的焦点状态。通过UIViewRepresentable直接操作UITextField的第一响应者,可绕开这一冲突,实现状态同步与程序化聚焦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 03:33:25