【问题标题】:How to get the correct callback value for NSUndoManager in Swift?如何在 Swift 中为 NSUndoManager 获取正确的回调值?
【发布时间】:2014-06-22 06:41:35
【问题描述】:

我有一个NSDocument 子类,连接到NSArrayController。作为参考,我正在尝试翻译 Cocoa Programming for Mac OS X Fourth Edition 第 9 章中的示例。

从我之前问过的this question看来,我需要对NSUndoManager使用基于对象的撤消。为了将两个值传递给被调用的方法,我将它们打包到带有两个实例变量的NSObject 子类中。

当通过单击我的应用程序中的按钮调用从 employees 数组中插入和删除的 KVO 方法时,它们按预期工作。

但是,当在撤消操作期间调用removeObjectFromEmployeesAtIndex 时,传入的index 非常超出范围(对于第一行,它似乎总是55,然后索引增加到接下来的几行有数千个)。

如何获得正确的索引来执行撤消操作?

class Document: NSDocument {

  var employee_list: Array<Person> = []

  var employees: Array<Person> {

  get {
    return self.employee_list
  }

  set {
    if newValue == self.employee_list {
      return
    }

    self.employee_list = newValue
  }

  }

  func insertObject(person: Person, inEmployeesAtIndex index: Int) {
    self.undoManager.registerUndoWithTarget(self, selector: Selector("removeObjectFromEmployeesAtIndex:"), object: index)

    if (!self.undoManager.undoing) {
      self.undoManager.setActionName("Add Person")
    }

    employees.insert(person, atIndex: index)
  }

  func removeObjectFromEmployeesAtIndex(index: Int) {
    let person = self.employees[index]
    let pair   = PersonIndexPair(person: person, index: index)

    self.undoManager.registerUndoWithTarget(self, selector: Selector("insertPersonIndexPair:"), object: pair)

    if (!self.undoManager.undoing) {
      self.undoManager.setActionName("Remove Person")
    }

    employees.removeAtIndex(index)
  }
  func insertPersonIndexPair(pair: PersonIndexPair) {
    insertObject(pair.person, inEmployeesAtIndex: pair.index)
  }

}

编辑:我通过传递一个字符串来解决这个问题,但这似乎很迟钝:

self.undoManager.registerUndoWithTarget(self, selector: Selector("removeObjectFromEmployeesAtStringIndex:"), object: String(index))

//...

func removeObjectFromEmployeesAtStringIndex(index: String) {
  if let i = index.toInt() {
    removeObjectFromEmployeesAtIndex(i)
  }
}

【问题讨论】:

    标签: macos cocoa swift


    【解决方案1】:

    使用 NSNumber 代替 Int。

    self.undoManager.registerUndoWithTarget(self, selector:Selector("removeObjectFromEmployeesAtStringIndex:"), object: NSNumber(integer: index))
    
    //...
    
    func removeObjectFromEmployeesAtStringIndex(index: NSNumber) {
        removeObjectFromEmployeesAtIndex(index.integerValue)
    }
    

    【讨论】:

      【解决方案2】:

      我现在正在阅读 Mac OS X 的 Cocoa Programming。我将示例 Object-C 代码翻译如下,只需将其附加到类 Document:

      func insertObject(p: Person, inEmployeesAtIndex index: Int) {
          //NSLog("adding %@ to %@", p, employees)
      
          let undo = self.undoManager
          undo!.prepareWithInvocationTarget(self).removeObjectFromEmployeesAtIndex(index)
          if !undo!.undoing {
              undo!.setActionName("Add Person")
          }
      
          employees.insertObject(p, atIndex: index)
      }
      
      func removeObjectFromEmployeesAtIndex(index: Int) {
          let p = employees.objectAtIndex(index) as! Person
      
          //NSLog("Removing %@ from %@", p, index)
      
          let undo = self.undoManager
          undo!.prepareWithInvocationTarget(self).insertObject(p, inEmployeesAtIndex: index)
      
          if !undo!.undoing {
              undo!.setActionName("Remove Person")
          }
      
          employees.removeObjectAtIndex(index)
      }
      

      【讨论】:

        【解决方案3】:

        我认为完全替换老式的 Objective-C NSUndoManager 方法是最干净的:

        private class SwiftUndoPerformer: NSObject {
            let closure: Void -> Void
        
            init(closure: Void -> Void) {
                self.closure = closure
            }
        
            @objc func performWithSelf(retainedSelf: SwiftUndoPerformer) {
                closure()
            }
        }
        
        extension NSUndoManager {
            func registerUndo(closure: Void -> Void) {
                let performer = SwiftUndoPerformer(closure: closure)
                registerUndoWithTarget(performer, selector: Selector("performWithSelf:"), object: performer)
                //(Passes unnecessary object to get undo manager to retain SwiftUndoPerformer)
            }
        }
        

        然后你可以快速注册任何闭包,而不必担心包装东西以绕过过时的接口:

        undoManager.registerUndo {
            self.insertObject(person, inEmployeesAtIndex: index)
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-19
          相关资源
          最近更新 更多