UndoManager 的 canUndo 属性 未在 SwiftUI 中更新

UndoManager's canUndo property not updating in SwiftUI

为什么 @Environment UndoManager 在其堆栈中有操作时不更新其 canUndo 属性?我有一个视图有一个可以使用 un/redo 功能的子视图,但出于某种原因我无法禁用基于管理器的撤消按钮。

struct MyView: View {
    @Environment(\.undoManager) var undoManager: UndoManager?

    var body: some View {
        Button("Undo") { ... }
            .disabled(!self.undoManager!.canUndo)
    }
}

UndoManager.canUndo 不符合 KVO,因此使用一些通知发布者来跟踪状态,如下所示

struct MyView: View {
    @Environment(\.undoManager) var undoManager
    @State private var canUndo = false

    // consider also other similar notifications
    private let undoObserver = NotificationCenter.default.publisher(for: .NSUndoManagerDidCloseUndoGroup)

    var body: some View {
        Button("Undo") { }
            .disabled(!canUndo)
            .onReceive(undoObserver) { _ in
                self.canUndo = self.undoManager!.canUndo
            }
    }
}

backup

当涉及到 canRedo 时,我尝试了多种方法,我最终得到的是这个 - 所以观察 viewModel(或 document 或任何其他支持撤消的数据源) 并更新 canUndo/canRedo 以响应它的变化:

struct MyView: View {
    @ObservedObject var viewModel: ViewModel

    @Environment(\.undoManager) private var undoManger: UndoManager!
    @State private var canUndo = false
    @State private var canRedo = false
    
    var body: some View {
        RootView()
            .onReceive(viewModel.objectWillChange) { _ in
                canUndo = undoManger.canUndo
                canRedo = undoManger.canRedo
            }

        if canUndo {
            Button(
                action: { undoManger?.undo() },
                label: { Text("Undo") }
            )
        }
        if canRedo {
            Button(
                action: { undoManger?.redo() },
                label: { Text("Redo") }
            )
        }
        ...

我还将它包装在一个 standalone button 中(没有过度概括超出我自己需要的实现),它从我的角度消除了样板文件并使复杂性更加私密,所以它最终对我来说是这样的:

struct MyView: View {
    @ObservedObject var viewModel: ViewModel

    var body: some View {
       RootView()
       UndoManagerActionButton(
           .undo,
           willChangePublisher: viewModel.objectWillChange
       )
       UndoManagerActionButton(
           .redo,
           willChangePublisher: viewModel.objectWillChange
       )
       ...