如何在UIKit与SwiftUI混合导航中使@FocusState生效
问题场景
项目采用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
相关产品推荐
相关产品推荐

