Отображение фиксированных и редактируемых элементов в списке источников

Фон

Я создаю список источников для своего приложения и хочу, чтобы он был структурирован аналогично iTunes с двумя типами элементов:

  • "Фиксированные" предметы - они не меняются и не могут быть перемещены - вверху
  • Редактируемые элементы внизу, которые могут быть изменены пользователем - перемещаться, переименовываться и т.д. (В примере iTunes, например, "Плейлисты" и "Смарт-плейлисты")

В моей аналогии с iTunes:

iTunes Source List
(источник: perspx.com)

До сих пор я структурировал свои данные следующим образом:

  • Элементы, которые я хочу быть редактируемыми являются "группа" элементы, в виде Group лиц Core Data.
  • Каждый элемент в списке источников представлен как обычный объект Objective-C SourceListItem так что я могу связать каждый элемент с заголовком, значком, дочерними элементами и т.д.
  • Фиксированные элементы в настоящее время представлены экземплярами SourceListItem, хранящимися в массиве в моем объекте контроллера.

Вопрос

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

Вот идеи, которые я выдвинул до сих пор:

  • Добавьте фиксированные элементы в модель базовых данных. Это означает, что я могу создать сущность, представляющую элементы списка источников, и размещать мои фиксированные и редактируемые элементы в их экземплярах. Затем их можно привязать к столбцу таблицы Outline View с помощью контроллера Array/Tree. Однако это означает, что мне нужно создать новый объект для представления элементов списка источников, а затем синхронизировать Group с этим. Мне также нужно было бы каким-то образом создать все фиксированные элементы только один раз, и если что-то случится с любым из постоянных файлов хранилища, тогда фиксированные элементы не будут отображаться.

  • Объедините фиксированные элементы с элементами группы. Хотя оба хранятся в отдельных массивах, это может быть сделано в контроллере для моего окна, когда Outline View запрашивает данные (если используется протокол NSOutlineViewDataSource, а не привязки). Однако это означает, что мне нужно будет создать новый SourceListItem для каждой группы в контроллере массива (чтобы связать каждую с иконками и другими атрибутами), сохранить их, а затем наблюдать за контроллером группового массива на предмет изменений, чтобы удалить, добавить или изменить экземпляры SourceListItem когда изменения сделаны в группах.

У кого-нибудь есть лучшие идеи о том, как я могу это реализовать?

Мне бы хотелось, чтобы мое приложение было совместимо с OS X v10.5, поэтому я бы предпочел любые решения, не зависящие от установки Snow Leopard.

Ответы

Ответ 1

Я работаю над приложением, которое имеет такое же поведение, и вот как я это делаю:

У меня есть 5 основных объектов в моей базовой модели данных:

  • AbstractItem - абстрактный объект, который имеет атрибуты, общие для всех элементов, такие как name, weight и editable. Также имеет два отношения: parent (to-one отношение к AbstractItem) и children (отношение to-many к AbstractItem и обратное к parent).
  • Group - конкретный дочерний объект AbstractItem.
  • Folder - конкретный дочерний объект AbstractItem. Добавляет отношение "многие ко многим" к основному объекту Item.
  • SmartFolder - конкретный дочерний объект Folder. Добавляет двоичный атрибут predicateData. Переопределяет атрибут отношения Folder "items" для возврата результатов выполнения запроса на выборку с предикатом, определенным атрибутом predicateData.
  • DefaultFolder - конкретный дочерний объект SmartFolder. Добавляет строковый атрибут identifier.

Для элементов раздела "Библиотека" я вставляю объекты DefaultFolder и присваиваю им уникальный идентификатор, чтобы я мог легко их извлекать и различать. Я также даю им NSPredicate, который соответствует тому, что Items они должны показывать. Например, "Музыка" DefaultFolder будет иметь предикат для извлечения всех элементов музыки, "Подкасты" DefaultFolder будет иметь предикат для извлечения всех элементов Podcast и т.д.

Элементы корневого уровня ( "Библиотека", "Общий", "Магазин", "Гениус" и т.д.) - это все Group элементы с родителем nil. Группы и папки, которые не могут быть отредактированы, имеют атрибут editable, установленный на NO.

Что касается фактического получения этого материала в вашем outlineView, вам придется реализовать протоколы NSOutlineViewDataSource и NSOutlineViewDelegate самостоятельно. Здесь слишком много поведенческой сложности, чтобы выкачать ее через NSTreeController. Тем не менее, в моем приложении, я получил все поведение (даже перетаскивание) в менее чем 200 строк кода (так что это не так уж плохо).

Ответ 2

Не вводите бессмысленность в набор данных просто для поддержки представления. Это не только противоречит шаблону проектирования MVC, но и добавляет ненужную сложность (т.е. "Больше возможностей для ошибок" ) к самой важной части: управление пользовательскими данными.

Тем не менее, использование Bindings с этим конкретным сценарием - это то, что вызывает столько трений. Почему бы не отказаться от Bindings полностью? Я думаю, что вы на правильном пути, используя протокол NSOutlineViewDataSource, но вы не сделали этого достаточно далеко. Вместо этого полностью полагайтесь на этот (по-прежнему прекрасно действующий и в некотором смысле превосходящий) протокол.

По сути, вы упрощаете упрощение настройки (и легкость уведомления об изменении) для полного контроля над древовидной структурой.