swiftuinsundomanager

UndoManager's canUndo property not updating in SwiftUI


Why does the @Environment UndoManager not update its canUndo property when it has actions in its stack? I have a view that has a child that can utilize the un/redo functionality, but for some reason I can't disable the undo button based on the manager.

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

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

Solution

  • UndoManager.canUndo is not KVO compliant, so use some notification publisher to track state, like below

    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
                }
        }
    }