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

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的焦点状态绑定,具体步骤如下:

  1. 给自定义MyUITextView添加@Binding var isFocused: Bool属性,用于同步焦点状态;
  2. 在Coordinator中实现textViewDidBeginEditing和textViewDidEndEditing代理方法,更新isFocused的值;
  3. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:44:58