Статические таблицы с раскадровкой

Определение раскадровки Edgesforextendedlayout.

Когда я впервые услышал, что iOS 5 представила концепцию статического табличного представления, которое может быть разработано в Interface Builder, я был разочарован, увидев, что оно фактически привязано к раскадровке. Я ничего не имею против раскадровок, это интересное нововведение, которое (потенциально) может сэкономить много кода. Однако, если вы еще не готовы полностью адаптировать раскадровки для дизайна пользовательского интерфейса, вам может быть отказано в изучении новых функций, которые они привносят. В этом посте я хочу изучить то, как можно использовать минимальную реализацию раскадровки для быстрого создания статического табличного представления без необходимости полностью переписывать существующее приложение..

Разработка таблицы настроек.

Реализация таблицы параметров — очевидное применение статических табличных представлений в приложении. Чтобы проиллюстрировать, насколько это просто, я начну с шаблона Xcode для приложения с вкладками. Обратите внимание, что я не выбрал вариант использования раскадровки, чтобы основной интерфейс по-прежнему создавался с использованием традиционного подхода Nib. (Тем не менее, я использую ARC, поэтому обратите внимание, если вы копируете и вставляете код в приложение, не использующее ARC). Шаблон дает нам контроллер панели вкладок, содержащий два контроллера представления, загруженные из отдельных файлов пера. Второй контроллер представления — это то, что мы заменим раскадровкой для реализации представлений настроек нашего приложения..

Для начала я собираюсь создать раскадровку и разметить несколько видов таблиц. Чтобы сделать этот пост кратким, я собираюсь ограничить его макетом iPhone, но этот принцип в равной степени применим к iPad или универсальному приложению. Чтобы добавить раскадровку, создайте новый файл и выберите раскадровку из шаблонов пользовательского интерфейса, убедитесь, что семейство устройств установлено на iPhone, и назовите файл Settings:

Первый контроллер табличного представления, который я собираюсь добавить в раскадровку, будет представлять меню настроек верхнего уровня, позволяющее мне группировать простые и расширенные настройки, к которым я буду обращаться в отдельных представлениях. Когда я закончу, таблица должна выглядеть так:

По умолчанию, когда вы перетаскиваете контроллер табличного представления в раскадровку, вы получаете динамическое табличное представление. Вы можете изменить это, выбрав Статические ячейки в Инспекторе атрибутов:

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

Чтобы получить желаемый макет, основные шаги следующие:

измените стиль представления таблицы с Plain на Grouped и удалите две строки, чтобы оставить одну строку (вы можете изменить количество строк с помощью Inspector, но для удаления строки быстрее просто щелкните строки в таблице и нажмите клавишу удаления) перетащите объект метки в ячейку табличного представления, отрегулируйте форматирование по своему усмотрению и установите для текстовой метки значение Общие с выбранным разделом табличного представления либо в навигаторе документа, либо на панели перехода, используйте Инспектор атрибутов, чтобы установить верхний и нижний колонтитулы раздела (здесь также можно настроить количество строк в разделе):

Чтобы добавить второй раздел, убедитесь, что в навигаторе документа выбрано табличное представление, а затем измените количество разделов в инспекторе атрибутов. Когда добавляется новый раздел, он копируется из первого раздела, что позволяет сэкономить время. Чтобы завершить макет, нам просто нужно изменить текст заголовка, нижнего колонтитула и метки..

Теперь у нас есть наше начальное табличное представление настроек, и мы можем повторить упражнение, чтобы добавить наши подробные таблицы настроек, но прежде, чем мы это сделаем, нам нужно встроить наш контроллер табличного представления в контроллер навигации для обработки перемещения между различными таблицами. Это простой шаг с помощью встроенного > Параметр «Контроллер навигации» из меню редактора Xcode. После добавления контроллера навигации мы также можем установить заголовок на панели навигации для нашего контроллера табличного представления настроек. На этом этапе раскадровка должна выглядеть следующим образом:

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

На этом этапе нам нужно добавить переходы от контроллера представления таблицы параметров к каждому из контроллеров представления подробных параметров. Редактор раскадровки называет каждое из представлений сценами, а переходы между сценами — сегментами. В этом случае нам нужны два сегмента: первый из ячейки табличного представления «Общие», чтобы перейти к табличному представлению «Общие параметры», а второй — из ячейки табличного представления «Дополнительные параметры» в табличное представление «Дополнительные параметры». Чтобы создать переходы, вам нужно управлять щелчком мыши по ячейке представления исходной таблицы, а затем перетащить ее в представление таблицы назначения. Когда вы отпускаете кнопку мыши, появляется всплывающее меню, позволяющее выбрать стиль перехода к раскадровке. Поскольку мы используем контроллер навигации, мы будем использовать команду push segue.

Теперь, когда наши новые контроллеры представлений включены в иерархию навигации, мы можем установить заголовок для каждого представления, набрав его непосредственно на панели навигации каждого представления, чтобы наша раскадровка теперь выглядела следующим образом (Interface Builder сделает вывод, когда ему нужно добавить навигацию и панели вкладок для просмотра):

Загрузка раскадровки.

Если бы мы использовали раскадровку для основного пользовательского интерфейса, мы могли бы указать имя файла в ключе UIMainStoryboardFile в файле Info.plist приложения, и оно загрузилось бы автоматически. Поскольку в этом примере мы хотим более ограниченно использовать раскадровки, нам нужно вручную загрузить и запустить наш файл раскадровки. Мы можем сделать это, изменив нашего делегата приложения для использования раскадровки при загрузке контроллера представления для второго элемента панели вкладок. Соответствующий код в методе application: didFinishLaunchingWithOptions выглядит следующим образом:

Строки 2 и 3 являются важными, метод класса UIStoryboard storyBoardWithName: bundle: используется для загрузки файла раскадровки. Нет необходимости указывать пакет, если раскадровка содержится в основном пакете приложения. Затем метод instantiateInitialViewController выделяет и инициализирует наш корневой контроллер представления навигации, который мы затем можем добавить к контроллеру представления панели вкладок вместе с первым контроллером представления, который был создан из его файла пера..

Теперь, если мы запускаем приложение в симуляторе много раз, табличное представление настроек уже работает, включая включение и отключение контроллеров подробных представлений в стеке контроллеров навигации. Пока что это выглядит как настоящая победа для раскадровки, поскольку у нас есть много функций, разработанных в Interface Builder, без необходимости писать какой-либо шаблонный код контроллера представления. Поскольку наш контроллер представления таблицы исходных настроек действует только как меню верхнего уровня, кажется, что нам даже не нужно реализовывать для него настраиваемый подкласс (я говорю «появляется», поскольку мы вскоре увидим, что есть одна проблема, с которой нам нужно справиться. что заставляет нас создавать общий подкласс табличного представления).

Реализация выбора строки.

Чтобы на самом деле сделать наши экраны настроек полезными, нам нужно реализовать контроллеры представления, чтобы мы могли взаимодействовать с пользовательским интерфейсом, а также читать и записывать настройки. Экран общих настроек (показанный ниже) самый простой, поэтому мы начнем с добавления подкласса UITableViewController с именем UYLGeneralSettingsTableViewController..

Открытый интерфейс для этого класса пуст:

Мы добавим два свойства к расширению частного класса, чтобы представить два параметра (скорость и объем), которыми управляет это представление:

Теперь нам нужно реализовать достаточно контроллера табличного представления, чтобы мы могли настроить состояние представления при его первой загрузке и позволить нам изменять выбранную строку в каждом из двух разделов. Прежде чем я покажу код, который я добавил, важно отметить код, который мне НЕ нужно реализовывать:

протокол UITableViewDataSource имеет два обязательных метода и ряд дополнительных методов, которые теперь в значительной степени избыточны. Из двух обычно требуемых методов я в этом случае реализую tableView: cellForRowAtIndexPath: (чтобы установить галочку для выбранной строки), но это может не всегда требоваться. Нет необходимости реализовывать другой требуемый метод tableView: numberOfRowsInSection: или numberOfSectionsInTableView:, поскольку размеры таблицы статической таблицы были установлены в InterfaceBuilder. Нам не нужно реализовывать tableView: titleForHeaderInSection: или tableView: titleForFooterInSection:, поскольку мы напрямую устанавливаем верхний и нижний колонтитулы в раскадровке..

Чтобы инициализировать представление, мы реализуем метод viewDidLoad для чтения наших пользовательских значений по умолчанию из файла..

Теперь, чтобы фактически установить галочку в текущей выбранной строке, мы реализуем метод протокола UITableViewDataSource tableView: cellForRowAtIndexPath:

Обычно первое, что вы делаете в tableView: cellForRowAtIndexPath, — это попытка исключить ячейку из очереди для повторного использования, если она недоступна, выделить и инициализировать новый UITableViewCell. Однако в данном случае это не применимо, поскольку это статическая таблица. Все ячейки табличного представления, которые нам нужны, были созданы в Interface Builder и создаются, когда раскадровка загружает контроллер представления. Кстати, об этом стоит подумать, если вы пытаетесь использовать статические табличные представления для создания огромных табличных представлений. Необходимость выделения всех ячеек статического табличного представления при первой загрузке представления может быть как медленной, так и вызвать проблемы с памятью. Если вы обнаружите, что создаете статическое табличное представление с более чем одним или двумя экранами информации, вы, вероятно, захотите вернуться к использованию динамического табличного представления..

Несмотря на то, что нам не нужно выделять ячейки табличного представления для статического табличного представления, мы все равно можем получить ссылку на каждую ячейку, чтобы мы могли настраивать ячейку. Есть несколько способов сделать это, включая объявление выхода для каждой ячейки представления таблицы в Интерфейсном Разработчике, который мы увидим в контроллере представления расширенных настроек. В этом случае нам просто нужно установить тип аксессуара ячейки, чтобы мы могли вызвать tableView: cellForRowAtIndexPath в суперклассе, чтобы вернуть нам текущую ячейку. Когда у нас есть текущая ячейка, мы можем установить или очистить accessoryType на основе текущего значения свойств скорости или объема..

Нам также необходимо реализовать метод UITableViewDelegate для обработки выбора строки (см. Ниже). В этом методе нет ничего примечательного, поэтому я не буду вдаваться в подробности. Метод в основном просто обновляет пользовательские значения по умолчанию в зависимости от того, какая строка выбрана, а затем запрашивает перезагрузку табличного представления, чтобы установить флажки..

Наконец, нам нужно вернуться к Interface Builder и с помощью инспектора удостоверений изменить класс универсального UITableViewController на наш настраиваемый подкласс UYLGeneralSettingsTableViewController:

Если вы повторно запустите приложение в симуляторе, экран общих настроек теперь должен быть полностью функциональным..

Подключение к ячейкам статического табличного представления.

Экран расширенных настроек на первый взгляд выглядит немного сложнее, поскольку у нас есть ряд переключателей и шаговых элементов управления, с которыми нам нужно взаимодействовать. Фактически, хотя в Интерфейсном Разработчике требуется немного усилий, чтобы связать все, на самом деле требуется еще меньше кода. Как и раньше, мы начнем с добавления в проект подкласса UITableViewController и назовем его UYLAdvancedSettingsViewController:

Чтобы иметь возможность взаимодействовать с содержимым ячеек статического табличного представления, нам нужно определить некоторые выходы, которые позволят нам установить различные элементы и методы действий для элементов управления переключателем и шаговым двигателем. Хорошее место для этого — расширение частного класса нашего настраиваемого подкласса:

Этот проект скомпилирован с использованием ARC, поэтому мы следуем рекомендациям Apple и делаем слабые ссылки на все наши свойства IBOutlet. Теперь мы используем Interface Builder для подключения выходов к соответствующей метке или элементу управления в представлении статической таблицы. Для этого нам сначала нужно использовать инспектор удостоверений, чтобы изменить класс представления с универсального UITableViewController на наш настраиваемый UYLAdvancedSettingsViewController. Я не буду проводить вас через подключение каждого элемента пользовательского интерфейса, так как это стандартный материал Interface Builder, а просто для справки иерархия представлений и соединения воспроизводятся ниже:

Чтобы определить, какой переключатель или шаговый двигатель запускает действие, каждый из них имеет уникальный тег в построителе интерфейса..

Теперь, когда наши розетки подключены, в viewDidLoad очень просто установить начальное состояние переключателей, шаговых двигателей и меток:

Обратите внимание, что нам не нужно реализовывать tableView: cellForRowAtIndexPath: чтобы получить ссылку на ячейку, поскольку у нас есть прямой доступ к каждой ячейке через выходы. Фактически нам не нужно реализовывать какие-либо методы UITableViewDataSource или UITableViewDelegate. Два метода действий для обработки переключателя и действия шагового двигателя довольно похожи, поэтому я просто покажу здесь метод для переключателя:

Обработка изменений ориентации.

Перед тем как закончить, мне нужно решить одну небольшую проблему. В настоящее время существует проблема с тем, как мы реагируем на ротацию устройства. Фактически, если вы создадите и запустите приложение, вы увидите, что оно не реагирует на изменения ориентации устройства. Общее правило для контроллера панели вкладок состоит в том, что все контроллеры представления на каждой вкладке должны разрешать вращение, иначе ничто не вращается. Я не показывал это, но каждый из реализованных нами контроллеров представления, включая контроллер представления, используемый для первого элемента панели вкладок, реализует shouldAutorotateToInterfaceOrientation: чтобы вернуть YES для всех ориентаций:

К сожалению, есть один контроллер представления, контроллер представления таблицы настроек верхнего уровня, который нам мешает. Поскольку нам не нужно было взаимодействовать с таблицей, мы никогда не реализовывали наш собственный подкласс для этого контроллера табличного представления. Это означает, что мы не можем переопределить метод ориентации, поэтому мы получаем поведение по умолчанию:

По умолчанию этот метод возвращает YES только для ориентации UIInterfaceOrientationPortrait. Если ваш контроллер представления поддерживает дополнительные ориентации, переопределите этот метод и верните YES для всех ориентаций, которые он поддерживает..

Чтобы решить эту проблему, я добавил новый подкласс UITableViewController с именем UYLRotatingTableViewController, который ничего не делает, кроме как реализует метод shouldAutorotateToInterfaceOrientation. Использование Инспектора удостоверений для изменения класса контроллера представления таблицы параметров на этот новый подкласс устраняет проблему. Окончательная раскадровка выглядит следующим образом:

Заключение.

Одна из критических замечаний, которые я слышал в отношении раскадровок, заключается в том, что, если ваше приложение не очень простое, они не сохранят вам много кода. Это потому, что вам все еще нужно обрабатывать поток данных между контроллерами представления. Обычно это включает реализацию prepareForSeque: Sender: для передачи данных в новый контроллер представления и реализацию протокола делегата для передачи данных обратно родительскому или представляющему контроллеру представления..

В этом примере мы очень ограниченно используем раскадровки, и нет реального потока данных между различными контроллерами статических таблиц. Это означает, что нет необходимости реализовывать prepareForSegue: sender: или создавать делегаты контроллера представления, поэтому требуется значительное сокращение объема необходимого кода. Вы можете сами решить, насколько важны раскадровки и когда они могут быть уместными. Лично мне нравится возможность выборочно добавлять их в приложение для определенных частей интерфейса, а статические табличные представления — определенно одна из областей, где я буду их использовать..

Теперь заархивированная версия проекта Xcode, которая сопровождает этот пост, может быть найдена в моем репозитории Github CodeExamples..

Похожие статьи