UIAppearance. Управление внешним видом iOS-приложений

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

Стиль или Суть
Сообщение или Носитель
Риторика или Диалектика

Красота — это нечто поверхностное или же идущее из глубинных истин?
Что значит «хороший дизайн»?
Эстетические суждения относительны или абсолютны?

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

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

Пользователи платят за красивые приложения.

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

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

UIAppearance позволяет единообразно управлять стилем компонентов во всем приложении.

Для реализации этого с сохранением существующей структуры UIKit специалисты Apple реализовали достаточно интересное решение: UIAppearance — это протокол, предоставляющий прокси-объект, который используется для конфигурирования объектов конкретного класса. Почему именно прокси, а не свойство или метод самого UIView? Потому что существуют объекты, не входящие в иерархию UIView, такие как UIBarButtonItem, со своим собственным представлением. Внешний вид может быть изменен у всех компонентов определенного типа или только у привязанных к специфической иерархии:

  • +appearance возвращает прокси-объект для данного класса элементов.
  • +appearanceWhenContainedIn:(Class <UIAppearanceContainer>)ContainerClass,…: возвращает прокси-объект для объектов, находящихся в определенном контейнере.

Для управления внешним видом всех объектов класса, нужно использовать прокси-объект, связанный с этим классом. Например, чтобы изменить цвет всех объектов UINavigationBar:

[[UINavigationBar appearance] setTintColor:myColor];

Для того, чтобы изменить внешний вид объектов определенного класса в заданном контейнере — используйте метод

appearanceWhenContainedIn::
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil]
setTintColor:myNavBarColor];
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], [UIPopoverController class], nil]
setTintColor:myPopoverNavBarColor];
[[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], nil]
setTintColor:myToolbarColor];
[[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], [UIPopoverController class], nil]
setTintColor:myPopoverToolbarColor];

 Определение свойств, доступных через UIAppearance

Важной проблемой работы UIAppearance через прокси является сложность определения списка методов, доступных для изменения. Поскольку +appearance возвращает id, Xcode не предоставляет соответствующую информацию при авто-подстановке.

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

$ cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS*.sdk/System/Library/Frameworks/UIKit.framework/Headers
$ grep -H UI_APPEARANCE_SELECTOR ./* |sed 's/ __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0) UI_APPEARANCE_SELECTOR;//'

UIAppearance завязан на наличие макроса UI_APPEARANCE_SELECTOR в определении метода. Любой метод с подобной пометкой может быть использован при работе через прокси.

 

Реализация UIAppearance в собственных компонентах

Подобно тому, как NSLocalizedString и #pragma являются признаками качества Objective-C кода, возможность управления внешним видом собственных UI компонентов через UIAppearance является не только рекомендуемой практикой, но также демонстрирует определенный уровень заботы создателя компонента при его реализации.

Альтернативные решения

Потенциальным недостатком UIAppearance является то, что управление интерфейсом является императивным, а не декларативным, т.е. осуществляется во время выполнения программы, а не считывается из некого списка правил.

Это идея, пришедшая из веб-разработки — разделение содержимого и его представления. Что бы не говорилось про CSS, но таблицы стилей великолепно справляются с этой задачей.

Приверженцы таблиц стилей на iOS могут выбирать из нескольких альтернатив. Pixate — коммерческий фреймворк, который использует CSS для стилизации приложений. NUI — проект с открытым кодом, делает то же при помощи CSS/SCSS-подобного языка. Другой открытый проект той же направленности UISS — позволяет настраивать UIAppearance при помощи JSON.

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

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

Не соглашайтесь на полумеры.
Делайте свои приложения прекрасными от внешнего вида до реализации.