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

SwiftUI TextEditor 自定义撤销按钮实现问题

SwiftUI TextEditor 自定义撤销按钮实现问题

兄弟我懂你这个困扰!SwiftUI的TextEditor自带的撤销/重做逻辑确实没直接暴露给我们开发者用,环境里拿到的UndoManager根本不是它内部实际在用的那个——毕竟底层还是依赖UITextView那套,用的是私有的_UITextUndoManager对吧?我之前也踩过这个坑,给你分享个靠谱的实现思路:

核心思路:获取TextEditor底层的UITextView实例

要拿到TextEditor真正的UndoManager,我们得先获取它包装的UITextView,然后直接用这个UITextView的undoManager属性。

步骤1:定义一个视图来捕获底层UITextView

我们可以写一个简单的UIViewRepresentable,用来在TextEditor的视图层级里找到对应的UITextView:

struct TextViewAccessor: UIViewRepresentable {
    let onTextViewFound: (UITextView) -> Void
    
    func makeUIView(context: Context) -> UIView {
        let placeholderView = UIView()
        // 延迟执行,确保视图层级已经构建完成
        DispatchQueue.main.async {
            // 遍历父视图找到UITextView
            if let textView = placeholderView.superview?.superview?
                .subviews.compactMap({ $0 as? UITextView }).first {
                onTextViewFound(textView)
            }
        }
        return placeholderView
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {}
}

步骤2:在你的View中集成并保存UndoManager

在使用TextEditor的视图里,我们添加一个状态变量来保存拿到的UndoManager,然后通过background修饰符注入TextViewAccessor:

struct ContentView: View {
    @State private var editorText = ""
    @State private var textEditorUndoManager: UndoManager?
    
    var body: some View {
        VStack(spacing: 16) {
            HStack {
                Button("撤销") {
                    guard let undoManager = textEditorUndoManager, undoManager.canUndo else { return }
                    undoManager.undo()
                }
                .disabled(!(textEditorUndoManager?.canUndo ?? false))
                
                Button("重做") {
                    guard let undoManager = textEditorUndoManager, undoManager.canRedo else { return }
                    undoManager.redo()
                }
                .disabled(!(textEditorUndoManager?.canRedo ?? false))
            }
            
            TextEditor(text: $editorText)
                .background(
                    TextViewAccessor(onTextViewFound: { textView in
                        self.textEditorUndoManager = textView.undoManager
                    })
                )
                .border(.gray, width: 1)
        }
        .padding()
    }
}

注意事项

  • 这个方法依赖于TextEditor的底层视图层级,苹果如果在后续iOS版本中修改了视图结构,可能会失效,建议在你的目标系统版本上充分测试(亲测iOS 15-17可用)
  • 不要直接引用私有类_UITextUndoManager,避免审核风险,直接用系统公开的UndoManager类型接收即可,功能完全不受影响
  • 环境中的UndoManager是SwiftUI全局环境的,和TextEditor内部的撤销逻辑完全独立,所以不能用它来实现针对TextEditor的撤销/重做

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 09:53:01