在 Swift 3 中使用 NSUndoManager 和 .prepare(withInvocationTarget:)
Using NSUndoManager and .prepare(withInvocationTarget:) in Swift 3
我正在将 Xcode 7 / Swift 2.2 mac OS X 项目迁移到 Xcode 8 / Swift 3,并且我 运行 在我的视图控制器 class、MyViewController 中使用 undoManager 时遇到问题,它具有撤消功能。
在 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")
}
但是downcast到MyViewController总是失败,undo操作没有注册
我是不是遗漏了一些明显的东西,或者这是一个错误?
prepareWithInvocationTarget(_:)
(或 Swift 3 中的 prepare(withInvocationTarget:)
)创建了一个隐藏的代理对象,Swift 3 运行时无法正常工作。
(您可以称其为错误,并发送错误报告。)
为了达到你的目的,你不能用registerUndo(withTarget:handler:)
吗?
undoManager?.registerUndo(withTarget: self) {targetSelf in
targetSelf.undo(data, moreData: moreData)
}
向后兼容 OS 10.10 的解决方案:使用 registerUndo(with Target: selector: object: )
。保存单个值没问题。为了保存多个值,我将它们打包到字典中并将其用于 "object" 参数。对于撤消操作,我从字典中解压它们,然后使用这些值调用 OS10.11+ 撤消方法。
我遇到了同样的问题,我不准备放弃 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()
}
}
希望对其他人也有帮助。
我正在将 Xcode 7 / Swift 2.2 mac OS X 项目迁移到 Xcode 8 / Swift 3,并且我 运行 在我的视图控制器 class、MyViewController 中使用 undoManager 时遇到问题,它具有撤消功能。
在 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")
}
但是downcast到MyViewController总是失败,undo操作没有注册
我是不是遗漏了一些明显的东西,或者这是一个错误?
prepareWithInvocationTarget(_:)
(或 Swift 3 中的 prepare(withInvocationTarget:)
)创建了一个隐藏的代理对象,Swift 3 运行时无法正常工作。
(您可以称其为错误,并发送错误报告。)
为了达到你的目的,你不能用registerUndo(withTarget:handler:)
吗?
undoManager?.registerUndo(withTarget: self) {targetSelf in
targetSelf.undo(data, moreData: moreData)
}
向后兼容 OS 10.10 的解决方案:使用 registerUndo(with Target: selector: object: )
。保存单个值没问题。为了保存多个值,我将它们打包到字典中并将其用于 "object" 参数。对于撤消操作,我从字典中解压它们,然后使用这些值调用 OS10.11+ 撤消方法。
我遇到了同样的问题,我不准备放弃 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()
}
}
希望对其他人也有帮助。