Пользовательские интерфейсы iOS: раскадровки, NIB и пользовательский код

Опубликовано: 2022-03-11

Я часто слышу, как разработчики iOS задают несколько вариантов одного и того же ключевого вопроса:

Как лучше всего разработать пользовательский интерфейс в iOS: с помощью раскадровок, NIB или кода?

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

Я считаю, что вместо этого ответ должен иметь форму одного или нескольких встречных вопросов.

Какая машина "лучшая"?

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

Вы действительно можете ответить, предложив модель или даже марку? Маловероятно, если только вы не предложите Феррари. Вместо этого вы, вероятно, ответите несколькими другими вопросами, например:

  • Каков ваш бюджет?
  • Сколько мест вам нужно?
  • Вас волнует расход топлива?
  • Как вы относитесь к спортивным автомобилям?

Очевидно, что нет такой вещи, как хорошая или плохая машина, если она не помещена в надлежащий контекст — есть просто хорошая или плохая машина в зависимости от конкретных потребностей.

Назад к дизайну пользовательского интерфейса iOS

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

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

  • iOS Storyboards : визуальный инструмент для размещения нескольких представлений приложений и переходов между ними.
  • NIB (или XIB) : каждый файл NIB соответствует одному элементу представления и может быть размещен в Интерфейсном Разработчике, что также делает его визуальным инструментом. Обратите внимание, что имя «NIB» происходит от расширения файла (ранее .nib, а теперь .xib, хотя старое произношение сохранилось).
  • Пользовательский код : т. е. никаких инструментов с графическим интерфейсом, а, скорее, программная обработка всего пользовательского позиционирования, анимации и т. д.

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

Раскадровки, например, являются последним дополнением к инструментарию пользовательского интерфейса iOS. Мне сказали, что за ними будущее, что они заменят NIB и пользовательский интерфейс с пользовательским кодом. Я считаю раскадровки полезным инструментом, но не столько заменой , сколько дополнением для NIB и пользовательского кода. Раскадровки — правильный выбор в некоторых, но не во всех ситуациях.

Это руководство по разработке для iOS призвано изучить разницу между тремя подходами к дизайну пользовательского интерфейса iOS.

Кроме того, почему вы должны статически придерживаться одного варианта, когда вы можете использовать их все (в одном проекте), выбирая механизм, который лучше всего подходит для конкретной проблемы?

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

В этом руководстве по разработке для iOS мы рассмотрим каждый из этих методов и представим варианты использования, в которых их следует и не следует использовать, а также способы их смешивания.

iOS раскадровки

Классическая ошибка новичка — создать одну огромную iOS Storyboard для всего проекта. Я тоже совершил эту ошибку, когда впервые начал работать с раскадровками (вероятно, потому что это заманчивый путь).

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

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

Например, имеет смысл использовать раскадровки при обработке:

  • Набор представлений для аутентификации и регистрации.
  • Многоэтапный поток ввода ордеров.
  • Поток, подобный мастеру (т. е. учебник).
  • Набор основных и подробных представлений (например, списки профилей, сведения о профиле).

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

Глупость больших раскадровок iOS

Большие раскадровки, кроме того, что их сложно просматривать и обслуживать, еще больше усложняют командную среду: когда несколько разработчиков одновременно работают над одним и тем же файлом раскадровки, конфликты системы управления исходным кодом неизбежны . И хотя раскадровка внутренне представлена ​​в виде текстового файла (на самом деле XML-файла), слияние обычно нетривиально.

Когда разработчики просматривают исходный код, они приписывают ему семантическое значение. Таким образом, при объединении вручную они могут читать и понимать обе стороны конфликта и действовать соответственно. Вместо этого раскадровка представляет собой XML-файл, управляемый Xcode, и значение каждой строки кода не всегда легко понять.

Возьмем очень простой пример: предположим, что два разных разработчика меняют позицию UILabel (используя autolayout), а последний подталкивает свое изменение, создавая такой конфликт (обратите внимание на конфликтующие атрибуты id ):

 <layoutGuides> <viewControllerLayoutGuide type="top"/> <viewControllerLayoutGuide type="bottom"/> </layoutGuides> <layoutGuides> <viewControllerLayoutGuide type="top"/> <viewControllerLayoutGuide type="bottom"/> </layoutGuides>

Сам id не дает никаких указаний на его истинное значение, поэтому вам не с чем работать. Единственное разумное решение состоит в том, чтобы выбрать одну из двух сторон конфликта и отказаться от другой. Будут ли побочные эффекты? Кто знает? Не вы.

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

Когда использовать раскадровки

Раскадровки лучше всего использовать с несколькими взаимосвязанными контроллерами представления, поскольку их основное упрощение заключается в переходе между контроллерами представления. В какой-то степени их можно рассматривать как композицию NIB с визуальными и функциональными потоками между контроллерами представления.

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

Помимо облегчения процесса навигации, еще одним явным преимуществом является то, что они устраняют шаблонный код, необходимый для извлечения, отправки, представления и закрытия контроллеров представления. Более того, контроллеры представления выделяются автоматически, поэтому нет необходимости вручную alloc и init .

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

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

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

Когда не следует использовать раскадровки iOS

Несколько случаев:

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

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

Общие плюсы и минусы

Теперь, когда у нас есть понимание, когда раскадровки полезны в дизайне пользовательского интерфейса iOS, и прежде чем мы перейдем к NIB в этом руководстве, давайте рассмотрим их общие преимущества и недостатки.

Плюсы: Производительность

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

Плюсы: прототипы

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

Минусы: повторное использование

Когда дело доходит до перемещения или копирования, раскадровки iOS плохо позиционируются. Раскадровка должна быть перемещена вместе со всеми зависимыми от нее контроллерами представления. Другими словами, один контроллер представления не может быть отдельно извлечен и повторно использован в другом месте как отдельный независимый объект; это зависит от остальной части раскадровки.

Против: поток данных

Данные часто необходимо передавать между контроллерами представления при переходе приложения. Однако в этом случае визуальный поток раскадровки нарушен, поскольку в интерфейсном конструкторе нет никаких следов этого. Раскадровки заботятся об обработке потока между контроллерами представления, но не потока данных. Таким образом, контроллер назначения должен быть настроен с помощью кода, перекрывающего визуальный опыт.

Раскадровки заботятся об обработке потока между контроллерами представления, но не потока данных.

В таких случаях мы должны полагаться на prepareForSegue:sender с такой структурой if/else-if:

 - (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { NSString *identifier = [segue identifier]; if ([identifier isEqualToString@"segue_name_1"]) { MyViewController *vc = (MyViewController *) [segue destinationViewController]; [vc setData:myData]; } else if ([identifier isEqualToString@"segue_name_2"]) { ... } else if ... }

Я считаю этот подход подверженным ошибкам и излишне многословным.

СИБ

NIB — это старый (более старый) способ выполнения дизайна интерфейса iOS.

В этом случае «старый» не означает «плохой», «устаревший» или «устаревший». На самом деле важно понимать, что раскадровки iOS не являются универсальной заменой NIB; в некоторых случаях они просто упрощают реализацию пользовательского интерфейса.

С помощью NIB можно спроектировать любое произвольное представление, которое разработчик может затем при необходимости прикрепить к контроллеру представления.

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

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

Когда использовать NIB для дизайна пользовательского интерфейса iOS

Подмножество всех вариантов использования будет:

  • Модальные представления
  • Простой вход и регистрация
  • Настройки
  • Всплывающие окна
  • Повторно используемые шаблоны представлений
  • Повторно используемые шаблоны ячеек таблицы

Тем временем…

Когда не следует использовать NIB

Вам следует избегать использования NIB для:

  • Представления с динамическим содержимым, где макет существенно меняется в зависимости от содержимого.
  • Представления, которые по своей природе нелегко проектировать в Интерфейсном Разработчике.
  • Просмотр контроллеров со сложными переходами, которые можно упростить с помощью раскадровки.

Общие плюсы и минусы

В общем, давайте рассмотрим плюсы и минусы использования NIB.

Плюсы: повторное использование

NIB пригодятся, когда один и тот же макет используется несколькими классами.

В качестве простого варианта использования шаблон представления, содержащий текстовое поле имени пользователя и пароля, может быть реализован с помощью гипотетических TTLoginView и TTSignupView , оба из которых могут происходить из одного и того же NIB. TTLoginView должен будет скрыть поле пароля, и оба должны будут указать соответствующие статические метки (например, «Введите свое имя пользователя» или «Введите свой пароль»), но метки будут иметь одинаковую базовую функциональность и похожие макеты.

Плюсы и минусы: производительность

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

Пользовательский код iOS (программируемые пользовательские интерфейсы)

Любой дизайн интерфейса iOS, который можно реализовать с помощью Storyboards и NIB, также можно реализовать с помощью необработанного кода (конечно, было время, когда у разработчиков не было такого богатого набора инструментов).

То, что нельзя сделать с помощью NIB и раскадровок, всегда можно реализовать с помощью кода.

Возможно, что еще более важно, то, что нельзя сделать с помощью NIB и Storyboards, всегда можно реализовать с помощью кода — при условии, конечно, что это технически осуществимо. С другой стороны, NIB и раскадровки реализованы с помощью кода, поэтому их функциональность, естественно, будет подмножеством. Давайте сразу перейдем к плюсам и минусам.

Плюсы: под капотом

Наибольшее преимущество программного создания пользовательского интерфейса iOS: если вы знаете, как кодировать пользовательский интерфейс, то вы знаете, что происходит под капотом , тогда как то же самое не обязательно верно для NIB и раскадровок.

Для сравнения: калькулятор — полезный инструмент. Но неплохо уметь выполнять расчеты вручную.

Это относится не только к iOS, но и к любому визуальному инструменту RAD (например, Visual Studio и Delphi, и это лишь некоторые из них). Среды Visual HTML RAD представляют собой типичный пограничный случай: они используются для генерации (часто плохо написанного) кода, утверждая, что знания HTML не требуются и что все можно сделать визуально. Но ни один веб-разработчик не стал бы реализовывать веб-страницу, не испачкав руки: они знают, что ручная обработка необработанного HTML и CSS приведет к более модульному и более эффективному коду.

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

Pro: когда код — единственный вариант

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

Pro: конфликты слияния

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

Минусы: прототипирование

Трудно понять, как будет выглядеть макет, пока не увидишь его в действии. Кроме того, вы не можете визуально размещать представления и элементы управления, поэтому преобразование спецификаций макета в осязаемое представление может занять гораздо больше времени по сравнению с NIB и раскадровками, которые дают вам немедленный предварительный просмотр того, как все будет отображаться.

Минусы: рефакторинг

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

Плюсы: Производительность

С точки зрения производительности раскадровки и NIB требуют дополнительных затрат на загрузку и синтаксический анализ; и, в конце концов, они косвенно переводятся в код. Излишне говорить, что это не происходит с пользовательскими интерфейсами, созданными из кода.

Плюсы: повторное использование

Любое представление, реализованное программно, может быть спроектировано повторно используемым образом. Давайте рассмотрим несколько вариантов использования:

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

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

  • Дублируйте файлы NIB и Storyboard. После этого у них отдельная жизнь и никакого отношения к исходному файлу.
  • Переопределите внешний вид и поведение с помощью кода, который может работать в простых случаях, но может привести к значительным осложнениям в других. Тяжелые переопределения с кодом также могут сделать визуальный дизайн бесполезным и превратиться в постоянный источник головной боли, например, когда определенный элемент управления отображается одним способом в построителе интерфейсов, но выглядит совершенно иначе, когда приложение запущено.

Когда использовать код

Часто полезно использовать собственный код для дизайна пользовательского интерфейса iOS, если у вас есть:

  • Динамические макеты.
  • Виды с эффектами, такими как закругленные углы, тени и т.д.
  • Любой случай, когда использование NIB и раскадровок сложно или невозможно.

Когда не использовать код

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

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

Один проект, несколько инструментов

Раскадровки, NIB и код — это три разных инструмента для создания пользовательского интерфейса iOS. Нам повезло с ними. Фанатики программируемых пользовательских интерфейсов, вероятно, не примут во внимание два других варианта: код позволяет вам делать все, что технически возможно, тогда как альтернативы имеют свои ограничения. Для остальных разработчиков армейский нож Xcode предоставляет три инструмента, которые можно эффективно использовать одновременно в одном проекте.

Как, спросите вы? Как вам нравится. Вот несколько возможных подходов:

  • Сгруппируйте все связанные экраны в отдельные группы и реализуйте каждую группу с собственной отдельной раскадровкой.
  • Создавайте одноразовые ячейки таблицы на месте с помощью раскадровки внутри контроллера табличного представления.
  • Создавайте многократно используемые ячейки таблицы в NIB, чтобы поощрять повторное использование и избегать повторений, но загружайте эти NIB через пользовательский код.
  • Создавайте собственные представления, элементы управления и промежуточные объекты с помощью NIB.
  • Используйте код для высокодинамичных представлений и, в более общем случае, для представлений, которые нелегко реализовать с помощью раскадровок и NIB, в то же время размещая переходы представлений в раскадровке.

В завершение давайте посмотрим на последний пример, который связывает все это вместе.

Простой вариант использования

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

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

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

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

Для реализации этого iOS-приложения пригодятся все три наших инструмента пользовательского интерфейса, поскольку мы можем использовать:

  • Раскадровка с четырьмя контроллерами представления (список, сведения, список сообщений и форма нового сообщения).
  • Отдельный файл NIB для многократно используемого шаблона ячейки списка профилей.
  • Три отдельных файла NIB для просмотра сведений о профиле, по одному для каждого из отдельных разделов, которые его составляют (сведения о профиле, статистика, последние три сообщения), для обеспечения лучшей ремонтопригодности. Эти NIB будут созданы как представления, а затем добавлены в контроллер представления.
  • Пользовательский код для просмотра облака тегов. Это представление является типичным примером того, что не может быть спроектировано в Интерфейсном Разработчике, ни через StoryBoards, ни через NIB. Вместо этого он полностью реализован через код. Чтобы сохранить визуальный поток раскадровки, мы решили добавить пустой контроллер представления в раскадровку, реализовать представление облака тегов как автономное представление и программно добавить представление к контроллеру представления. Очевидно, что представление также можно реализовать внутри контроллера представления, а не как отдельное представление, но мы разделяем их для лучшего повторного использования.

Действительно простой макет может выглядеть так:

На этой диаграмме показан один проект разработки пользовательского интерфейса iOS, в котором используются раскадровки, NIB и пользовательский код iOS.

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

Подведение итогов

Как показано в этом руководстве, раскадровки заметно упрощают дизайн пользовательского интерфейса iOS и визуальный поток. Они также исключают шаблонный код; но за все это приходится платить гибкостью. Между тем NIB предлагают большую гибкость, фокусируясь на одном представлении, но без визуального потока. Наиболее гибким решением, конечно же, является код, который имеет тенденцию быть довольно недружественным и по своей сути невизуальным.

Если эта статья вас заинтриговала, я настоятельно рекомендую посмотреть отличные дебаты от Рэя Вендерлиха, 55 минут, потраченных на обсуждение NIB, раскадровок и кодовых UIS.

В заключение я хочу подчеркнуть одну вещь: любой ценой избегайте использования неподходящего инструмента для разработки пользовательского интерфейса iOS . Если представление нельзя разработать с помощью раскадровки или если его можно реализовать с помощью NIB или кода более простым способом, не используйте раскадровку. Точно так же, если представление нельзя спроектировать с помощью NIB, не используйте NIB. Эти простые правила помогут вам в обучении разработчика.