如何解决UndoManager/NSUndoManager适配异步或长时间运行任务的问题
基于UndoManager/NSUndoManager处理异步长耗时任务的问题
我在使用UndoManager / NSUndoManager处理异步或长时间运行任务时遇到问题,目前已有可行方案但复杂度太高,远超出这类常见问题的合理实现成本,因此我将发布现有方案同时希望能获得更优解法。
问题1
可撤销任务无法在当前runloop内完成,这类任务可以是带有异步回调的短操作,也可以是需要展示进度指示器、甚至支持取消选项的长时间运行操作。
问题2
可撤销任务可能失败或被取消,更糟糕的是重做(redo)任务也可能失败。示例:我移动了一个文件,执行撤销操作时发现该文件已不在新路径下,此时就不应该将重做任务重新放回撤销栈。
已尝试方案
思路1
我尝试在任务完成时再注册undo/redo,毕竟未完成、已取消或已失败的操作本身就不支持撤销。但这种配置下无法让操作和它的撤销操作正确配对:重做功能失效。示例:用户触发文件复制,复制操作结束后我向UndoManager注册该操作;用户选择撤销,我再次等待撤销操作完成后再向UndoManager注册,此时UndoManager无法识别刚完成的文件删除操作其实是之前复制操作的逆向操作,它不会向用户提供重做复制的选项,反而会提供撤销删除的选项。
思路2
关闭自动撤销分组。但我不知道如何针对长时间运行操作实现该方案,毕竟其他绝大多数任务我都需要使用自动分组功能。
我尝试给带异步回调的简单操作使用该方案时抛出了报错:endUndoGrouping called with no matching begin,相关代码如下:
let assets = PHAsset.fetchAssets(in: album, options: nil) let parent = PHCollectionList.fetchCollectionListsContaining(album, options: nil).firstObject if let undoManager = undoManager { undoManager.groupsByEvent = false undoManager.beginUndoGrouping() let isUndoManagerOperation = undoManager.isUndoing || undoManager.isRedoing let targetSelf = Controller.self as AnyObject undoManager.registerUndo(withTarget: targetSelf) { [weak undoManager] targetSelf in Controller.createAlbum(for: assets, title: album.localizedTitle, parent: parent, with: undoManager, completionHandler: nil) } if !isUndoManagerOperation { undoManager.setActionName(NSLocalizedString("Delete Album", comment: "Undoable action: Delete Album")) } } PHPhotoLibrary.shared().performChanges { PHAssetCollectionChangeRequest.deleteAssetCollections(NSArray.init(object: album)) } completionHandler: { (success, error) in DispatchQueue.main.async { undoManager?.endUndoGrouping() undoManager?.groupsByEvent = true } }
内容的提问来源于stack exchange,提问作者Pierre Bernard
相关产品推荐
相关产品推荐

