如何在Swift中获取NSUndoManager的正确回调值?

时间:2014-06-22 06:41:35

标签: macos cocoa swift

我有一个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)
  }
}

3 个答案:

答案 0 :(得分:0)

使用NSNumber而不是Int。

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

//...

func removeObjectFromEmployeesAtStringIndex(index: NSNumber) {
    removeObjectFromEmployeesAtIndex(index.integerValue)
}

答案 1 :(得分:0)

我现在正在阅读 Mac OS X的Cocoa编程。我将示例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)
}

答案 2 :(得分:-1)

我认为完全取代传统的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)
    }
}

然后你可以Swift-ly注册任何一个闭包,而不用担心包装东西以绕过一个过时的界面:

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