使用 NSUndoManager,如何使用 Swift 闭包注册撤销

Using NSUndoManager, how to register undos using Swift closures

我正在尝试了解如何使用 Swift 闭包来使用 NSLayoutManager。我可以成功注册一个 undo 如下:

doThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
    undoThing();
}
undoManager?.setActionName("do thing")

当然我需要支持redo,这相当于撤消了撤消。我可以做到:

doThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
    undoThing();

    undoManager?.registerUndoWithTarget(self, handler: { _ in
        doThing();
    }
    undoManager?.setActionName("do thing")
}
undoManager?.setActionName("do thing")

但现在我需要支持撤销重做...嗯...好的:

doThing();
undoManager?.registerUndoWithTarget(self, handler: { _ in
    undoThing();

    undoManager?.registerUndoWithTarget(self, handler: { _ in
        doThing();

        undoManager?.registerUndoWithTarget(self, handler: { _ in
             undoThing();
        }
        undoManager?.setActionName("do thing")
    }
    undoManager?.setActionName("do thing")
}
undoManager?.setActionName("do thing")

如您所见 "turtles all the way down." 我该如何摆脱这种疯狂?即,在我能找到的所有示例代码中,人们使用代码的选择器版本来注册一个可以撤消自身的方法——这显然不适用于我正在使用的闭包方法……如何使用闭包版本并获得无限 undo/redo?

你要找的是相互递归。您需要两个函数,每个函数都注册对另一个的调用。这里有几种不同的结构方式:

  1. doThing()中,注册撤消操作以调用undoThing()。在 undoThing 中,注册撤消操作以调用 doThing()。即:

    @IBAction func doThing() {
        undoManager?.registerUndoWithTarget(self, handler: { me in
            me.undoThing()
        })
        undoManager?.setActionName("Thing")
    
        // do the thing here
    }
    
    @IBAction func undoThing() {
        undoManager?.registerUndoWithTarget(self, handler: { me in
            me.doThing()
        })
        undoManager?.setActionName("Thing")
    
        // undo the thing here
    }
    

请注意,您应该在闭包中引用self,除非您使用weak捕获它,因为强烈捕获它(默认)可能创建一个保留周期。由于您将 self 作为 target 传递给撤消管理器,它已经为您保留了弱引用并将其(强)传递给撤消块,因此您不妨使用它而不是引用self 完全在撤消块中。

  1. 将对 doThing()undoThing() 的调用包装在处理撤消注册的单独函数中,并将用户操作连接到这些新函数:

    private func doThing() {
        // do the thing here
    }
    
    private func undoThing() {
        // undo the thing here
    }
    
    @IBAction func undoablyDoThing() {
        undoManager?.registerUndoWithTarget(self, handler: { me in
            me.redoablyUndoThing()
        })
        undoManager?.setActionName("Thing")
        doThing()
    }
    
    @IBAction func redoablyUndoThing() {
        undoManager?.registerUndoWithTarget(self, handler: { me in
            me.undoablyDoThing()
        })
        undoManager?.setActionName("Thing")
        undoThing()
    }