У меня есть интерфейс 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 - Или, может быть, я совершенно не прав?отслеживать здесь и нужно по-другому заняться дизайном этого проекта?