У Apple есть небольшое руководство по созданию простого интерфейса master-detail. Interface Builder даже автоматически сгенерирует его для вас из объекта Core Data. Однако я пытаюсь сделать что-то более сложное, чем простой пример, и какое-то время я пытался заставить его работать.
У меня есть приложение на основе документов Core Data. Модель включает в себя абстрактный объект Page и несколько конкретных подобъектов Page. Все страницы имеют некоторые общие атрибуты (например, «имя»), и они определены в Page. Очевидно, что субсущности обладают уникальными для них атрибутами.
Я хочу, чтобы интерфейс позволял пользователю видеть все типы страниц в главном списке (NSTableView). Когда они выбирают Страницу, отображаемые поля сведений будут зависеть от типа Страницы.
Вот что у меня есть прямо сейчас:
У меня есть основной файл пера, в котором отображается основной список, а также все поля, общие для страницы. Для каждого типа страницы есть наконечник со своими специфическими полями. В основном файле пера есть основной NSArrayController, который заполняет NSTableView. В каждом из перьев, специфичных для страницы, также есть NSArrayController, так что я могу привязать поля сведений к атрибутам текущего выбора. Все мои NSArrayController настроены одинаково, и все они привязаны к одному и тому же manageObjectContext и тем же selectionIndexes.
Я использую метод Аарона Хиллегасса для обмена представлениями, который он описывает в своей книге Cocoa. Итак, я зарегистрировался для NSTableViewSelectionDidChangeNotifications, и когда я его получаю, он вызывает метод switchView:
switchView просматривает текущий выбранный объект, проверяет, к какому типу относится страница, и подкачивает соответствующий файл пера в соответствии с методом Хиллегасса.
Все работает нормально, если я добавляю только страницы одного типа, но как только я добавляю страницу второго типа, я получаю эту ошибку:
Ошибка установки значения для ключевого пути selectionIndexes объекта (из объекта связанного объекта: Страница, количество выбранных объектов: 1): [ valueForUndefinedKey:]: объект NoColPage не соответствует кодированию значения ключа для стороны ключа.
Последняя часть ошибки имеет смысл: он зависает, пытаясь отобразить неправильный кончик, поэтому он пытается выполнить привязку к полям, которые не существуют для этого объекта.
Я добавил поле selectionIndexes в MyDocument, чтобы все мои NSArrayController могли привязываться к одному и тому же месту. Я мучился над этим в течение нескольких дней, и я не могу понять это. Любые идеи?
Если это поможет, вот образец проекта, который вы можете скачать. Я извлек из своего проекта только то, что имеет отношение к этой проблеме, в новое фиктивное приложение, которое я использовал для тестирования и экспериментов.
PS: инструмент Interface Builder для создания интерфейса master-detail из объекта Core Data не работает так, как я хочу, для абстрактных объектов. Он только создает поля для атрибутов в суперсущности.
Редактировать: я думаю, что Джошуа что-то задумал, но, к сожалению, это не работает — я постоянно сталкиваюсь с одной и той же проблемой. Сначала мне было тяжело, потому что я не понимал, что -unbind: ожидает строковую константу, а не ключевой путь.
Я пробовал несколько вариантов: где я отслеживаю текущий отображаемый контроллер массива пера; где я отслеживаю отображаемый в данный момент тип страницы и только отвязываю/связываю заново, когда пытаюсь отобразить другой тип страницы...
Вот соответствующий раздел кода.
-(void) displayViewController: (ManagingVC *) vc withClass:(NSString*) className {
//Try to end editing
NSWindow *w = [box window];
BOOL ended = [w makeFirstResponder:w];
if (!ended) {
NSBeep();
return;
}
//The Managing View Controller's NSArrayController
NSArrayController* vcAc = [vc arrCont];
//if the page we're trying to switch to is NOT the same type as the current page we're displaying
//if it is, do nothing.
if (![currPageDisplayed isEqual:className]) {
//unbind the old view controller
ManagingVC *oldvc = [viewControllers objectForKey:className];
NSArrayController* oldsac = [oldvc arrCont];
[oldsac unbind:@"selectionIndexes"];
//bind the new view controller
[vcAc bind:@"selectionIndexes" toObject:self withKeyPath:@"selectionIndexes" options:nil];
currPageDisplayed = className;
NSView *v = [vc view];
//display the new view in an NSBox in the main nib
[box setContentView:v];
}
}