我正在迁移一个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+的撤消方法。