Используя NSUndoManager, как зарегистрировать отмены с помощью замыканий Swift

Я пытаюсь понять, как использовать 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")

Как видите, "черепахи внизу". Как мне избежать этого безумия? то есть, во всем примере кода, который я могу найти, люди используют версию кода селектора, чтобы зарегистрировать метод, который может отменить сам - это явно не выполнимо с методом закрытия, который я использую... Как можно использовать замыкание версия и получить неограниченное отменить / повторить?

1 ответ

Решение

То, что вы ищете, это взаимная рекурсия. Вам нужны две функции, каждая из которых регистрирует вызов другой. Вот несколько способов структурировать его:

  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()
    }
    
Другие вопросы по тегам