Как сделать всплывающее окно swift

Обновлено: 17.05.2024

Всплывающие окно iOS, как реализовать?

Доброго времени суток!
Интересует как в iOS SDK проекте реализовать всплывающее окно.
Суть такова, в моём приложении на одном из ViewController'ов есть некий UIButton. Нужно чтобы по нажатию на UIButton всплывало окно поверх моего ViewController, так же чтобы его потом можно было свернуть обратно свайпом, либо по нажатию на кнопку возврата.
Примеры
1) Подтверждение TouchID в AppStore ( iOS 11.x)

2) Открытие страницы с видео в YouTube App

Задача

После выполнения задачи у нас получится такое приложение:

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


Добавьте еще одни контроллер из библиотеки элементов Xcode. Сразу добавьте файл, в котором будет описан класс нашего нового контроллера.

Можете создать файл вручную, в этом случае укажите наименование нового класса и то что наследуется от UIVewController. Либо Xcode создаст этот файл автоматически.


Не забудьте в панели утилит справа присвоить контроллеру созданный клас

Добавление всплывающего окна


Вывод на экран


Далее следует добавить код, который покажет контролер PopUpViewController поверх корневого ViewController. Этот код нужно добавить во вновь созданый метод

Что тут у нас происходит:

  1. Мы получаем ссылку на PopUpViewController, причем получаем ссылку мы при помощи метода, который обращается к StoryBoard и находит контроллер, у которого идентификатор popUpVCid. ВНИМАНИЕ! Мы его еще не устанавливали, сделаем чуть ниже.
  2. Добавляем полученный контроллер PopUpViewController в качестве дочернего к ViewController.
  3. Размеры View нового контроллера мы устанавливаем равными размера View текущего контроллера.
  4. Непосредственно выводим на экран то, как отображается PopUpViewController. Т.е. добавляем ViewPopUpViewController в стек той кучи View, которые уже есть.
  5. Вызываем метод делегата, чтобы сообщить всем делегатам, о том, что контроллер мы отобразили.

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


Закрываем окно


Добавляем строку кода для удаления отображения контроллера PopUpVC во вновь сознанный Action.

Можно запустить и посмотреть что получится. Обе кнопки должны работать.

Детали

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


Затемнение фона

Для этого просто установим цвет у фона PopUpViewController в темный цвет и добавим фону прозрачности. Это следует произвести в том же методе, в котором мы меняли углы у серого View.

Запустите проект и отобразите Pop Up контроллер:


Анимация

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

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

How to create a custom pop up view with swift?

So I am working on a simple app, where I use a table view. This table view displays the name of "players". But in order for me to add players in the table view, I want a pop up window to be displayed with a text field where you provide the name.

Now I have been reading about creating a xib or nib file, but I am not sure how to "load" the pop up window.

What's the best approach to this?

Looks like this:

Pop up View

enter image description here


302k 40 40 gold badges 480 480 silver badges 523 523 bronze badges asked Nov 29 '15 at 20:06 739 1 1 gold badge 7 7 silver badges 20 20 bronze badges Jun 8 '18 at 9:14

3 Answers 3

In storyboard

  • In storyboard create one UIViewController and design it according to your requirement.
  • Set custom class as anotherViewController and Storyboard_ID as another_view_sid
  • Create one new Cocoa Touch Class as anotherViewController and subclass of UIVIewController

In viewController

In anotherViewController

answered Mar 23 '17 at 11:32


1,286 11 11 silver badges 20 20 bronze badges

you'd create an custom UIView with all respected object needed, from your Controller's viewDidLoad() you'll hide it.

Whenever your user wants to perform some action or task, you unhide it and once the user finished then hide it again or remove from the superView.

Below there is some code to help you start

Check it out this github project, It's closed to be production ready, it gives you a better way to deal (present and dismiss) with UIViews

If you only want the player's name then use a UIAlertController containing a textfield

Как создать пользовательский всплывающий вид с помощью swift?

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

теперь я читал о создании файла xib или nib, но я не уверен, как "загрузить" всплывающее окно.

каков наилучший подход к этому?

Pop up View

enter image description here

автор: rmaddy

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

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

ниже есть код, который поможет вам начать

если вы хотите только имя игрока, то используйте UIAlertController содержит текстовое поле

как реализовать всплывающее диалоговое окно в iOS

автор: Suragch

Ага, а UIAlertView вероятно, это то, что вы ищете. Вот пример:

если вы хотите сделать что-то более причудливое, скажем, отобразить пользовательский интерфейс в вашем UIAlertView , вы можете подкласс UIAlertView и поместите пользовательские компоненты пользовательского интерфейса в init метод. Если вы хотите ответить на кнопку, нажмите после UIAlertView появляется, Вы можете установить delegate выше и реализовать - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex метод.

вы также можете посмотреть на UIActionSheet .

автор: donkim

разные люди, которые приходят к этому вопросу, означают разные вещи всплывающим окном. Я настоятельно рекомендую прочитать Временные Представления документация. Мой ответ-это в основном резюме этой и другой связанной с ней документации.

предупреждение (покажите мне пример)

enter image description here

стоит процитировать предупреждение документации и советы о создании ненужных предупреждений.

enter image description here

  • см. также Alert Views, но начиная с iOS 8 . Вы должны использовать UIAlertController для создания предупреждений сейчас.
  • iOS Основы: UIAlertView и UIAlertController (учебник)

Лист Действий (покажите мне пример)

enter image description here

Листы Действий дайте пользователю список вариантов. Они появляются либо в нижней части экрана, либо в всплывающем окне в зависимости от размера и ориентации устройства. Как и с предупреждениями, a UIAlertController используется для создания листа действий. До iOS 8, UIActionSheet использовался, но теперь the документация говорит:

важно: UIActionSheet устарел в iOS 8. (Обратите внимание, что UIActionSheetDelegate также не рекомендуется.) Для создания и управления листами действий в iOS 8 и более поздних версиях вместо этого используйте UIAlertController С preferredStyle of UIAlertControllerStyleActionSheet .

Модальное Представление (покажите мне пример)

enter image description here

A модал вид сдержанный вид, который имеет все необходимое для выполнения задачи. Он может занимать или не занимать весь экран. Чтобы создать модальное представление, используйте UIPresentationController С Модальные Стили Презентации.

пирог (покажите мне пример)

enter image description here

A пирог - это вид, который появляется, когда пользователь нажимает на что-то и исчезает при нажатии. Он имеет стрелку, показывающую элемент управления или место, откуда был сделан кран. Содержимое может быть практически любым, что вы можете поместить в контроллер представления. Вы делаете popover с UIPopoverPresentationController . (До iOS 8, UIPopoverController рекомендуемый метод.)

в прошлом popovers были доступны только на iPad, но начиная с iOS 8 вы также можете получить их на iPhone (см. здесь, здесь и здесь).

уведомления

enter image description here

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

enter image description here

примечание о Android тосты

enter image description here

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

  • сделайте свой собственный с подклассом UIView
  • импорт стороннего проекта, который имитирует тост
  • используйте оповещение без кнопок с таймером

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

автор: Suragch

С момента выпуска iOS 8 UIAlertView теперь устарел. Теперь вы будете использовать UIAlertController.

вот пример того, как это выглядит в Swift

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

автор: Entalpi

обновлено для iOS 8.0

начиная с iOS 8.0, вам нужно будет использовать UIAlertController следующим образом:

где self в моем примере является UIViewController, который реализует метод "presentViewController" для всплывающего окна.

автор: us_david

Для Swift 3 & Swift 4:

поскольку UIAlertView устарел, есть хороший способ отображения оповещения на Swift 3

удалено :

это версия swift, вдохновленная проверенным ответом:

добавьте делегат в контроллер вида:

когда пользователь нажимает на кнопку, этот код будет выполняться :

автор: Kevin ABRIOUX

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

Адаптивные Split View Controller и Popover в iOS 9 (Swift). Часть 2


В первой части перечислены пять интересных с точки зрения разработчика случаев применения адаптивного Split View Controller и Popover, которые отличаются сложностью Master. Detail везде один и тот же — единственный Image View Controller, вставленный в Navigation Controller и призванный показывать изображение фотографии:

1. Классический вариант: один элемент в Master, вставленный в Navigation Controller, (часто это Table View Controller)

2. Множество Table View Controller элементов, вставленных в Navigation Controller

3. Tab Bar Controller в качестве Master

4. Случай разных UI и разных пользовательских классов для приборов с разными Size Classes здесь не рассматривается, но идею можно посмотреть в “Адаптивный интерфейс с двумя storyboards для iOS 9”.

5. Адаптивный Popover

В первой части осуществлялось построение базового экспериментального приложения на Swift, которое было распространено на случаи 1-2. В этой статье мы будем дальше усложнять наше экспериментальное приложение и распространим его на случаи 3 и 5. Код для всех вариантов можно найти на Github.

3. Tab Bar Controller в качестве Master


Усовершенствуем наше приложение и предоставим пользователю возможность запоминать “недавно” просмотренные фотографии в хранилище NSUserDefaults (то есть они должны постоянно сохранятся между запусками данного приложения) и просматривать их на отдельной закладке Tab Bar Controller. Для этого на основе предыдущего приложения AdaptiveSplitViewController2Swift создадим новое приложение AdaptiveSplitViewController3Swift и добавим Tab Bar Controller на storyboard, удаляем сопровождающие его View Controllers, а вместо них подсоединяем к нему наших Photographers, вставленных в Navigation Controller и другой экранный фрагмент Resents, предназначенный для просмотра “недавних” фотографий и также вставленный в Navigation Controller:

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



AppDelegate.swift

Возврат true говорит о том, что SplitViewController не должен управлять показом Detail, мы сами будем его показывать. Теперь изображение фотографии для Compact-width приборов будет показываться правильно: c возвратной кнопкой и скользить как в Navigation Controller, а не появляться модально с невозможностью его покинуть:


Но коль скоро мы добавили ImageViewController в стэк Navigation Controller для Master, то можем получить наш ImageViewController в Master при переходе в ландшафтный режим, что, конечно, недопустимо, так как ImageViewController должен появляться только на месте Detail:


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


AppDelegate.swift

Пользуясь знанием imageURL только что убранного ImageViewController , можем настроить соответствующую строчку в списке фотографий:


AppDelegate.swift

Восстанавливаем Detail со storyboard и настраиваем ее Модель на полученную фотографию:


AppDelegate.swift


В результате ситуация на iPhones поправится, включая и переход от портретного режима к ландшафтному на iPhone 6+:

Теперь поговорим немного о закладке “Resents”, так называемых “недавних” фотографиях. Этот View Controller обслуживается пользовательским классом ResentsTVC



Класс ResentsTVC наследует от класса FlickrPhotosTVC и его единственной функциональной особенностью является считывание списка фотографий из NSUserDefaults :
ResentsTVC.swift

Нам потребовалось всего две строки кода и ниже будет показано почему это так просто.
Взаимодействие с NSUserDefaults организовано с помощью специального класса ResentsDefault и вычисляемого свойства var resentsPhotos :


ResentsDefault.swift


DataModel.swift

Класс ResentsDefault , обслуживающий взаимодействие с NSUserDefaults , имеет в качестве public API два свойства:
var addedPhoto = Photo? — фотографию, которую мы хотим добавить в NSUserDefaults ,
var resentsPhotos: [[String : String]] — массив словарей с информацией о фотографиях, хранимых в NSUserDefaults :


ResentsDefault.swift

Наша работа с хранилищем NSUserDefaults начинается с того, что мы добавляем addedPhoto в уже существующее хранилище и это выполняется одной строкой кода и вспомогательной функцией addPhoto , у которой на входе добавляемое photo и массив resentsPhotos фотографий, хранящихся в NSUserDefaults :
addPhoto (photo, inDefaultsPhotos: resentsPhotos)
Когда мы используем resentsPhotos как входной параметр вспомогательной функции, то срабатывает для этого вычисляемого свойства и данные считываются из хранилища NSUserDefaults в resentsPhotos . Затем мы добавляем в самое начало массива resentsPhotos новую фотографию photo как наиболее “недавнюю”:


ResentsDefault.swift

Результирующий массив будет присвоен опять resentsPhotos :
resentsPhotos = addPhoto (photo, inDefaultsPhotos: resentsPhotos)
, а это значит, что работает для этого вычисляемое свойства и обновленный массив записывается в хранилище NSUserDefaults .
Механизм NSUserDefaults предназначен для сохранения очень небольшого количества данных, поэтому мы ограничимся лишь числом фотографий, задаваемых константой ResentPhotoAmount , которая в нашем конкретном случае равна 20.
Теперь подумаем о том, где лучше всего записывать информацию о фотографии в хранилище NSUserDefaults . Если пользователь выбрал фотографию, то мы должны поместить информацию о ней в NSUserDefaults , и лучше всего это сделать в методе prepareForSegue класса FlickrPhotosTVC , но мы оставим этот класс более обобщенным (он может понадобиться нам для других целей). Поэтому разместим новую функциональность, связанную с записью информации о фотографии в NSUserDefaults , в новом классе PhotosSavedNSUserDefaults , который является subclass класса FlickrPhotosTVC :


PhotosSavedNSUserDefaults.swift

Необходимо установить именно новый класс PhotosSavedNSUserDefaults в качестве пользовательского для экранного фрагмента Flickr Photos на storyboard


Но вернемся к классу ResentsTVC , обслуживающему “недавние” фотографии и экранный фрагмент Resents. Его задача восстанавливать информацию о “недавних” фотографиях из NSUserDefaults и мы использовали для этого единственную строку кода:


ResentsTVC.swift

Если мы сравним эту строку с аналогичной строкой еще одного класса JustPostedFlickrPhotosTVC , также наследующего от FlickrPhotosTVC :


JustPostedFlickrPhotosTVC.swift

то мы увидим одну и ту же конструкцию .flatMap(Photo.init) . В первом случае при восстановлении из NSUserDefaults на вход поступает [String : String] , а во втором случае при считывании данных с сервера Flickr — [String : AnyObject] . Как компилятор знает, какой инициализатор Photo запустить? Ведь их два?


DataModel.swift

  • init?(json:[String:AnyObject]) , для инициализации struct Photo из JSON данных с сервера Flickr,
  • init(userDefaults:[String:String]) , для инициализации struct Photo из хранилища NSUserDefaults .

4. Адаптивный Popover

Раньше Popover можно было показывать только на iPad, но начиная с iOS 8 он показывается и на iPhone как в портретном, так и в ландшафтном режимах в так называемом “адаптивном” варианте (об этом позже). Концепция Popover осталась той же самой: нам необходим View Controller, как содержимое, которое показывается внутри Popover, но сам по себе Popover — не UIViewController . Он появляется на экране, используя так называемый механизм Popover Presentation Controller.
Продолжим работать с нашим экспериментальным приложением и добавим еще один экранный фрагмент на storyboard, который будет показывать URL изображения выбранной фотографии в Popover «окошке». Для этого на основе приложения AdaptiveSplitViewController2Swift (с многочисленными Table View Controllers в качестве Master) создадим новое приложение AdaptiveSplitViewController4Swift и добавим левую кнопку «URL» на навигационую панель экранного фрагмента Image View Controller (показ изображения фотографии) и segue типа Presents as Popover. Теперь наш пользовательский интерфейс выглядит следующим образом:


Относительно Popover интересно, что хотя он сам по себе не является MVC, он все же использует segue типа Present as Popover, чтобы вызвать появление презентуемого им View Controller.
Для создания segue типа Present as Popover используется как обычно CTRL-перетягивание к некоторому View Controller, и вы также получаете возможность выполнять метод prepareForSegue .
Как только вы установили segue типа Presents as Popover,



появляется возможность регулировать размер “окошка”, а именно размер топового view:


Устанавливаем идентификатор “Show URL” для segue и добавим метод prepareForSegue для ImageViewController :


ImageViewController.swift

Следует обратить внимание на некоторые дополнительные возможности при подготовки segue для Popover. Одна из них — это то, что внутри вашего prepareForSegue вы можете получить то, что называется UIPopoverPresentationController и конфигурировать презентацию Popover. Например, вы можете сказать, что не хотите, чтобы Popover “всплывала” слева от чего-то, а хотите, чтобы Popover всегда “всплывала” справа от чего-то. Вы можете всем этим управлять. Кроме того, в методе prepareForSegue можно определить себя делегатом:


ImageViewController.swift

Далее подтвердить протокол UIPopoverPresentationControllerDelegate :


ImageViewController.swift

и “выключить” адаптивное поведение, реализовав метод делегата UIPopoverPresentationControllerDelegate , в котором возвращается .None , то есть “отказ” от адаптации Popover на iPhone:


ImageViewController.swift

И это все, что нужно для представления Popover ввиде маленького “окошка” на iPhone. Обычно этот способ и встречается в блогах.
Но в нашем экспериментальном приложении мы реализуем это немного по-другому, придерживаясь идеи, что класс ImageViewController должен оставаться независимым от способа показа URL изображения фотографии, а сам экранный фрагмент, на котором размещен TextView для показа URL изображения фотографии, должен быть самонастраивающимся.
Давайте рассмотрим этот второй способ.
Показ URL изображения фотографии обслуживается пользовательским классом URLViewController .
Моделью класса URLViewController является свойство
var url : NSURL?
При его установке, а также в методе “жизненного цикла” viewDidLoad , c помощью метода updateUI() обновляется пользовательский интерфейс. Всю настройку Popover мы будет проводить непосредственно в этом классе, поэтому класс URLViewController подтверждает протокол UIPopoverPresentationControllerDelegate :


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


URLViewController.swift

Используя UIPopoverPresentationControllerDelegate , вы можете воздействовать на то, как Popover будет адаптироваться на iPhone. Popover на iPad “всплывает” так, как мы привыкли — в виде маленького окошка. На iPhone Popover адаптируется и превращается вместо маленького окошка в модальное окно на полный экран. Оно не “всплывает” как что-то маленькое на iPhone. Почему? Потому что экран iPhone значительно меньше, и если “всплывающая” вещь реально большая, то может не быть способа сделать ее подходящей размеру экрана. Но если вы представите ее модально на весь экран, то она будет точно соответствовать размеру экрана. iOS автоматически делает эту адаптацию вместо вас, также как автоматически адаптируется Split View Controller и Navigation Controller. Но используя делегата UIPopoverPresentationControllerDelegate , вы можете воздействовать на эту адаптацию, и именно это мы будем делать.
В iOS 9, когда Popover пытается провести презентацию, то спрашивает нас, как мы хотим, чтобы он “адаптировал” Popover на iPhone? По умолчанию, это модальное представление на полный экран (возврат .FullScreen ), но мы можем сказать, что хотим использовать UIModalPresentationStyle.None , что означает отсутствие какой-либо автоматической адаптации. То есть презентация на iPhone будет в точности такая же, как и на iPad. Для маленького Popover это имеет большой смысл. Мы можем “выключить” поведение “адаптации”, реализовав метод делегата UIPopoverPresentationControllerDelegate , в котором вернем UIModalPresentationStyle.None :


URLViewController.swift

Последняя и очень важная вещь, касающаяся Popover — это размер “всплывающего” окна.
Вам действительно нравится, когда появляется абсолютно идеально подогнанное к размеру MVC “всплывающее” окно. Потому что MVC могут быть разного размера. В любом случае вы хотели бы реально управлять его размером. В объектно-ориентированном мире система спрашивает MVC, которое находится в Popover, какой размер этот MVC предпочитает? Какого размера ты хочешь быть? Потому что реально только сам MVC может знать, какой размер может быть предпочтительным. Но это только рекомендация, потому что у Popover тоже есть некоторые ограничения. Например, Popover может появляться на экране только в определенном месте, экран должен быть достаточно большим, стрелочки могут иметь определенные направления. У Popover много ограничений такого рода.
Но он все равно спрашивает MVC, которое размещает внутри, чем он хочет быть?
Есть специальное свойство в вашем UIViewController :
var preferredContentSize: CGSize
Вы можете переопределить это свойство и вернуть предпочтительный размер. Если ваш предпочтительный размер всегда один и тот же, вы можете просто его установить. Если вам нужно рассчитать предпочтительный размер на основании его содержимого, то вы можете сделать это следующим образом:


URLViewController.swift

В результате мы получаем на Compact-width приборах такое же маленькое “окошко”, как и на Regular-width приборах:


Есть одна проблема, связанная с работой Popover в портретном режиме на iPad. Если я нахожусь в портретном режиме на iPad и покажу URL для определенной фотографии, а затем я кликну на Flickr Photographers в левой части навигационной панели и выберу другого фотографа и другую фотографию … Вы видите, что предыдущий URL остается на экране, хотя я полагала, что если я кликну где-нибудь за пределами Popover, то Popover уйдет.


У нас уже сменилась картинка, а URL будет оставаться старым до тех пор, пока мы не кликнем на самой картинке за пределами Popover.
Ответ заключается в том, что, если вы кликаете на ту же самую панель, где находится кнопка “URL”, то вам разрешается взаимодействовать со всем, что там находится, то есть кликать на любые кнопки и Popover не уйдет. В частности, когда мы кликаем на возвратной кнопке Flickr Photographers. Эта ситуация имеет отношение к passthroughViews . Целая навигационная панель является частью passthroughViews , и реально плохая вещь заключается в том, что, если я кликну на другой фотографии в списке, то изображение фотографии обновится, а Popover с URL— нет. По-прежнему на экране будет URL старой фотографии. Это действительно очень плохо.
Эту проблему в коде будем решать следующим образом. Каждый раз, когда кто-то устанавливает новое изображение image , я буду убирать (dismiss) любой Popover, который у меня есть.


ImageViewController.swift

Как настроить popover на размер содержимого в моем tableview в swift?

Я использую popoverPresentationController чтобы показать мой popover. The UITableViewController используется для отображения как popover создается программно и обычно содержит от 1 до 5 строк. Как настроить это всплывающее окно для настройки размера содержимого tableview?

код для моего popover:

автор: Henny Lee автор: zisoft

в вашем UITableViewController viewDidLoad() вы можете добавить наблюдателя:

затем добавьте этот метод:

и наконец, viewDidDisappear() , убедитесь, что вы удалите наблюдателя:

таким образом, popover автоматически отрегулирует размер, чтобы соответствовать содержимому, когда он загружен или изменен.

автор: Bo Frese

переопределить preferredContentSize свойство в вашем расширении uitableviewcontroller следующим образом:

автор: Philip De Vries

для swift 4 Если вы хотите наблюдать размер содержимого, я нашел это оптимальным решением. Сообщая об этом здесь, так как я не нашел полного примера в интернете:

автор: Daniele Bernardini

Первое: все комментарии хороши и помогают в полной мере. Я мало что изменил в своей логике, что делает мой VC многоразовым компонентом.

Изменения модальной презентации экранов в iOS 13

image

Введение

На момент написания статьи WWDC 2019 подходит к концу. Как и многие разработчики для iOS, я медленно обрабатываю всю новую информацию, которую Apple нам дала, и в ближайшие недели (и месяцы!) Постараюсь посмотреть столько видео, сколько смогу.
У меня появилось три вопроса по поводу моих собственных приложений:

  • Мои текущие приложения работают без проблем на iOS 13? У Apple долгая история обратной совместимости, основанная на версии Xcode, с которой было создано приложение. История показывает, что приложения, созданные в Xcode 10 под iOS 13, будут вести себя так, как если бы они работали на iOS 12. Но это не всегда так.
  • Работают ли мои приложения при сборке с Xcode 11 / iOS 13? Сборка с использованием новейших инструментов позволяет приложению работать по-новому, минуя обратную совместимость с предыдущими версиями iOS. Что-нибудь сломалось?
  • Какие изменения можно / нужно сделать, чтобы мои приложения работали лучше или использовали преимущества новых функций iOS 13? Это самая большая задача, и она займет больше времени для изучения и реализации. Это исследование для отдельной статьи.

Я еще не установил iOS 13 на реальном устройстве, но могу провести тестирование пункта №1, установив приложения, созданные в Xcode-10, на симулятор iOS 13.
Я все еще работаю над пунктом №2, но на основании моего начального тестирования и чтения твитов от других разработчиков, делающих подобные открытия, я обнаружил ряд поведенческих изменений в моих приложениях при сборке с Xcode 11. У меня есть много видео для просмотра и информация для усвоения, но в этом посте я хочу сосредоточиться на заметных сразу и потенциально разрушительных изменениях в презентации UIViewController в iOS 13.

Изменение стиля модальной презентации по-умолчанию

По умолчанию модальная презентация теперь представляет собой "страницу" (ориг. Page Sheet), а не полный экран (Full Screen). Документация для modalPresentationStyle гласит:

По умолчанию используется UIModalPresentationAutomatic для iOS, начиная с iOS 13.0, и UIModalPresentationFullScreen в предыдущих версиях.
По умолчанию UIViewController, если в качестве modalPresentationStyle установлен UIModalPresentationAutomatic, использует UIModalPresentationPageSheet, но системные контроллеры могут использовать другие стили показа для UIModalPresentationAutomatic.

Последствия этого изменения различаются для iPhone и iPad.

Модальная презентация на iPhone

Стили презентации Page Sheet, Form Sheet и Popover на iPhone адаптированы к полноэкранному режиму, если только метод UIAdaptivePresentationControllerDelegate не используется для предотвращения адаптации. Например, экран настроек может быть презентован в стиле Form Sheet, чтобы он отображался в полноэкранном режиме на iPhone и в уменьшенном виде на iPad. Технически внешний вид / адаптация зависят от ширины. Презентации в стиле Page Sheet / Form Sheet / Popover на устройствах Landscape iPhone Plus и XS Max не занимают весь экран, потому что они имеют обычную ширину. Внешний вид iPad зависит от размеров slide over и режима многозадачности.
На следующих снимках экрана показана презентация Form Sheet на iPhone XS для трех случаев: сборка в Xcode 10 для iOS 12, сборка в Xcode 10 для iOS 13, сборка в Xcode 11 для iOS 13.




Обратная совместимость iOS 12 с iOS 13 для сборки Xcode 10 приводит к полноэкранному представлению. Стиль сгруппированных UITableView изменился в iOS 13, чтобы скрыть пространство над первым разделом при отсутствии заголовка. Даже сборка Xcode 10 / iOS 12 ведет себя по-разному при запуске на iOS 13, а это не то, что я ожидал.
Самым большим изменением в iOS 13, конечно же, является карточное представление экранов (ориг. card-like appearance). UIViewController был уменьшен в размерах, и его верх все еще немного виден за вновь представленным UIViewController. UIWindow за корневым UIViewController также немного видимо. Черный фон UIWindow по умолчанию выглядит великолепно, особенно на устройствах с выемкой (notch). Некоторые из моих приложений устанавливали фон UIWindow белым (по причинам, которые я уже не помню), и это выглядело довольно уродливо. Я быстро исправил это!

Поведение UIViewController при новом модальном стиле показа

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




Другое потенциально важное различие в поведении — это то, что происходит с показывающим (ориг. presenting) UIViewController. Полноэкранное представление (ориг. full screen presentation), которое полностью покрывает UIViewController, приведет к удалению UIViewController из иерархии. Но в случае с новой карточной презентацией UIViewController должен оставаться в иерархии, потому что он все еще видим. Однако, хотя пользователь может видеть только два UIViewController одновременно, многократный показ UIViewController не удаляет нижние UIViewController из иерархии.

Изменения размеров

Новый внешний вид в стиле карточки означает, что показанный UIViewController не такой высокий на iOS 13, как на iOS 12:




Я хочу full screen!

Явный запрос на показ UIViewController в режиме Full Screen предотвратит показ экрана в стиле карточки. Однако, это может нарушить поведение приложения на iPad. Пожалуйста, не поддавайтесь искушению проверить идиому устройства и использовать другой стиль презентации для iPhone и iPad. Если последние несколько лет нас чему-то и научили, так это тому, что мы не должны делать предположений на основе типов устройств или размеров экрана. Если вы хотите, чтобы на iPad отображался Page / Form Sheet, но на iPhone был Full Screen, вы можете использовать UIAdaptivePresentationControllerDelegate для адаптации к полноэкранному режиму в условиях компактной ширины.

Модальная презентация на iPad

Form Sheets

Экраны, показанные в стиле Form Sheet, остаются неизменными в iOS 13:




Page Sheets

Как отмечалось выше, modalPresentationStyle по умолчанию в iOS 13 теперь Page Sheet. На iPad размер UIViewController в этом стиле изменился как в книжной, так и в альбомной ориентации:






Как и в iOS 12, ограничение по "читаемому контенту" (ориг. readable content size) меняет размер при изменении категории размера контента (ориг. content size category). Реальный размер кажется разным на iOS 12 и 13 в некоторых категориях размера контента.
Сам UIViewController, презентованный в стиле Page Sheet, также увеличивается на iOS 13 с увеличением категории размера контента. Вот так выглядит категория «Extra Extra Extra Large» (максимальный размер, доступный без включения larger accessibility sizes):






Остальные виды презентации

Документация для modalPresentationStyle гласит:

По умолчанию UIViewController определяет UIModalPresentationAutomatic как UIModalPresentationPageSheet, но остальные системные контроллеры могут определять UIModalPresentationAutomatic по-другому.

Я не уверен на 100% во всех правилах для «остальных системных контроллеров», но я обнаружил, что показ UIViewController с разделенным экраном (split screen) без установки modalPresentationStyle дает карточный вид во всю ширину:




Swipe to dismiss

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




Обратите внимание, что в этом примере я поместил экран «О программе» в UINavigationController экрана настроек. Несмотря на то, что UINavigationController не отображал свой корневой контроллер, интерактивное закрытие было возможным.

Не смахивай меня, пожалуйста!

Если вы полагаетесь на то, что пользователь нажимает кнопку «Готово» (или аналогичную кнопку) или переходит обратно к вершине стека контроллера навигации, чтобы закрыть показанный модально UIViewController, новое поведение смахивания для закрытия может нарушить работу вашего приложения, поскольку ваш обработчик закрытия экрана не будет выполнен.
Например, в моем приложении Pomodoro Timer Pommie пользователь может перейти к подэкрану на экране «Настройки» и добавить или отредактировать профиль таймера (конфигурация для периодов работы / перерыва для конкретного вида задачи):




В случае с Pommie, я думаю, что это нормально (и безопасно), если пользователь закрывает весь экран настроек с помощью смахивания. Пользователи, вероятно, будут ожидать, что они могут закрыть экран одним движением, и я хочу, чтобы мои приложения работали правильно в iOS 13. Однако, я чувствую, что на экране «Добавить / изменить профиль таймера» нельзя давать пользователю закрывать экран смахиванием, так как существует риск потери изменений. Для пользователя может быть не совсем понятно что произойдет после такого закрытия.
Одна часть исправления этой проблемы — новое свойство UIViewController: isModalInPresentation . Из документации:

modalInPresentation устанавливается, когда вы хотите заставить экран стать модальным. Когда этот параметр включен, презентация будет предотвращать интерактивное закрытие и игнорировать события за пределами границ UIViewController, пока для этого параметра не будет установлено значение NO.

Чтобы получить поведение, аналогичное iOS 12, для моего экрана «Настройки» на iOS 13, я мог бы просто установить true для свойства isModalInPresentation у показанного модально UINavigationController. Если пользователь попытается смахнуть вниз, чтобы закрыть его, экран немного сместится, но будет сопротивляться действиям пользователя и не будет закрыт.
Свойство можно изменить в любое время, чтобы вы могли, например, разрешить закрытие, если пользователь еще не внес изменения, которые будут потеряны, если он явно не сохранил их. Но как только изменение было внесено, вы можете установить isModalInPresentation , чтобы предотвратить закрытие с помощью смахивания. Это заставит пользователя нажать кнопку «Отмена» или «Сохранить».

Обнаружение закрытия

Ресурсы

Видео WWDC 2019 станет лучшим местом для того, чтобы узнать, что изменилось в iOS 13, какие изменения нужно внести в свои приложения, чтобы они работали корректно при сборке в Xcode 11, и какие изменения вы можете внести, чтобы улучшить их, чтобы воспользоваться новыми функциями. Вот несколько видео для начала:

Заключение

До сих пор я не обнаружил никаких проблем с моими приложениями, созданными в Xcode 10 под iOS 13. Обратная совместимость здесь действительно работает. Я был немного удивлен, увидев изменение внешнего вида сгруппированной таблицы.
Сборки Xcode 11 нуждались в некоторых небольших исправлениях, чтобы иметь дело с изменениями в модальных представлениях, обсужденных в этом посте. Вероятно, будут изменения, которые я еще не обнаружил.
Тщательно протестируйте свои модальные презентации (особенно с панелями поиска)! Решите, хотите ли вы разрешить пользователю закрывать модальные экраны смахиванием, и используйте isModalInPresentation , чтобы получить поведение, необходимое для предотвращения случайной потери данных из-за ошибочного смахивания. Для большей гибкости и контроля используйте UIAdaptivePresentationControllerDelegate .

Читайте также: