Использование NSArrayController в нескольких сценариях раскадровки

У меня есть приложение Core Data на базе Mac, которое использует раскадровки. Раскадровка имеет следующий макет:

Window Controller
    Split View Controller
        Table View Controller
        Text View Controller

Моя модель основных данных содержит элемент главы, который содержит два атрибута: заголовок и содержимое. Я хочу, чтобы в представлении таблицы отображалось название каждой главы. Текстовое представление отображает содержимое выбранной главы.

Если бы я использовал xib файл, я бы добавил контроллер массива в xib файл. Я бы привязал контроллер массива к файловому владельцу для доступа к подклассу NSPsistentDocument. Я бы привязал табличное представление к свойству array controllerObjects и привязал текстовое представление к выбору контроллера массива.

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

Как добавить контроллер массива в Interface Builder, чтобы как контроллер табличного представления, так и контроллер текстового вида могли получить к нему доступ и связать с ним?

Ответ 1

Ключом к выполнению этой работы является наличие экземпляра NSArrayController в каждом из ваших подклассов NSViewController и их связывание через центральный источник данных (скорее всего, ваш подкласс NSDocument). Затем вы можете установить этот источник данных в качестве подклассов NSViewController representedObject, передав его через ваш нисходящий контроллеры. Ниже приведен пример приложения раскадровки с NSWindowController, у которого есть контроллер представления контента, который является NSSplitViewController с двумя контроллерами детского представления (настройка "Мастер/Деталь" ):

class Document: NSDocument {

    var dataSource: DataSource? = DataSource()

    ...
}

class DataSource: NSObject, NSCoding {

    var items: [Item] = []
    var selectionIndexes: NSIndexSet = NSIndexSet()

    ...
}

class WindowController: NSWindowController {

    override var document: AnyObject? {
        didSet {
            if let document = self.document as? Document {
                self.contentViewController?.representedObject = document
            }
        }
    }

}

class SplitViewController: NSSplitViewController {

    override var representedObject: AnyObject? {
        didSet {
            for viewController in self.childViewControllers as! [NSViewController] {
                viewController.representedObject = representedObject
            }
        }
    }
}

Трюк состоит в том, чтобы привязать representedObject к каждому из ваших нисходящих контроллеров просмотра NSArrayController в раскадровке. Вы должны привязать НЕ ТОЛЬКО contentArray НО ТАКЖЕ selectionIndexes.

Результат состоит в том, что selectionIndexes в обоих нисходящих NSArrayController сохраняются в синхронизации, потому что они связаны через центральный источник данных (подкласс DataSource в приведенном выше примере).

Чтобы сделать это все яснее, я создал примерный проект, который демонстрирует это здесь: https://github.com/acwright/StoryboardBindingsExample