Поделиться через


Унифицированные раскадровки в Xamarin.iOS

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

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

Классы размера

До iOS 8 разработчик использовал UIInterfaceOrientation и UIInterfaceIdiom различает книжные и альбомные режимы, а также между устройствами i Телефон и iPad. В iOS8 ориентация и устройство определяются с помощью классов размера.

Устройства определяются классами size в вертикальных и горизонтальных осях, а в iOS 8 — двумя типами классов размеров:

  • Обычный — это для большого размера экрана (например, iPad) или гаджета, который дает впечатление большого размера (например, UIScrollView
  • Компактный — это для небольших устройств (например, i Телефон). Этот размер учитывает ориентацию устройства.

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

Сетка 2 x 2, которая определяет различные возможные размеры, которые можно использовать в регулярных и компактных ориентациях.

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

Классы размера iPad

IPad из-за размера имеет обычный размер класса для обеих ориентаций.

Классы размера iPad

Классы размера i Телефон

I Телефон имеет разные классы размера на основе ориентации устройства:

Классы размера i Телефон

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

i Телефон 6 плюс классы размера

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

i Телефон 6 плюс классы размера

Так как i Телефон 6 Plus имеет достаточно большой экран, он может иметь класс регулярного размера ширины в альбомном режиме.

Поддержка нового масштабирования экрана

I Телефон 6 Plus использует новый дисплей Retina HD с коэффициентом масштабирования экрана 3,0 (три раза больше исходного разрешения экрана i Телефон). Чтобы обеспечить оптимальный интерфейс на этих устройствах, включите новые рисунки, предназначенные для этого масштаба экрана. В Xcode 6 и более поздних версиях каталоги активов могут включать изображения в 1x, 2x и 3x размера; Просто добавьте новые ресурсы образа и iOS выберет правильные ресурсы при запуске в i Телефон 6 Plus.

Поведение загрузки изображений в iOS также распознает @3x суффикс в файлах изображений. Например, если разработчик включает ресурс изображения (в разных разрешениях) в пакете приложения со следующими именами файлов: MonkeyIcon.pngMonkeyIcon@2x.pngи MonkeyIcon@3x.png. На i Телефон 6 Плюс образ MonkeyIcon@3x.png будет использоваться автоматически, если разработчик загружает образ с помощью следующего кода:

UIImage icon = UIImage.FromFile("MonkeyImage.png");

Экраны динамического запуска

Файл экрана запуска отображается как экран-заставка во время запуска приложения iOS для предоставления отзывов пользователю о том, что приложение фактически запускается. До iOS 8 разработчик должен включить несколько Default.png ресурсов изображений для каждого типа устройства, ориентации и разрешения экрана, на котором будет работать приложение.

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

Признаки

Признаки — это свойства, которые можно использовать для определения изменения макета при изменении среды. Они состоят из набора свойств (HorizontalSizeClassи на основеUIUserInterfaceSizeClass), а также идиом интерфейса (UIUserInterfaceIdiom) и VerticalSizeClass шкалы отображения.

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

Среда признака

Среды предателя — это новый интерфейс в iOS 8 и могут возвращать коллекцию предателя для следующих объектов:

  • Экраны ( UIScreens ).
  • Windows ( UIWindows ).
  • Контроллеры просмотра ( UIViewController ).
  • Представления ( UIView ).
  • Контроллер презентации ( UIPresentationController ).

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

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

Схема иерархии сред предателя

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

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

Типичные коллекции черт

В этом разделе рассматриваются типичные типы коллекций признаков, которые пользователь будет использовать при работе с iOS 8.

Ниже приведена типичная коллекция атрибутов, которую разработчик может увидеть на i Телефон:

Свойство Значение
HorizontalSizeClass Compact
VerticalSizeClass Обычное
UserInterfaceIdom Номер телефона
DisplayScale 2.0

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

Также можно иметь коллекцию атрибутов, которая отсутствует некоторые из его значений (которые Apple называется неуказанный):

Свойство Значение
HorizontalSizeClass Compact
VerticalSizeClass Не определено
UserInterfaceIdom Не определено
DisplayScale Не определено

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

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

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

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

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

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

Внешний прокси-сервер

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

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

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

UIImage

Другой класс, к которому Apple добавила коллекцию UIImageатрибутов. В прошлом разработчику пришлось указать @1X и @2x версию любого битового графического ресурса, который они будут включать в приложение (например, значок).

IOS 8 был расширен, чтобы позволить разработчику включить несколько версий образа в каталог образов на основе коллекции атрибутов. Например, разработчик может включать меньший образ для работы с классом Compact Trait и изображением полного размера для любой другой коллекции.

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

UIImageAsset

Apple добавил новый класс в iOS 8, UIImageAsset чтобы дать разработчику еще больше контроля над выбором изображений.

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

Дополнительные сведения о ресурсах изображений см. в документации Apple UIImageAsset .

Объединение коллекций признаков

Другая функция, которую разработчик может выполнять в коллекциях trait, состоит в том, чтобы добавить два вместе, что приведет к объединенной коллекции, где неопределенные значения из одной коллекции заменяются указанными значениями во втором. Это делается с помощью FromTraitsFromCollections метода UITraitCollection класса.

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

Контроллеры адаптивного представления

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

Контроллер разделенного представления

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

В iOS 8 UISplitViewController класс доступен на обеих платформах (iPad и i Телефон), что позволяет разработчику создать одну иерархию контроллера представления, которая будет работать как для i Телефон, так и для iPad.

Когда i Телефон находится в альбомном режиме, контроллер разделенного представления будет отображать свои представления параллельно, как и при отображении на iPad.

Переопределение признаков

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

Контроллер разделенного представления на iPad в альбомной ориентации

Так как iPad имеет класс регулярного размера как в горизонтальной, так и в вертикальной ориентации, представление разделения будет отображать как основные, так и подробные представления.

В i Телефон, где класс size компактен в обеих ориентациях, контроллер разделенного представления отображает только представление сведений, как показано ниже:

Контроллер разделенного представления отображает только представление сведений

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

Разработчик должен вставить родительский контейнер для контроллера разделенного представления и переопределить коллекцию атрибутов

Значение UIView задано в качестве родительского элемента контроллера разделенного представления, и SetOverrideTraitCollection метод вызывается для представления, передавающего новую коллекцию предателя и нацелив контроллер разделенного представления. Новая коллекция атрибутов переопределяет HorizontalSizeClassзначение , чтобы Regularконтроллер разделенного представления отображал как основные, так и подробные представления на i Телефон в альбомной ориентации.

Обратите внимание, что VerticalSizeClass задано значение unspecified, которое позволяет добавить новую коллекцию признаков в коллекцию признаков родительского элемента, в результате чего Compact VerticalSizeClass для дочернего контроллера разделенного представления.

Изменения признака

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

Обзор портрета для альбомной черты изменений

Во-первых, iOS 8 выполняет некоторые настройки, чтобы подготовиться к переходу. Затем система анимирует состояние перехода. Наконец, iOS 8 очищает все временные состояния, необходимые во время перехода.

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

Этап Обратный вызов Description
Настройка
  • WillTransitionToTraitCollection
  • TraitCollectionDidChange
  • Этот метод вызывается в начале изменения признака перед тем, как коллекция атрибутов получает новое значение.
  • Метод вызывается, когда значение коллекции атрибутов изменилось, но до того, как будет проходить анимация.
Анимация WillTransitionToTraitCollection Координатор перехода, который передается этому методу, имеет AnimateAlongside свойство, которое позволяет разработчику добавлять анимации, которые будут выполняться вместе с анимациями по умолчанию.
Очистка WillTransitionToTraitCollection Предоставляет метод для разработчиков, чтобы включить собственный код очистки после перехода.

Этот WillTransitionToTraitCollection метод отлично подходит для анимации контроллеров представления вместе с изменениями коллекции признаков. Метод WillTransitionToTraitCollection доступен только в контроллерах представления ( UIViewController) и не в других средах предателя, например UIViews.

Отлично TraitCollectionDidChange подходит для работы с классом UIView , где разработчик хочет обновить пользовательский интерфейс по мере изменения признаков.

Сворачивание контроллеров разделенного представления

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

  • По умолчанию контроллер разделенного представления будет использовать основной контроллер представления в качестве представления после сворачивания. Разработчик может переопределить это поведение, переопределив GetPrimaryViewControllerForCollapsingSplitViewController метод и UISplitViewControllerDelegate предоставив любой контроллер представления, который они хотят отобразить в свернутом состоянии.
  • Контроллер дополнительного представления должен объединиться с основным контроллером представления. Как правило, разработчику не нужно предпринимать никаких действий для этого шага; Контроллер разделения представлений включает автоматическую обработку этого этапа на основе аппаратного устройства. Однако в некоторых случаях разработчику потребуется взаимодействовать с этим изменением. CollapseSecondViewController Вызов метода UISplitViewControllerDelegate для контроллера главного представления может отображаться при сворачивании, а не в представлении сведений.

Расширение контроллера разделенного представления

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

  • Сначала определите новый основной контроллер представления. По умолчанию контроллер разделенного представления автоматически будет использовать основной контроллер представления из свернутого представления. Опять же, разработчик может переопределить это поведение с помощью GetPrimaryViewControllerForExpandingSplitViewController метода UISplitViewControllerDelegate .
  • После выбора основного контроллера представления необходимо повторно создать дополнительный контроллер представления. Опять же, контроллер разделения представлений включает автоматическую обработку этого этапа на основе аппаратного устройства. Разработчик может переопределить это поведение, вызвав SeparateSecondaryViewController метод объекта UISplitViewControllerDelegate .

В контроллере разделенного представления основной контроллер представления играет роль как в расширении, так и в сворачивании представлений путем реализации CollapseSecondViewController и SeparateSecondaryViewController методов.UISplitViewControllerDelegate UINavigationController реализует эти методы для автоматической отправки и всплывающих окон контроллера дополнительного представления.

Отображение контроллеров представления

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

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

  • ShowViewController — Адаптируется к отображению нового контроллера представления в зависимости от среды. Например, в UINavigationController нем просто отправляется новое представление в стек. В контроллере разделенного представления новый контроллер представления будет представлен слева в качестве нового основного контроллера представления. Если контроллер представления контейнера отсутствует, новое представление будет отображаться как модальный контроллер представления.
  • ShowDetailViewController — работает аналогично ShowViewController, но реализуется на контроллере разделенного представления, чтобы заменить представление сведений новым контроллером представления, передаваемым в. Если контроллер разделенного представления свернут (как видно в приложении i Телефон), вызов перенаправляется в ShowViewController метод, а новое представление будет отображаться как основной контроллер представления. Если контроллер представления контейнера отсутствует, новое представление будет отображаться как модальный контроллер представления.

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

Разработчики могут реализовать ShowViewController и ShowDetailViewController в собственных пользовательских контроллерах представления, чтобы получить те же автоматизированные функции, которые UINavigationController и UISplitViewController предоставляют.

Принцип работы

В этом разделе мы рассмотрим, как эти методы на самом деле реализованы в iOS 8. Сначала рассмотрим новый GetTargetForAction метод:

Новый метод GetTargetForAction

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

  1. ShowViewController Если вызывается метод, первый контроллер представления в цепочке, реализующей этот метод, является контроллером навигации, поэтому он используется в качестве родительского элемента нового представления.
  2. ShowDetailViewController Если вместо этого вызывается метод, контроллер разделенного представления является первым контроллером представления для его реализации, поэтому он используется в качестве родительского элемента.

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

Адаптивная презентация

В iOS 8 Apple сделала всплывающие презентации ( UIPopoverPresentationController) адаптивными. Таким образом, контроллер представления представления всплывающей презентации автоматически будет представлять обычное представление всплывающего окна в классе регулярного размера, но будет отображать его полный экран в горизонтально компактном классе размера (например, в i Телефон).

Для размещения изменений в единой системе раскадровки был создан новый объект контроллера для управления представленными контроллерами представлений — UIPresentationController. Этот контроллер создается с момента представления контроллера представления до его закрытия. Так как это класс управления, он может считаться суперклассом над контроллером представления, так как он реагирует на изменения устройства, влияющие на контроллер представления (например, ориентация), которые затем передаются в контроллер представления элементов управления контроллера презентации.

Когда разработчик представляет контроллер представления с помощью PresentViewController метода, управление процессом презентации передается UIKit. UIKit обрабатывает (среди прочего) правильный контроллер для создаваемого стиля, причем единственным исключением является то, что контроллер представления имеет для него стиль UIModalPresentationCustom. Здесь приложение может предоставить собственный PresentationController, а не использовать UIKit контроллер.

Пользовательские стили презентации

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

Работа с классами size

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

Хотя приложение полностью создает пользовательский интерфейс из кода, а не создание единой раскадровки с помощью построителя интерфейсов Xcode, применяются те же методы.

Теперь рассмотрим, как проект адаптивных фотографий реализует несколько функций класса size в iOS 8 для создания адаптивного приложения.

Адаптация к изменениям среды предателя

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

Контроллер разделенного представления будет отображать как главное, так и представление сведений, как показано здесь

Это достигается путем переопределения UpdateConstraintsForTraitCollection метода контроллера представления и настройки ограничений на основе значения VerticalSizeClass. Например:

public void UpdateConstraintsForTraitCollection (UITraitCollection collection)
{
    var views = NSDictionary.FromObjectsAndKeys (
        new object[] { TopLayoutGuide, ImageView, NameLabel, ConversationsLabel, PhotosLabel },
        new object[] { "topLayoutGuide", "imageView", "nameLabel", "conversationsLabel", "photosLabel" }
    );

    var newConstraints = new List<NSLayoutConstraint> ();
    if (collection.VerticalSizeClass == UIUserInterfaceSizeClass.Compact) {
        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|[imageView]-[nameLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("[imageView]-[conversationsLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("[imageView]-[photosLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:|[topLayoutGuide]-[nameLabel]-[conversationsLabel]-[photosLabel]",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:|[topLayoutGuide][imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.Add (NSLayoutConstraint.Create (ImageView, NSLayoutAttribute.Width, NSLayoutRelation.Equal,
            View, NSLayoutAttribute.Width, 0.5f, 0.0f));
    } else {
        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|[imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[nameLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[conversationsLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("|-[photosLabel]-|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));

        newConstraints.AddRange (NSLayoutConstraint.FromVisualFormat ("V:[topLayoutGuide]-[nameLabel]-[conversationsLabel]-[photosLabel]-20-[imageView]|",
            NSLayoutFormatOptions.DirectionLeadingToTrailing, null, views));
    }

    if (constraints != null)
        View.RemoveConstraints (constraints.ToArray ());

    constraints = newConstraints;
    View.AddConstraints (constraints.ToArray ());
}

Добавление анимаций перехода

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

public override void WillTransitionToTraitCollection (UITraitCollection traitCollection, IUIViewControllerTransitionCoordinator coordinator)
{
    base.WillTransitionToTraitCollection (traitCollection, coordinator);
    coordinator.AnimateAlongsideTransition ((UIViewControllerTransitionCoordinatorContext) => {
        UpdateConstraintsForTraitCollection (traitCollection);
        View.SetNeedsLayout ();
    }, (UIViewControllerTransitionCoordinatorContext) => {
    });
}

Переопределение среды признака

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

Это было сделано с помощью следующего кода в контроллере представления:

private UITraitCollection forcedTraitCollection = new UITraitCollection ();
...

public UITraitCollection ForcedTraitCollection {
    get {
        return forcedTraitCollection;
    }

    set {
        if (value != forcedTraitCollection) {
            forcedTraitCollection = value;
            UpdateForcedTraitCollection ();
        }
    }
}
...

public override void ViewWillTransitionToSize (SizeF toSize, IUIViewControllerTransitionCoordinator coordinator)
{
    ForcedTraitCollection = toSize.Width > 320.0f ?
         UITraitCollection.FromHorizontalSizeClass (UIUserInterfaceSizeClass.Regular) :
         new UITraitCollection ();

    base.ViewWillTransitionToSize (toSize, coordinator);
}

public void UpdateForcedTraitCollection ()
{
    SetOverrideTraitCollection (forcedTraitCollection, viewController);
}

Расширение и сворачивание контроллера разделенного представления

Далее рассмотрим, как в Xamarin реализовано расширение и сворачивание поведения контроллера разделенного представления. AppDelegateПри создании контроллера разделенного представления его делегат назначается для обработки этих изменений:

public class SplitViewControllerDelegate : UISplitViewControllerDelegate
{
    public override bool CollapseSecondViewController (UISplitViewController splitViewController,
        UIViewController secondaryViewController, UIViewController primaryViewController)
    {
        AAPLPhoto photo = ((CustomViewController)secondaryViewController).Aapl_containedPhoto (null);
        if (photo == null) {
            return true;
        }

        if (primaryViewController.GetType () == typeof(CustomNavigationController)) {
            var viewControllers = new List<UIViewController> ();
            foreach (var controller in ((UINavigationController)primaryViewController).ViewControllers) {
                var type = controller.GetType ();
                MethodInfo method = type.GetMethod ("Aapl_containsPhoto");

                if ((bool)method.Invoke (controller, new object[] { null })) {
                    viewControllers.Add (controller);
                }
            }

            ((UINavigationController)primaryViewController).ViewControllers = viewControllers.ToArray<UIViewController> ();
        }

        return false;
    }

    public override UIViewController SeparateSecondaryViewController (UISplitViewController splitViewController,
        UIViewController primaryViewController)
    {
        if (primaryViewController.GetType () == typeof(CustomNavigationController)) {
            foreach (var controller in ((CustomNavigationController)primaryViewController).ViewControllers) {
                var type = controller.GetType ();
                MethodInfo method = type.GetMethod ("Aapl_containedPhoto");

                if (method.Invoke (controller, new object[] { null }) != null) {
                    return null;
                }
            }
        }

        return new AAPLEmptyViewController ();
    }
}

Метод SeparateSecondaryViewController проверяет, отображается ли фотография и выполняет действия на основе этого состояния. Если фотография не отображается, она свернула дополнительный контроллер представления, чтобы отображался главный контроллер представления.

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

Перемещение между контроллерами представления

Далее давайте рассмотрим, как приложение Адаптивных фотографий перемещается между контроллерами представления. AAPLConversationViewController В классе, когда пользователь выбирает ячейку из таблицы, ShowDetailViewController вызывается метод для отображения представления сведений:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    var photo = PhotoForIndexPath (indexPath);
    var controller = new AAPLPhotoViewController ();
    controller.Photo = photo;

    int photoNumber = indexPath.Row + 1;
    int photoCount = (int)Conversation.Photos.Count;
    controller.Title = string.Format ("{0} of {1}", photoNumber, photoCount);
    ShowDetailViewController (controller, this);
}

Отображение индикаторов раскрытия информации

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

public bool Aapl_willShowingViewControllerPushWithSender ()
{
    var selector = new Selector ("Aapl_willShowingViewControllerPushWithSender");
    var target = this.GetTargetViewControllerForAction (selector, this);

    if (target != null) {
        var type = target.GetType ();
        MethodInfo method = type.GetMethod ("Aapl_willShowingDetailViewControllerPushWithSender");
        return (bool)method.Invoke (target, new object[] { });
    } else {
        return false;
    }
}

public bool Aapl_willShowingDetailViewControllerPushWithSender ()
{
    var selector = new Selector ("Aapl_willShowingDetailViewControllerPushWithSender");
    var target = this.GetTargetViewControllerForAction (selector, this);

    if (target != null) {
        var type = target.GetType ();
        MethodInfo method = type.GetMethod ("Aapl_willShowingDetailViewControllerPushWithSender");
        return (bool)method.Invoke (target, new object[] { });
    } else {
        return false;
    }
}

Они реализуются с помощью GetTargetViewControllerForAction метода, описанного выше.

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

public override void WillDisplay (UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
{
    bool pushes = ShouldShowConversationViewForIndexPath (indexPath) ?
         Aapl_willShowingViewControllerPushWithSender () :
         Aapl_willShowingDetailViewControllerPushWithSender ();

    cell.Accessory = pushes ? UITableViewCellAccessory.DisclosureIndicator : UITableViewCellAccessory.None;
    var conversation = ConversationForIndexPath (indexPath);
    cell.TextLabel.Text = conversation.Name;
}

Новый ShowDetailTargetDidChangeNotification тип

Apple добавила новый тип уведомлений для работы с классами размера и средами предателя из контроллера ShowDetailTargetDidChangeNotificationразделенного представления. Это уведомление отправляется всякий раз, когда целевое представление сведений для контроллера разделенного представления изменяется, например при развертывании или сворачивании контроллера.

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

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();
    TableView.RegisterClassForCellReuse (typeof(UITableViewCell), AAPLListTableViewControllerCellIdentifier);
    NSNotificationCenter.DefaultCenter.AddObserver (this, new Selector ("showDetailTargetDidChange:"),
        UIViewController.ShowDetailTargetDidChangeNotification, null);
    ClearsSelectionOnViewWillAppear = false;
}

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

Унифицированные раскадровки

Новые для iOS 8 унифицированные раскадровки позволяют разработчику создавать один, унифицированный файл раскадровки, который можно отобразить на устройствах i Телефон и iPad, нацелив несколько классов размеров. Используя унифицированные раскадровки, разработчик записывает менее конкретный код пользовательского интерфейса и имеет только один дизайн интерфейса для создания и обслуживания.

Ключевыми преимуществами унифицированных раскадровки являются:

  • Используйте тот же файл раскадровки для i Телефон и iPad.
  • Развертывание обратно в iOS 6 и iOS 7.
  • Предварительный просмотр макета для различных устройств, ориентации и версий ОС.

Включение классов размера

По умолчанию любой новый проект Xamarin.iOS будет использовать классы размера. Чтобы использовать классы размера и адаптивные segues внутри раскадровки из старого проекта, сначала его необходимо преобразовать в формат единой раскадровки Xcode 6, а в папке "Классы размера" проверка box выбраны в инспекторе файлов Xcode для раскадровок.

Экраны динамического запуска

Файл экрана запуска отображается как экран-заставка во время запуска приложения iOS для предоставления отзывов пользователю о том, что приложение фактически запускается. До iOS 8 разработчик должен включить несколько Default.png ресурсов изображений для каждого типа устройства, ориентации и разрешения экрана, на котором будет работать приложение. Например, , Default@2x.png, Default-Landscape@2x~ipad.pngDefault-Portrait@2x~ipad.pngи т. д.

Факторирование новых устройств i Телефон 6 и i Телефон 6 Plus (и предстоящих устройств Apple Watch) со всеми существующими устройствами i Телефон и iPad, это представляет большой массив различных размеров, ориентаций и разрешений ресурсов образа начального экрана, которые должны быть созданы и сохраненыDefault.png. Кроме того, эти файлы могут быть довольно большими и будут "blat" доставить пакет приложений, увеличивая время, необходимое для скачивания приложения из iTunes App Store (возможно, сохраняя его от возможности доставки по сотовой сети) и увеличив объем хранилища, необходимого на устройстве конечного пользователя.

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

Экраны динамического запуска имеют следующие ограничения и рекомендации.

  • Используйте только UIKit классы.
  • Используйте одно корневое представление, которое является UIView объектом или UIViewController объектом.
  • Не делайте никаких подключений к коду приложения (не добавляйте действия или точки).
  • Не добавляйте UIWebView объекты.
  • Не используйте пользовательские классы.
  • Не используйте атрибуты среды выполнения.

Учитывая приведенные выше рекомендации, давайте рассмотрим добавление динамического экрана запуска в существующий проект Xamarin iOS 8.

Выполните следующие действия.

  1. Откройте Visual Studio для Mac и загрузите решение, чтобы добавить в нее динамический экран запуска.

  2. В Обозреватель решений щелкните файл правой кнопкой мыши MainStoryboard.storyboard и выберите команду Открыть с помощью>построителя интерфейсов Xcode:

    Открытие с помощью построителя интерфейсов Xcode

  3. В Xcode выберите "Создать>файл>"...:

    Выбор файла /Создать

  4. Выберите экран запуска пользовательского интерфейса>iOS>и нажмите кнопку "Далее":

    Выбор iOS / Пользовательский интерфейс / Экран запуска

  5. Назовите файл LaunchScreen.xib и нажмите кнопку "Создать ":

    Назовите файл LaunchScreen.xib

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

    Изменение дизайна экрана запуска

  7. Сохраните изменения в LaunchScreen.xib.

  8. Выберите целевой объект приложений и вкладку "Общие ":

    Выберите целевой объект приложений и вкладку

  9. Нажмите кнопку "Выбрать info.plist", выберите Info.plist для приложения Xamarin и нажмите кнопку "Выбрать".

    Выберите info.plist для приложения Xamarin

  10. В разделе "Значки приложения" и "Запустить изображения" откройте раскрывающийся список "Файл экрана запуска" и выберите LaunchScreen.xib созданный выше:

    Выберите LaunchScreen.xib

  11. Сохраните изменения в файле и вернитесь к Visual Studio для Mac.

  12. Дождитесь завершения синхронизации изменений с Xcode Visual Studio для Mac.

  13. В Обозреватель решений щелкните правой кнопкой мыши папку "Ресурс" и выберите "Добавить>файлы..."

    Выберите

  14. LaunchScreen.xib Выберите созданный выше файл и нажмите кнопку "Открыть".

    Выберите файл LaunchScreen.xib

  15. Соберите приложение.

Тестирование экрана динамического запуска

В Visual Studio для Mac выберите симулятор i Телефон 4 Retina и запустите приложение. Экран динамического запуска будет отображаться в правильном формате и ориентации:

Экран динамического запуска, отображаемый в вертикальной ориентации

Остановите приложение в Visual Studio для Mac и выберите устройство iPad iOS 8. Запустите приложение и экран запуска будет правильно отформатирован для этого устройства и ориентации:

Экран динамического запуска, отображаемый в горизонтальной ориентации

Вернитесь к Visual Studio для Mac и остановите работу приложения.

Работа с iOS 7

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

Итоги

В этой статье описаны классы размера и их влияние на макет в устройствах i Телефон и iPad. Он обсудил, как Traits, Trait Среды и коллекции признаков работают с классами размеров для создания унифицированных интерфейсов. Он кратко рассмотрел контроллеры адаптивного представления и как они работают с классами размера внутри унифицированных интерфейсов. Он рассмотрел реализацию классов размера и унифицированных интерфейсов полностью из кода C# в приложении Xamarin iOS 8.

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