Дополнение к Лекции 14 Stanford CS 193P iOS 7. Адаптивные SplitViewController и Popover в приложении Photomania для iOS 9. (Objective-C).

Screen Shot 2015-11-26 at 6.05.00 PM

Screen Shot 2015-11-26 at 6.05.13 PM

С незапамятных времен Split View Controller был доступен только на  iPad. Начиная с  iOS 8, он теперь работает и на iPad, и на iPhone.

Это произошло потому, что в iOS 8 Apple представила, а в iOS 9 развивает дальше адаптивный пользовательский интерфейс (UI),  который включает в себя использование  Size Classes и адаптивную разметку Auto Layout. Согласно новой философии, пользовательский интерфейс может достаточно быстро настраиваться для любого типа прибора в зависимости от того, какой Size Class (класс размера) имеет его экран.

Адаптивный вариант UI для  Photomania Universal URL, функционирующий в iOS 9, находится на  Github.  Давайте посмотрим, как он был создан.

Для классификации различных приборов в iOS 8 было введено понятие Size classes. Они заменяют как UIInterfaceOrientation, так и  UIUserInterfaceIdiom. Всего четыре  size classes:

  • Horizontal Regular
  • Horizontal Compact
  • Vertical Regular
  • Vertical Compact

Ваш View Controller всегда существует в среде Size class с определенной шириной (width) и высотой ( height). В настоящий момент size class может быть либо Compact, либо Regular

Screen Shot 2015-11-20 at 10.53.24 PMПолноразмерные View Controllers on iPad всегда являются Regular по обоим направлениям (горизонтальному и вертикальному). На всех iPhones перед появлением iPhone 6+ и  iPhone 6s+, горизонтальный размер всегда был Compact (и в портретном, и в ландшафтном режимах), а вертикальный размер был Regular в портретном режиме и Compact в  ландшафтном режиме. С появлением больших  iPhones  6+ и iPhones  6s+ у них,  в отличие от других iPhones, ширина стала  Regular в ландшафтном режиме, что и дало основание распространить применение Split View Controller на  iPhones  6+ и iPhones  6s+ в ландшафтном режиме.

Будет не очень удобно, если я буду для всех своих приложений создавать различные интерфейсы для всех 4-х ситуаций.  Поэтому, фактически, в Xcode у нас есть еще один Size Class, называемый Ayn (wAny hAny), который мы используем при проектировании универсального приложения в  Xcode 7.

Screen Shot 2015-11-22 at 8.14.50 PM

В iOS 7 нам приходилось при создании универсального приложения  cоздавать два совершенно разных интерфейса, используя совершенно разные типы Controllers ( ниже представлены storyboards приложения Photomania Universal URL, спроектированные в iOS 7):
Split View Controller на iPad
Main_iPad.storyboard
Screen Shot 2015-11-19 at 9.29.11 PM
Navigation Controller на iPhone
Main_iPhone.storyboard
Screen Shot 2015-11-19 at 9.48.33 PM
В iOS 8 Split View Controller становится адаптивным. Это означает, что единственный Split View Controller может управлять двумя этими архитектурами для двух типов приборов.

Для получения адаптивного интерфейса нужно выбрать файл storyboard и указать в Инспекторе Файла режим Size Class и Auto Layout:

Screen Shot 2015-11-21 at 3.08.55 PM
И наш storyboard  теперь будет соответствовать адаптивному интерфейсу.

Screen Shot 2015-11-21 at 5.03.35 PM
Адаптивная storyboard очень похожа на storyboard для iPad в iOS 7, за исключением двух вещей:

  1. размеры всех экранных фрагментов одинаковые и соответствуют универсальному в Xcode классу wAny hAny,
  2. появился новый адаптивный segue  типа Show Detail Segue, который помогает  Split View Controller осуществлять сразу две функции: “скольжение” как в  Navigation Controller  и «переезд» к  Detail как это принято в Split View Controller.

Нам придется внести небольшие поправки в стандартное адаптивном функционирование Split View Controller для придания ему привычного нам вида. Чтобы понять необходимость этих поправок, будем экспериментировать не с громоздким приложения  Photomania, а с очень простым приложением, представляющим списком стран (Master). При выборе определенной страны приложение показывает ее флаг (Detail). Окончательный вариант простейшего приложения SplitViewCountry находится на Github. Но мы будем создавать его с «чистого листа».

Открываем новый проект для универсального приложения для  Objective-C и называем его SplitViewCountry

Screen Shot 2015-11-24 at 11.22.30 AM
и используем шаблон Single View Application ( именно этот шаблон, а не Master-Detail Application, который мы будем исследовать позже). Будем проектировать наше приложение с «чистого листа». Поэтому уберем со storyboard единственный экранный фрагмент View Controller и оставим storyboard совершенно пустой. Удалим также файлы ViewController.h и ViewController.m.

Перетянем из палитры объектов на storyboard Split View Controller
Screen Shot 2015-11-24 at 12.28.24 PM
Устанавливаем Split View Controller в качестве стартового экранного фрагмента.

Screen Shot 2015-11-24 at 1.12.21 PM
Добавляем два новых класса SelectCountryTableViewController (наследует от UITableViewController) и CountryViewController  (наследует от обычного UIViewController) в наш проект для Master и Detail. Устанавливаем эти классы на storyboard.
Master представляет собой таблицу стран.
Screen Shot 2015-11-24 at 3.41.09 PM

Detail показывает флаг определенной страны.
Screen Shot 2015-11-24 at 3.41.32 PM
На Detail добавлена метка, которая центрируется по горизонтали и вертикали с помощью системы Autolayout. Эта метка будет показывать флаг выбранной страны, а в случае, если страна не задана, будет размещаться такой неопределенный флаг.

Для Master  используется класс SelectCountryTableViewController, который содержит определение Модели (это просто массив стран) и реализацию методов делегата  Data Source для TableView.
SelectCountryTableViewController.m
Screen Shot 2015-11-24 at 5.26.17 PM
Для Detail  используется класс ContryViewController. Он показывает метку с флагом страны, которая устанавливается извне  с помощью Модели country.
ContryViewController.h
Screen Shot 2015-11-24 at 5.18.51 PM
ContryViewController.m
Screen Shot 2015-11-24 at 5.23.21 PM
Связь Master и Detail (то есть передача страны country для показа флага) осуществляется классическим для  Split View Controller способом с помощью метода делегата Table View.
SelectCountryTableViewController.m
Screen Shot 2015-11-24 at 5.35.04 PM
Напоминаем, что storyboard осталась почти неизменной по сравнению с той, которую мы вытянули из Палитры Объектов, никакие элементы пользовательского интерфейса и segues не добавлялись, были изменены только пользовательские классы для Master и Detail
Screen Shot 2015-11-24 at 5.44.31 PM
Запускаем приложение на iPad в портретном режиме
Screen Shot 2015-11-24 at 6.53.47 PM
Появляется  практически пустой экран с изображением флага отсутствующей страны на Detail,  и даже непонятно, что делать. Пользователь должен каким-то магическим способом догадаться, что работает жест swipe, который и покажет нам Master, то есть экранный фрагмент со списком стран. Дальше можно выбирать страну и экран с флагом будет автоматически обновляться. Все работает, но отсутствует заголовок Detail и нет возвратной кнопки в Master.
Запускаем приложение на iPad в ландшафтном режиме.
Screen Shot 2015-11-24 at 7.05.25 PM
Все работает. При изменении страны флаги обновляются.

Запускаем приложение на iPhone 6+ в портретном режиме
Screen Shot 2015-11-24 at 7.12.22 PM

Появляется  почти такой же экран, как и в случае с iPad с изображением флага отсутствующей страны на Detail,  но есть возвратная кнопка, призывающая нас выбрать страну. Нажимаем на эту кнопку, и действительно попадаем в Master, то есть экранный фрагмент со списком стран. Дальше можно выбирать страну и ничего не происходит. Этот режим не работает, так как нам не удается достичь  Detail .

Переходим в ландшафтный режим на  iPhone 6+

Screen Shot 2015-11-24 at 7.21.00 PM

Все работает. Флаги обновляются.
Какой можно сделать вывод из этих экспериментов?

Если прибор имеет Size ClassRegular (iPad в портретном и ландшафтном режимах и iPhone 6+ в ландшафтном режиме), то все работает, то есть происходит обновление флагов при изменении страны. При этом на экране одновременно находятся и Master, и Detail. Этот режим назван expanded для адаптивного Split View Controller.

Если прибор имеет Size Class — Compact ( iPhone 6+ в портретном режиме, все другие iPhones в портретном и ландшафтном режимах), то ничего не работает, то есть не только не происходит обновление флагов при изменении страны, но мы вообще не попадает на Detail. В случае  Compact Size Class на экране находится только один MVC: либо Master, либо Detail, поэтому этот режим назван collapsed для адаптивного Split View Controller.

Причем, если мы включим отладочный режим для режима  collapsed, то увидим, что в методе didSelectRowAtIndexPath: делегата UITableViewDelegateDetail определяется как UINavigationController, но в стэке у него только SelectCountryTableViewController. Нужно как-то поместить в  UINavigationController стэк CountryViewController, тогда удастся достичь Detail. 

Screen Shot 2015-11-24 at 7.59.07 PM

Такую работу и даже больше, выполняет новый segue типа Show Detail. В скобках на картинке, представленной ниже, в Инспекторе Атрибутов дается уточнение, что это Replace segue, а это означает, что Detail этого Split View Controller будет замещаться новым экземпляром MVC. Это очень важно для дальнейшего понимания.

Давайте разместим segue типа Show Detail на нашей storyboard и назовем его «Show flag».

Screen Shot 2015-11-24 at 8.18.43 PM

Как и всякий другой segue, этот segue нуждается в подготовке, которая должна проводится с учетом того, что destinationViewController для этого segue будет разным в зависимости от  Size Class : для Regular — это CountryViewController, а для  Compact — это UINavigationController, в стэке которого на самом верху находится CountryViewController.

Screen Shot 2015-11-24 at 9.25.35 PM

Нам придется внести некоторые изменения в CountryViewController, так как при использовании segue происходит полная замена Detail новым MVC, а prepareForSegue работает до полной загрузки CountryViewController и установка нового значения Модели может не обновить до конца пользовательский интерфейс из-за того, что  некоторые outlets еще не установлены. Поэтому нам нужно сделать обновление UI с помощью функции updateUI и в случае установки нового значения Модели country, и при загрузке CountryViewController:

Screen Shot 2015-11-24 at 9.46.44 PM

Запускаем приложение на iPhone 6+ в портретном режиме (раньше в этом режиме приложение не работало).

Screen Shot 2015-11-24 at 9.57.31 PM

Также как и раньше, перед нами  появляется экран с изображением флага отсутствующей страны на Detail и возвратной кнопкой, призывающей нас выбрать страну. Нажимаем на эту кнопку, и действительно попадаем в экранный фрагмент со списком стран. Дальше можно выбирать страну, и мы получаем флаг этой страны. Теперь этот режим работает. Но мало этого, мы получили заголовок (страну) на Detail, который появляется для Compact Size Class благодаря действию механизма стэка для Navigation Controller. Для Regular Size Class заголовок на Detai, по-прежнему отсутствует, так как там работает «родной» механизм Split View Controller.

Таким образом, благодаря наличие  Navigation Controller со стороны Master и Show Detail segue мы получили работающий адаптивный  Split View Controller. Он работает как на iPad, так и на любых типах iPhone. На Regular экранах он работает в режиме expanded (Master и Detail одновременно на экране), а на Compact экранах он работает в режиме collapsed (только один View Controller на экране: либо Master, либо Detail ).

Мы получили одну storyboard, которая работает на обоих платформах (iPhone и iPad) и автоматически адаптируется.

Однако хочется иметь привычный заголовок на Detail и для Regular Size Class (iPad в портретном и ландшафтном режимах и iPhone 6+ в ландшафтном режиме). Мы знаем как этого добиться — нужно вставить Detail в Navigation Controller.
Screen Shot 2015-11-25 at 12.08.30 PM
Теперь заголовок для Regular Size Class появился на навигационной панели
Screen Shot 2015-11-25 at 12.21.09 PM

Кроме заголовка на навигационной панели можно разместить возвратную кнопку и специальную кнопку смены режимов работы Split View Controller.

Для этого необходимо в методе prepareForSegue подготовки  segue типа Show Detail разместить кнопки на навигационной панели в качестве левой кнопки навигационной панели
SelectCountryTableViewController.m
Screen Shot 2015-11-25 at 12.57.56 PM

Для того, чтобы эти кнопки действовали при старте приложения аналогичный код нужно добавить в AppDelegate.m
Screen Shot 2015-11-25 at 6.45.52 PM

В результате мы имеем необходимую возвратную кнопку для iPad в портретном режиме и кнопку переключения режимов для iPhone 6+ в ландшафтном режиме.
Screen Shot 2015-11-25 at 6.52.44 PM

Теперь работу Split View Controller для Regular Size Class можно считать удовлетворительной.

Что нас не устраивает в работе Compact Size Class? На iPhones работа должна сразу начинаться с показа Master и дальше двигаться последовательно в сторону Detail c помощью механизма Navigation Controller. Это обеспечивается методами делегата SplitViewControllerDelegate, один из которых мы сейчас реализуем в AppDelegate. Вначале мы подтверждаем протокол UISplitViewControllerDelegate:

AppDelegate.m
Screen Shot 2015-11-25 at 7.28.43 PM
И, наконец, реализуем метод collapseSecondaryViewController:ontoPrimaryViewController: делегата UISplitViewControllerDelegate, который срабатывает при переходе в collapsed режим, когда на экране должен остаться только один View Controller, и он спрашивает нас, нужно ли отбросить Detail. Если мы отвечаем YES, то на экране в collapsed режиме остается только Master, если NO — то Detail. Мы хотим иметь Master только при старте, то есть  когда Detail — это Navigation Controller,  его топовым View Controller в стэке является CountryViewController , Модель которого country имеет значение равное nil.

AppDelegate.m
Screen Shot 2015-11-25 at 7.39.09 PM

Стартуем  iPhone 6+ в портретном режиме
Screen Shot 2015-11-25 at 8.03.14 PM

Стартуем  iPhone 5s в ландшафтном режиме
Screen Shot 2015-11-25 at 8.11.54 PM

Теперь наш адаптивный интерфейс работает как нужно, то есть для Compact Size Class (все iPhones и iPhone 6+ в портретной режиме) мы стартуем со списка стран.

Итак, настройка адаптивного Split View Controller завершена. Окончательный вариант простейшего приложения SplitViewCountry находится на Github.

Подведем итог нашим действиям:

Шаг 1. Перетаскивая Split View Controller из Палитры объектов.

Шаг 2. Добавляем segue Show Detail

Шаг 3. Настраиваем для segue метод prepareForSegue с учетом того, что destinationViewController для этого segue будет разным в зависимости от  Size Class : для Regular это CountryViewController, а для  Compact  это UINavigationController, в стэке которого на самом верху находится CountryViewController.

Шаг 4.  Добавляем кнопки на навигационную панель в методе prepareForSegue подготовки  segue типа Show Detail и в AppDelegate.

Шаг 5. Реализуем в AppDelegate метод collapseSecondaryViewController:ontoPrimaryViewController: делегата UISplitViewControllerDelegate, который срабатывает при переходе в collapsed режим, когда на экране должен остаться только один View Controller и он спрашивает нас, нужно ли отбросить Detail.

Можно обойтись без этих 5 шагов и получить точно такой же код и UI сразу же, если воспользоваться прекрасным  Master-Detail шаблоном приложения, в котором уже есть и Show Detail segue и Navigation Controller и весь необходимый код.
Screen Shot 2015-11-25 at 8.20.23 PM
Screen Shot 2015-11-25 at 8.25.55 PMНо пойдем дальше и  сравним полученную в простейшем приложении SplitViewCountry storyboard, со storyboard Photomania, которую мы привели в начале этого поста. Мы поймем, что в Photomania две  Table View Controllers в Master: одна — для фотографов, другая — для списка его фотографий.  Поэтому нам придется продолжить эксперименты с нашим маленьким примером и добавим еще один экранный фрагмент Table View Controller для «Континентов». Теперь наш пользовательский интерфейс выглядит следующим образом:
Screen Shot 2015-11-26 at 11.58.57 AM

Все будет работать прекрасно, за исключением одной ситуации, когда iPhone 6+ переходит из портретного режима в ландшафтный (то есть из collapsed режима в expanded), и при этом его экран в портретном режиме показывает Список Стран на месте Detail, а вовсе не флаг выбранной страны:

Screen Shot 2016-01-13 at 5.28.33 PM

При переходе из collapsed режима, когда на экране только один View Controller, в  режим expanded (Master и Detail одновременно на экране), адаптивный Split View Controller берет текущий экран в качестве Detail по умолчанию. А это совсем не то, что нам нужно. С помощью другого метода  separateSecondaryViewControllerFromPrimaryViewController:  делегата UISplitViewControllerDelegate, нам самим предлагается произвести нужную настройку
Screen Shot 2016-01-13 at 3.58.21 PM

Метод  separateSecondaryViewControllerFromPrimaryViewController:  спрашивает вас, какой View Controller следует взять в качестве  Detail. В нашей ситуации, когда primaryViewController — это Страны, нам неоткуда взять CountryViewController (изображение флага) как только со storyboard. Далее мы делаем небольшую настройку нашего  Detail.: добавляет кнопки на навигационную панель и выставляем флаг страны, первой в списке стран.

Теперь все работает правильно

Screen Shot 2016-01-13 at 4.57.17 PM

Полученный пример, простейшее приложении SplitViewCountry, можно рассматривать как шаблон для построения адаптивного приложения с такой конфигурацией storyboard, как в Photomania Universal URL.

Мы можем еще немного улучшить работу Split View Controller для iPad, задав преимущественный режим показа UISplitViewControllerDisplayModeAllVisible:

AppDelegate.m
Screen Shot 2015-12-03 at 6.18.30 PM

Что означает preferredDisplayMode? Это свойство определяет режим показа Split View Controller, который вы предпочтительно хотите использовать. Split View Controller делает все возможное, чтобы настроить интерфейс соответствующим образом, но может использовать и другой режим показа, если, например, будет недостаточно места для показа в заданном вами предпочтительном режиме.

Установка значения этого свойства в UISplitViewControllerDisplayModeAutomatic заставляет выбирать наиболее подходящий режим для текущего доступного пространства. На iPad это приводит к использованию режима UISplitViewControllerDisplayModePrimaryOverlay в портретной ориентации и UISplitViewControllerDisplayModeAllVisible  в ландшафтной ориентации. По умолчанию значение этого свойства равно UISplitViewControllerDisplayModeAutomatic.

Задавая режим показа UISplitViewControllerDisplayModeAllVisible, мы, фактически, влияем только на показ Split View Controller на iPad в портретном режиме
Screen Shot 2015-12-03 at 3.37.26 PM
в ландшафтном режиме тоже есть небольшие изменения: появляется кнопка переключения режимов показа, которая раньше на iPad не показывалась
Screen Shot 2015-12-03 at 3.43.19 PM
Я привела еще две закомментированные строки кода

//  splitViewController.preferredPrimaryColumnWidthFraction = 0.5;
//  splitViewController.maximumPrimaryColumnWidth = 512;

Если вы раскомментируете эти строки, то сможете регулировать ширину столбцов для Master и Detail.  Вы можете использовать свойство preferredPrimaryColumnWidthFraction, которое принимает значение от 0.0 до 1.0 , чтобы представить долю от общей ширины экрана, которую занимает Master. Например, значение 0.4 представляет 40% текущей ширины. По умолчанию это свойство принимает значение UISplitViewControllerAutomaticDimension, которое приводит к подходящей ширине Master, выбираемой Split View Controller.

Действительная ширина Master ограничивается значениями в диапазоне minimumPrimaryColumnWidth и maximumPrimaryColumnWidth. Split View Controller будет делает все возможное, чтобы настроить интерфейс соответственно заданным вами значениям свойств, но может поменять значения на другие в зависимости от располагаемого пространства. Вы можете получить действительную ширину Master с помощью свойства primaryColumnWidth.

Давайте распространим наши достижения на наше адаптивное приложение Photomania Universal URL. Мы уже получили storyboard в нужном виде с Show Detail segue:
Screen Shot 2015-11-21 at 5.03.35 PM

Делаем подготовку нового segue типа Show Detail в коде класса PhotosCDTVC таким образом, чтобы учесть присутствие Navigation Controller, предшествующего Detail, и  добавить имеющуюся у  Split View Controller кнопку displayModeButtonItem на навигационную панель. :

PhotosCDTVC.m
Screen Shot 2015-11-26 at 2.17.31 PM

Добавляем кнопки на навигационную панель в PhotomaniaAppDelegate для появления их при старте приложения:
Screen Shot 2015-11-26 at 3.42.36 PM

Подтверждаем протокол UISplitViewControllerDelegate:

PhotomaniaAppDelegate.m
Screen Shot 2015-11-21 at 10.11.42 PM

Затем назначаем себя делегатом:
PhotomaniaAppDelegate.m
Screen Shot 2015-11-26 at 3.47.59 PM

И, наконец, реализуем метод collapseSecondaryViewController:ontoPrimaryViewController: делегата UISplitViewControllerDelegate, который спрашивает нас, нужно ли отвергать Detail в collapsed режиме:
Screen Shot 2015-11-26 at 3.53.34 PM

В нашем случае, мы оставляем на экране Master, если Detail — это Navigation Controller, его топовым View Controller является ImageViewController и Модель  imageURL имеет значение, равное nil.

Реализуем другой метод  separateSecondaryViewControllerFromPrimaryViewController: делегата UISplitViewControllerDelegate, который срабатывает при переходе из collapsed режима в  expanded (Master и Detail одновременно на экране). В нашей ситуации, когда primaryViewController представляет собой PhotosByPhotographerCDTVC, берем ImageViewController (изображение фотографии) со storyboard и настраиваем его на автоматически выбираемую фотографию в таблице фотографий, которая может быть любой случайной из списка, но мы выбираем первую фотографию в таблице фотографий PhotosByPhotographerCDTVC.
Screen Shot 2016-01-13 at 3.47.41 PM
Можем задать режим показа  UISplitViewControllerDisplayModeAllVisible:

Screen Shot 2015-12-03 at 5.52.56 PM

Адаптивный вариант UI для  Photomania Universal URL, функционирующий в iOS 9, находится на  Github.

Popover

Продолжим эксперименты с нашим маленьким примером и добавим еще один экранный фрагмент Capital View Controller на storyboard, который будет показывать столицу выбранной страны в Popover «окошке»  при нажатии кнопки «Столица» на навигационной панели. Новый  экранный фрагмент Capital View Controller активизирует свою работу с помощью кнопки «Столица»и segue типа Presents as Popover.
Screen Shot 2015-11-27 at 10.06.57 AM
В iOS 9 класс UIPopoverController упразднен. Вместо него в iOS 9 используется  UIPopoverPresentationController, который доступен с iOS 8. Концепция Popover осталась той же самой: нам необходим View Controller, как содержимое, которое показывается внутри Popover, но сам по себе Popover — не UIViewController. Он появляется на экране, используя так называемый механизм Popover Presentation Controller

Относительно Popover интересно еще то, что  помимо того, что хотя он сам по себе не является MVC, он все же использует segues и все, что с ними связано. Он использует CTRL-перетягивание к некоторому View Controller, и вы все также получаете возможность выполнять метод prepareForSegue. И это выглядит совершенно точно также, хотя у него нет своего собственного View Controller. Popover все равно использует segues, чтобы вызвать появление презентуемого  им View Controller.

Следует обратить внимание на некоторые возможности при подготовки segue для Popover.

Одна из них — это то, что внутри вашего prepareForSegue вы можете получить то, что называется Presentation Controller. И Presentation Controller может рассказать вам о том, какие вещи (например, кнопка) заставили “всплыть” Popover. Вы можете даже конфигурировать презентацию Popover. Например, вы можете сказать, что не хотите, чтобы Popover “всплывала” слева от чего-то. Я хочу, чтобы Popover всегда “всплывала” справа от чего-то. Вы можете этим управлять.

Еще более интересно то, что вы можете использовать делегата (delegate) Presentation Controller.

Используя Presentation Controller делегата, вы можете воздействовать на то, как Popover будет адаптироваться на iPhone. Popover на iPad “всплывает”  ввиде маленького окошка. На  iPhone Popover адаптируется и превращается вместо маленького окошка в модальное окно на полный экран. Оно не “всплывает” как что-то маленькое на  iPhone. Почему? Потому что экран iPhone значительно меньше, и если “всплывающая” вещь реально большая, то может не быть способа избавиться от нее или сделать ее подходящей размеру экрана. Но если вы представите ее модально на весь экран, то она точно подойдет по размеру экрана. Адаптация  делается автоматически. iOS автоматически делает эту адаптацию вместо вас, также как автоматически адаптируется Split View и Navigation Controller,  если  вы создаете вашу storyboard прямо на iPhone. Но используя делегата  Presentation Controller Delegate, вы можете воздействовать на эту адаптацию.

Давайте посмотрим, как в методе prepareForSegue вы можете получить этот Popover Presentation Controller,  и как вы можете делать что-то с этим, например, устанавливать делегата.

Код для подготовки segue в методе prepareForSegue будет отличаться от того, что был в iOS 7, когда мы получали экземпляр упраздненного в iOS 9 класса  UIPopoverController непосредственно из segue. В  iOS 9 мы будем получать экземпляр нового класса  UIPopoverPresentationController не из segue, а непосредственно из  segue.destinationViewController:

CountryViewController.m
Screen Shot 2015-11-27 at 11.09.16 AM

Мы можем использовать его делегата

  ppс.delegate = self;

если подтвердим протокол  UIPopoverPresentationControllerDelegate

CountryViewController.m
Screen Shot 2015-11-27 at 10.34.55 AM
Протокол нам нужен для следующих целей. В iOS 9, когда Popover пытается провести презентацию, он собирается вас спросить, как вы хотите, чтобы я “адаптировал” Popover, если я на iPhone? По умолчанию, мы говорим, что это модальное представление на полный экран (возврат .FullScreen), но вы можете сказать, что вы хотите UIModalPresentationStyle.None, что означает, что адаптации не будет. То есть презентация на iPhone будет в точности такая же, как и на iPad. Для маленького Popover это имеет большой смысл. Мы можем “выключить” поведение “адаптации”, реализовав метод делегата  UIPopoverPresentationControllerDelegate

CountryViewController.m
Screen Shot 2015-11-27 at 11.12.39 AM
Последняя и очень важная вещь, касающаяся Popover — это размер  “всплывающего” окна.

Вам действительно нравится, когда появляется абсолютно идеально подогнанное к размеру MVC. Потому что MVC могут быть разного размера. В любом случае вы хотели бы реально управлять размером. В объектно-ориентированном мире система спрашивает MVC, которое находится в Popover, какой размер этот MVC предпочитает? Какого размера ты хочешь быть? Потому что реально только само MVC может знать, какой размер может быть предпочтительным или идеальным. Но это только рекомендация, потому что у Popover тоже есть некоторые ограничения (constraints). Потому что Popover может появляться на экране только в определенном месте, экран должен быть достаточно большим, стрелочки могут иметь определенные направления, у Popover  много ограничений, с которыми надо работать.

Но он все равно спрашивает MVC, которое размещает внутри, чем оно хочет быть? Есть специальное свойство в вашем UIViewController:

@property(nonatomic) CGSize preferredContentSize

Вы можете переопределить это свойство и вернуть предпочитаемый размер. Если ваш предпочтительный размер всегда один и тот же размер, вы можете просто его установить. Если вам нужно рассчитать предпочтительный размер на основании его содержимого, что вы можете сделать это следующим образом:

CapitalViewController.m
Screen Shot 2015-11-27 at 11.21.45 AM
Вариант простейшего приложения SplitViewCountry c Popover находится на Github.

Есть одна проблема, связанная с работой Popover в портретном режиме на iPad. Если я перейду в портретный режим и покажу «Столицу» Санта-Фа-Де-Богота для Колумбии. Затем я кликну на Континенты в левой части навигационной панели и выберу другую страну — Чили … Вы видите, что столица Санта-Фа-Де-Богота не ушла, осталась на экране, хотя я полагала, что если я кликну где-нибудь за пределами Popover, то Popover уйдет.
Screen Shot 2015-11-27 at 12.34.22 PM
Ответ заключается в том, что, если вы кликаете на ту же самую панель, где находится кнопка “Столица”, то вам разрешается взаимодействовать со всем, что там находится, то есть кликать на любых кнопках и Popover не уйдет. Это вещь имеет отношение к passthroughViews. Целая навигационная панель является частью passthroughViews, и реально плохая вещь заключается в том, что, если я кликну на другой стране, то изображение флага этой страны обновится, а Popover со Столицей — нет. Мне по-прежнему будет показываться Столица старой страны. Это действительно очень плохо.

Эту проблему в коде будем решать следующим образом. Каждый раз, когда кто-то устанавливает мой country в методе setCountry, я буду убирать (dismiss) любой  Popover, который у меня есть. 

CountryViewController.m
Screen Shot 2015-11-27 at 2.00.07 PM
Теперь ситуация исправилась
Screen Shot 2015-11-27 at 2.08.56 PM
Вариант простейшего приложения SplitViewCountry cо всеми нюансами Popover находится на Github.

Давайте вернемся к адаптивному варианту UI для  Photomania Universal URL, функционирующий в iOS 9, и посмотрим, какие нам нужно сделать изменения для Popover.

ImageViewController.m
Screen Shot 2015-11-27 at 2.22.42 PM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Screen Shot 2015-11-27 at 2.26.15 PM
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Screen Shot 2015-11-27 at 2.42.50 PM

. . . . . . . . . . . . . . . . . . . . . . .  . . .  .
Screen Shot 2015-11-27 at 2.52.53 PM

URLViewController.m
Screen Shot 2015-11-27 at 2.49.26 PM
Адаптивный вариант UI для  Photomania Universal URL, функционирующий в iOS 9 c Popover, находится на  Github.

Один комментарий к “Дополнение к Лекции 14 Stanford CS 193P iOS 7. Адаптивные SplitViewController и Popover в приложении Photomania для iOS 9. (Objective-C).

Обсуждение закрыто.