Is there a way to "undo a redo" with using UndoManager?

Hello everyone,
When I was trying to implement redo and undo with using UndoManager, I noticed I could not do undo a redo.
Here is a code sample which explain the problem.

import Cocoa

class ViewController: NSViewController {
    var reundoManager = UndoManager()
    var value = 0
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }
    
    @IBOutlet weak var label: NSTextField!
    
    @IBAction func add(_ sender: NSButton) {
        value += 1
        label.stringValue = String(value)
        reundoManager.registerUndo(withTarget: self) { target in
            
            target.value -= 1
            self.label.stringValue = String(self.value)
            target.reundoManager.registerUndo(withTarget: target) { target in
                target.value += 1
                self.label.stringValue = String(self.value)
            }
        }
    }
    @IBAction func reduce(_ sender: NSButton) {
        value -= 1
        self.label.stringValue = String(self.value)
        reundoManager.registerUndo(withTarget: self) { target in
            
            target.value += 1
            self.label.stringValue = String(self.value)
            target.reundoManager.registerUndo(withTarget: target) { target in
                target.value -= 1
                self.label.stringValue = String(self.value)
                
            }
        }
    }
    @IBAction func undo(_ sender: NSButton) {
        reundoManager.undo()
    }
    @IBAction func redo(_ sender: NSButton) {
        reundoManager.redo()
    }
}



After pushing add or reduce button some times, I pushed undo button and redo button.
example:

add→add→add
label:3
undo→undo→undo
label:0
redo→redo→redo
label:3

after that, I pushed undo button again.
I assumed label's number will be 2.
But, actually label's number didn't change.

After a while, I found when I make nest, it seemed to work as I assumed.

    @IBAction func add(_ sender: NSButton) {
        value += 1
        label.stringValue = String(value)
        reundoManager.registerUndo(withTarget: self) { target in
            
            target.value -= 1
            self.label.stringValue = String(self.value)
            target.reundoManager.registerUndo(withTarget: target) { target in
                target.value += 1
                self.label.stringValue = String(self.value)
                target.reundoManager.registerUndo(withTarget: target) { target in
                    target.value -= 1
                    self.label.stringValue = String(self.value)
                }
            }
        }
    }

But it was only once and redoing didn't work. If I use this, I need infinity nest.
Is there any way to work I assumed?
Thank you for your advice.

The intended way to use UndoManager is to call the API as if the user had done it. That is, when you register an undo for add, it should call reduce (or a common helper function), using self as the target. That way there’s always a second call to registerUndo and you don’t have to nest infinitely.

1 Like

That's what I really wanted to do!
Thank you very much!!!!

1 Like