Отмена не работает в интерфейсе Details проекта интерфейса Master-Details - PullRequest
0 голосов
/ 18 октября 2018

У меня есть интерфейс Master-Details.Мастер - это табличное представление со связанной моделью, контроллером массива и кнопками добавления / удаления.Интерфейс Details состоит из множества отдельных табличных представлений, каждое из которых имеет собственную модель, контроллер массива и кнопки добавления / удаления.Табличным представлением вверху на снимке экрана является Master.

Снимок экрана построителя интерфейса

Контроллер мастер-массива привязан к владельцу файла и связанному массиву, который в данном случае является экземпляром документа и аэропортов.

Привязка контроллера основного массива

Каждый отдельный контроллер массива деталей связан с контроллером главного массива, ключом контроллераof Selection, и связанный с ним массив Details, являющийся свойствами главной модели.

пример привязки контроллера массива Details

У меня есть эта настройка, так что Addкнопки для всех видов таблицы «Подробности» не активны, если в мастере не была выбрана строка.

Пример сведений Добавить привязку кнопок

У меня есть код отменыв Document.swift

private var KVOContext: Int = 0
private var currentAirportIndex = 0

class Document: NSDocument, NSWindowDelegate {

    ...

@IBAction func addAirports(sender: NSButton) {
    let windowController = windowControllers[0]
    let window = windowController.window!

    let endedEditing = window.makeFirstResponder(window)
    if !endedEditing {
        Swift.print("Unable to end editing")
        return
    }

    let undo: UndoManager = undoManager!

    // Has an edit occurred already in this event?
    if undo.groupingLevel > 0 {
        // Close the last group
        undo.endUndoGrouping()
        // Open a new group
        undo.beginUndoGrouping()
    }

    // Create the object
    let airport = airportsArrayController.newObject() as! Airport

    // Add it to the array controller's contentArray
    airportsArrayController.addObject(airport)

    // Re-sort (in case the user has sorted a column)
    airportsArrayController.rearrangeObjects()

    // Get the sorted array
    let sortedAirports = airportsArrayController.arrangedObjects as! [Airport]

    // Find the object just added
    let row = sortedAirports.index(of: airport)!

    // Begin the edit in the first column
    airportsTableView.editColumn(0,
                         row: row,
                         with: nil,
                         select: true)
}

...

// MARK: - Accessors

@objc func insertObject(_ airport: Airport, inAirportsAtIndex index: Int) {

    // Add the inverse of this operation to the undo stack
    let undo = self.undoManager!
    (undo.prepare(withInvocationTarget: self)
        as AnyObject).removeObjectFromAirportsAtIndex(index)
    if !undo.isUndoing {
        undo.setActionName("Add Airport")
    }
    airports.insert(airport, at: index)
}


@objc func removeObjectFromAirportsAtIndex(_ index: Int) {
    // variable to be used in each Deatils action method - not working yet
    currentAirportIndex = index

    let airport: Airport = airports[index]

    // Add the inverse of this operation to the undo stack
    let undo: UndoManager = undoManager!
    (undo.prepare(withInvocationTarget: self) as AnyObject)
        .insertObject(airport, inAirportsAtIndex: index)
    if !undo.isUndoing {
        undo.setActionName("Remove Airport")
    }
    airports.remove(at: index)
}

Отмена хорошо работает для добавления / удаления и редактирования основного вида таблицы - Аэропорт.- (В качестве примечания я хотел бы, чтобы кнопка меню отмены говорила «отменить редактирование» при отмене редактирования, поэтому я знаю, что для этого мне нужно где-то изменить actionName здесь. Я еще не работал над этой частью)

Моя текущая проблема заключается в том, что мне не удалось заставить отмену работать для табличных представлений сведений для добавления / удаления или редактирования.Цепочка респондента перемещается к представлениям таблицы Details, как и ожидалось.Я не уверен в правильном способе сделать это.Я пытался перегрузить методы insertObject (: :) и removeObjectFrom… ( :).

// MARK: - Actions

@IBAction func addAirports(sender: NSButton) {
    let windowController = windowControllers[0]
    let window = windowController.window!

    let endedEditing = window.makeFirstResponder(window)
    if !endedEditing {
        Swift.print("Unable to end editing")
        return
    }

    let undo: UndoManager = undoManager!

    // Has an edit occurred already in this event?
    if undo.groupingLevel > 0 {
        // Close the last group
        undo.endUndoGrouping()
        // Open a new group
        undo.beginUndoGrouping()
    }

    // Create the object
    let airport = airportsArrayController.newObject() as! Airport

    // Add it to the array controller's contentArray
    airportsArrayController.addObject(airport)

    // Re-sort (in case the user has sorted a column)
    airportsArrayController.rearrangeObjects()

    // Get the sorted array
    let sortedAirports = airportsArrayController.arrangedObjects as! [Airport]

    // Find the object just added
    let row = sortedAirports.index(of: airport)!

    // Begin the edit in the first column
    airportsTableView.editColumn(0,
                         row: row,
                         with: nil,
                         select: true)
}


@IBAction func addGroundFrequencies(sender: NSButton) {
    let windowController = windowControllers[0]
    let window = windowController.window!

    let endedEditing = window.makeFirstResponder(window)
    if !endedEditing {
        Swift.print("Unable to end editing")
        return
    }

    // **** I think this is where I need to add a sub group of undo?

//      let undo: UndoManager = undoManager!
//      
//      // Has an edit occurred already in this event?
//      if undo.groupingLevel > 0 {
//          // Close the last group
//          undo.endUndoGrouping()
//          // Open a new group
//          undo.beginUndoGrouping()
//      }


    let groundFrequency = groundFrequenciesArrayController.newObject() as! GroundFrequency
    groundFrequenciesArrayController.addObject(groundFrequency)
    groundFrequenciesArrayController.rearrangeObjects()
    let sortedGroundFrequencies
        = groundFrequenciesArrayController.arrangedObjects as! [GroundFrequency]
    let row = sortedGroundFrequencies.index(of: groundFrequency)!
    groundFrequenciesTableView.editColumn(0,
                                  row: row,
                                  with: nil,
                                  select: true)
}

// MARK: - Accessors

@objc func insertObject(_ airport: Airport, inAirportsAtIndex index: Int) {

    // Add the inverse of this operation to the undo stack
    let undo = self.undoManager!
    (undo.prepare(withInvocationTarget: self)
        as AnyObject).removeObjectFromAirportsAtIndex(index)
    if !undo.isUndoing {
        undo.setActionName("Add Airport")
    }
    airports.insert(airport, at: index)
}


@objc func removeObjectFromAirportsAtIndex(_ index: Int) {
    // variable to be used in each Deatils action method - not working yet
    currentAirportIndex = index

    let airport: Airport = airports[index]

    // Add the inverse of this operation to the undo stack
    let undo: UndoManager = undoManager!
    (undo.prepare(withInvocationTarget: self) as AnyObject)
        .insertObject(airport, inAirportsAtIndex: index)
    if !undo.isUndoing {
        undo.setActionName("Remove Airport")
    }
    airports.remove(at: index)
}



// **** these functions are never called

@objc func insertObject(_ groundFrequency: GroundFrequency,
                                 inGroundFrequenciesAtIndex index: Int) {

    // Add the inverse of this operation to the undo stack
    let undo = self.undoManager!
    (undo.prepare(withInvocationTarget: self)
        as AnyObject).removeObjectFromGoundFrequenciesAtIndex(index)
    if !undo.isUndoing {
        undo.setActionName("Add Ground Frequency")
    }
    airports[currentAirportIndex].groundFrequencies.insert(groundFrequency, at: index)
}


@objc func removeObjectFromGroundFrequenciesAtIndex(_ index: Int) {
    let groundFrequency: GroundFrequency = airports[currentAirportIndex].groundFrequencies[index]

    let undo: UndoManager = undoManager!
    (undo.prepare(withInvocationTarget: self) as AnyObject)
        .insertObject(groundFrequency, inGroundFrequenciesAtIndex: index)
    if !undo.isUndoing {
        undo.setActionName("Remove Ground Frequency")
    }
    airports[currentAirportIndex].groundFrequencies.remove(at: index)
}

Методы insertObject (_ groundFrequency: :) и removeObjectFromGroundFrequenciesAtIndex (_ :) никогда не вызывались,Так что я уверен, что это не правильно.Когда я запускаю его, я добавляю аэропорт, затем добавляю наземную частоту.Отмена по-прежнему действует в аэропорту.Даже когда ответчик все еще активен в основной таблице частот.

Я не уверен, как настроить отмену для работы с контроллерами массива Details и связанным табличным представлением.После того, как я понял это, мне нужно знать, как добавить к ним отмену редактирования.

Я пытался исследовать вложение undoManager, но не могу найти ничего, что могло бы помочь.Это может даже не быть тем, что мне нужно сделать здесь.

Я попытался добавить следующий код, чтобы отменить редактирование для работы.Как указывалось ранее, отмена работает при редактировании для главной таблицы, но не для представления таблицы Details (groundFrequency).

private var KVOContext: Int = 0
private var currentAirportIndex = 0


class Document: NSDocument, NSWindowDelegate {

...


// commented sections below did nothing.

@objc dynamic var airports: [Airport] = [] {
    willSet {
        for airport in airports {
//              for groundFrequency in airport.groundFrequencies {
//                  stopObservingGroundFrequency(groundFrequency: groundFrequency)
//              }
            stopObservingAirport(airport: airport)
        }
    }
    didSet {
        for airport in airports {
            startObservingAirport(airport: airport)
//              for groundFrequency in airport.groundFrequencies {
//                  startObservingGroundFrequency(groundFrequency: groundFrequency)
//              }
        }
    }
}

// MARK: - Key Value Observing

func startObservingAirport(airport: Airport) {
    airport.addObserver(self,
                        forKeyPath: "airportCode",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "gateServices",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "pushCall",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "companyMaintenanceAvailable",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "walkAroundRequired",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "dAtis",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "pdc",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "cpdlc",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "companyOpsFrequency",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "clearanceFrequency",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "mxFrequency",
                        options: .old,
                        context: &KVOContext)
    airport.addObserver(self,
                        forKeyPath: "atopsFrequency",
                        options: .old,
                        context: &KVOContext)

}


func stopObservingAirport(airport: Airport) {
    airport.removeObserver(self, forKeyPath: "airportCode")
    airport.removeObserver(self, forKeyPath: "gateServices")
    airport.removeObserver(self, forKeyPath: "pushCall")
    airport.removeObserver(self, forKeyPath: "companyMaintenanceAvailable")
    airport.removeObserver(self, forKeyPath: "walkAroundRequired")
    airport.removeObserver(self, forKeyPath: "dAtis")
    airport.removeObserver(self, forKeyPath: "pdc")
    airport.removeObserver(self, forKeyPath: "cpdlc")
    airport.removeObserver(self, forKeyPath: "companyOpsFrequency")
    airport.removeObserver(self, forKeyPath: "clearanceFrequency")
    airport.removeObserver(self, forKeyPath: "mxFrequency")
    airport.removeObserver(self, forKeyPath: "atopsFrequency")
}


func startObservingGroundFrequency(groundFrequency: GroundFrequency) {
    groundFrequency.addObserver(self,
                                forKeyPath: "groundSector",
                                options: .old,
                                context: &KVOContext)
    groundFrequency.addObserver(self,
                                forKeyPath: "groundFrequency",
                                options: .old,
                                context: &KVOContext)
}


func stopObservingGroundFrequency(groundFrequency: GroundFrequency) {
    groundFrequency.removeObserver(self, forKeyPath: "groundSector")
    groundFrequency.removeObserver(self, forKeyPath: "groundFrequency")
}


override func observeValue(forKeyPath keyPath: String?,
                           of object: Any?,
                           change: [NSKeyValueChangeKey : Any]?,
                           context: UnsafeMutableRawPointer?) {
    if context != &KVOContext {
        // If context does not match, this message must be intended for the super class
        super.observeValue(forKeyPath: keyPath,
                           of: object,
                           change: change,
                           context: context)
        return
    }


    var oldValue: AnyObject? = change?[NSKeyValueChangeKey.oldKey] as AnyObject
    if oldValue is NSNull {
        oldValue = nil
    }

    let undo: UndoManager = undoManager!
    (undo.prepare(withInvocationTarget: object as Any) as AnyObject)
        .setValue(oldValue, forKeyPath: keyPath!)
}

Вот код для Airport и GroundFrequency, если это необходимо:

@objc(Airport)
class Airport: NSObject, NSCoding {

@objc dynamic var airportCode: String? = "Airport"
@objc dynamic var gateServices: Bool = true
@objc dynamic var pushCall: Bool = true
@objc dynamic var companyMaintenanceAvailable: Bool = false
@objc dynamic var walkAroundRequired: Bool = true
@objc dynamic var dAtis: Bool = true
@objc dynamic var pdc: Bool = true
@objc dynamic var cpdlc: Bool = true

@objc dynamic var companyOpsFrequency: String = "Freq"
@objc dynamic var clearanceFrequency: String = "Freq"
@objc dynamic var mxFrequency: String = "Freq"
@objc dynamic var atopsFrequency: String = "Freq"

@objc dynamic var groundFrequencies: [GroundFrequency] = []  
@objc dynamic var towerFrequencies: [TowerFrequency] = []
@objc dynamic var rampFrequencies: [RampFrequency] = []
@objc dynamic var atisFrequencies: [AtisFrequency] = []
@objc dynamic var vorFrequencies: [VorFrequency] = []
@objc dynamic var ilsFrequencies: [IlsFrequency] = []
@objc dynamic var departureProcedures: [DepartureProcedure] = []
@objc dynamic var singleEngineProcedures: [SingleEngineProcedure] = []
@objc dynamic var hudData: [HudData] = []


// MARK: - NSCoding

...

}



@objc(GroundFrequency)
class GroundFrequency: NSObject, NSCoding {

@objc dynamic var groundSector: String? = "Sector"  // East, West, North, South
@objc dynamic var groundFrequency = "Freq"


// MARK: - NSCoding

...

}

Может ли кто-нибудь указать мне правильное направление, чтобы отменить работу для каждого представления таблицы интерфейса Details?

Редактировать 10/19 - Или, может быть, я совершенно не прав?отслеживать здесь и нужно по-другому заняться дизайном этого проекта?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...