在Swift 3中使用NSUndoManager和.prepare(withInvocationTarget:)



我正在迁移一个Xcode 7/Swift 2.2 mac OS X项目到Xcode 8/Swift 3,我在我的视图控制器类MyViewController中使用undoManager遇到了一个问题,它有一个功能undo。

在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总是失败,并且undo操作没有注册。

是我遗漏了一些明显的东西,还是这是一个bug?

prepareWithInvocationTarget(_:)(或Swift 3中的prepare(withInvocationTarget:))创建了一个隐藏的代理对象,Swift 3运行时不能很好地工作。

(你可以称之为bug,并发送一个bug报告。)

为了达到你的目的,你不能使用registerUndo(withTarget:handler:)吗?

undoManager?.registerUndo(withTarget: self) {targetSelf in
    targetSelf.undo(data, moreData: moreData)
}

我也遇到过同样的问题,我不准备放弃对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()
    }
}

与OS 10.10向后兼容的解决方案:使用registerUndo(with Target: selector: object: )。保存单个值没问题。为了保存多个值,我将它们打包到一个字典中,并将其用于"object"参数。对于撤消操作,我将它们从字典中解包,然后用这些值调用OS10.11+的撤消方法。

相关内容

  • 没有找到相关文章

最新更新