6

我正在尝试了解如何NSLayoutManager使用 Swift 闭包。我可以成功注册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")

如您所见,它的“乌龟一路向下”。我该如何摆脱这种疯狂?即,在我能找到的所有示例代码中,人们使用代码的选择器版本来注册一种可以自行撤消的方法——这显然不适用于我正在使用的闭包方法......如何使用闭包版本并获得无限撤消/重做?

4

1 回答 1

19

您正在寻找的是相互递归。您需要两个函数,每个函数都注册对另一个函数的调用。以下是构建它的几种不同方法:

  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,因为强捕获它(默认)可能会创建一个保留周期。由于您将 as 传递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()
    }
    
于 2016-04-08T06:03:39.257 回答