Использование NSUndoManager и .prepare(withInvocationTarget:) в Swift 3

Я переношу проект Xcode 7/Swift 2.2 mac OS X в Xcode 8/Swift 3, и у меня возникла проблема с использованием undoManager в моем классе контроллера представления, MyViewController, который имеет функцию отмены.

В Xcode 7/Swift 2.2 это работало нормально:

undoManager?.prepareWithInvocationTarget(self).undo(data, moreData: moreData)
undoManager?.setActionName("Change Data)

В Xcode 8/Swift 3 с использованием рекомендуемого шаблона из https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html

это должно быть изменено на:

if let target = undoManager?.prepare(withInvocationTarget: self) as? MyViewController {
    target.undo(data, moreData: moreData)
    undoManager?. setActionName("Change Data")
}

Однако приведение вниз к MyViewController всегда терпит неудачу, и операция отмены не регистрируется.

Я пропустил что-то очевидное здесь, или это ошибка?


person jbaraga    schedule 18.09.2016    source источник


Ответы (3)


prepareWithInvocationTarget(_:) (или prepare(withInvocationTarget:) в Swift 3) создает скрытый прокси-объект, с которым среда выполнения Swift 3 не может работать должным образом.

(Вы можете назвать это ошибкой и отправить отчет об ошибке.)

Разве вы не можете использовать registerUndo(withTarget:handler:) для достижения своей цели?

undoManager?.registerUndo(withTarget: self) {targetSelf in
    targetSelf.undo(data, moreData: moreData)
}
person OOPer    schedule 18.09.2016
comment
Спасибо. Я тоже играл с этим. К сожалению, он не совместим с OS10.10, но, возможно, пришло время двигаться дальше. - person jbaraga; 19.09.2016
comment
Решение для ОС 10.10: используйте registerUndo(with Target: selector: object: ). Нет проблем для сохранения одного значения. Чтобы сохранить несколько значений, я упаковываю их в словарь и использую его для параметра объекта. Для операции отмены я распаковываю их из словаря. - person jbaraga; 24.09.2016
comment
@jbaraga, спасибо за отчет. Но почему вы не опубликуете это как еще один ответ? Это может помочь разработчикам, которым нужно работать с 10.10. - person OOPer; 24.09.2016
comment
Хорошее предложение. Сделанный. - person jbaraga; 24.09.2016

У меня была та же проблема, и я не был готов отказаться от поддержки iOS 8 и macOS 10.10 или вернуться к Swift 2.3. Хотя синтаксис registerUndo(withTarget:handler) удобен, поэтому я в основном просто накатил свою собственную версию:

/// An extension to undo manager that adds closure based
/// handling to OS versions where it is not available.
extension UndoManager
{
    /// Registers an undo operation using a closure. Behaves in the same wasy as 
    /// `registerUndo(withTarget:handler)` but it compatible with older OS versions.
    func compatibleRegisterUndo<TargetType : AnyObject>(withTarget target: TargetType, handler: @escaping (TargetType) -> ())
    {
        if #available(iOS 9.0, macOS 10.11, *)
        {
            self.registerUndo(withTarget: target, handler: handler)
        }
        else
        {
            let operation = BlockOperation {
                handler(target)
            }
            self.registerUndo(withTarget: self, selector: #selector(UndoManager.performUndo(operation:)), object: operation)
        }
    }

    /// Performs an undo operation after it has been registered
    /// by `compatibleRegisterUndo`. Should not be called directly.
    func performUndo(operation: Operation)
    {
        operation.start()
    }
}

Надеюсь, это полезно и для кого-то еще.

person Luke Rogers    schedule 26.10.2016

Решение для обратной совместимости с ОС 10.10: используйте registerUndo(with Target: selector: object: ). Нет проблем для сохранения одного значения. Чтобы сохранить несколько значений, я упаковываю их в словарь и использую его для параметра «объект». Для операции отмены я распаковываю их из словаря, а затем вызываю метод отмены OS10.11+ с этими значениями.

person jbaraga    schedule 23.09.2016
comment
Спасибо. Благодаря включению кода случая с несколькими значениями больше людей могут легко найти ваш ответ полезным. Пожалуйста, потратьте на это некоторое время. - person OOPer; 24.09.2016