SwiftUI中.toolbar与UITextView兼容问题:如何让键盘工具栏生效?
问题
使用SwiftUI的TextField时,通过.toolbar设置的键盘工具栏可正常显示,但使用基于UIViewRepresentable实现的自定义UITextView时,工具栏无法显示。需要解决如何让.toolbar()在自定义UITextView中生效的问题,原代码如下:
struct MyUITextView: UIViewRepresentable { @Binding var text: String private let editor = UITextView() func makeUIView(context: Context) -> UITextView { editor.delegate = context.coordinator return editor } func makeCoordinator() -> Coordinator { Coordinator(text: $text) } func updateUIView(_ editor: UITextView, context: Context) { editor.text = text } class Coordinator: NSObject, UITextViewDelegate { @Binding private var text: String init(text: Binding<String>) { self._text = text } func textViewDidChange(_ editor: UITextView) { text = editor.text } } } struct ContentView: View { @State var uitextView = "UITextView" @State var textfield = "TextField" var body: some View { VStack { MyUITextView(text: $uitextView) // 聚焦时工具栏不显示 .border(.black, width: 1) .frame(maxHeight: 40) TextField("", text: $textfield) // 聚焦时工具栏正常显示 .border(.black, width: 1) } .toolbar { ToolbarItem(placement: .keyboard) { Button("Click") {} } } } }
解决方案
SwiftUI的.toolbar(placement: .keyboard)仅会响应原生SwiftUI输入控件的焦点状态,自定义UIViewRepresentable封装的UITextView不会自动触发这个关联,需要手动将UITextView的编辑状态与SwiftUI的焦点状态绑定,具体步骤如下:
- 给自定义
MyUITextView添加@Binding var isFocused: Bool属性,用于同步焦点状态; - 在
Coordinator中实现textViewDidBeginEditing和textViewDidEndEditing代理方法,更新isFocused的值; - 在
ContentView中创建@FocusState private var isUITextViewFocused: Bool变量,将其与MyUITextView的isFocused绑定,并添加.focused($isUITextViewFocused)修饰符。
修正后的完整代码:
struct MyUITextView: UIViewRepresentable { @Binding var text: String @Binding var isFocused: Bool // 新增焦点绑定 func makeUIView(context: Context) -> UITextView { let editor = UITextView() editor.delegate = context.coordinator return editor } func makeCoordinator() -> Coordinator { Coordinator(text: $text, isFocused: $isFocused) } func updateUIView(_ editor: UITextView, context: Context) { // 避免循环更新,仅当文本不同时更新 if editor.text != text { editor.text = text } // 手动控制焦点状态 if isFocused && !editor.isFirstResponder { editor.becomeFirstResponder() } else if !isFocused && editor.isFirstResponder { editor.resignFirstResponder() } } class Coordinator: NSObject, UITextViewDelegate { @Binding private var text: String @Binding private var isFocused: Bool init(text: Binding<String>, isFocused: Binding<Bool>) { self._text = text self._isFocused = isFocused } func textViewDidChange(_ editor: UITextView) { text = editor.text } // 开始编辑时标记为聚焦 func textViewDidBeginEditing(_ textView: UITextView) { isFocused = true } // 结束编辑时取消聚焦标记 func textViewDidEndEditing(_ textView: UITextView) { isFocused = false } } } struct ContentView: View { @State var uitextView = "UITextView" @State var textfield = "TextField" @FocusState private var isUITextViewFocused: Bool // 管理UITextView的焦点状态 var body: some View { VStack { MyUITextView(text: $uitextView, isFocused: $isUITextViewFocused) .border(.black, width: 1) .frame(maxHeight: 40) .focused($isUITextViewFocused) // 绑定焦点状态 TextField("", text: $textfield) .border(.black, width: 1) } .toolbar { ToolbarItem(placement: .keyboard) { Button("Click") {} } } } }
内容的提问来源于stack exchange,提问作者Konrad
相关产品推荐
相关产品推荐

