AppDelegate отходит на второй план. SceneDelegate вступает во владение.

iPadOS 13 был выпущен во время WWDC 2019. Наконец, у iPad есть отдельная ОС.

Введение поддержки нескольких окон в iPadOS - это революционный шаг. Это позволяет нам одновременно открывать несколько экземпляров приложения.

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

Наша цель на сегодня

  • Знание того, как поддержка многооконного режима меняет жизненный цикл приложения.
  • UIScene API с высоты птичьего полета.
  • Реализовать поддержку нескольких окон двумя разными способами - с пользовательским вводом и с помощью перетаскивания. Мы будем разрабатывать приложение для редактирования фотографий для iOS и iPadOS, которое использует CIFilters на изображении человека-паука!

Не теряя больше времени, приступим.

Включение поддержки многооконного режима

Это просто, просто перейдите в навигатор проекта, откройте общие настройки и убедитесь, что установлен флажок поддерживать несколько окон.

Как только это будет сделано, в info.plist. Прежде чем мы углубимся в изменения и реализацию API, давайте обратимся к слону в комнате.

Поддержка нескольких окон не является поддержкой разделенного экрана

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

Изменения в AppDelegate и жизненном цикле приложения

Поддержка многооконного режима внесла серьезные изменения в класс AppDelegate. Сейчас он намного светлее. Вся тяжелая работа выполняется классом SceneDelegate. Если вы посмотрите на AppDelegate в любом проекте iOS 13 Xcode, вы увидите, что у него очень мало методов.

UIApplicationDelegate не уведомляется, когда приложение запускается и запускается в фоновом режиме в iOS 13 и более поздних версиях.

Недавно представленный протокол UIWindowSceneDelegate обрабатывает уведомления в нескольких окнах приложения.

Следующие свойства класса UIApplication объявлены устаревшими:

  • statusBarStyle
  • statusBarHidden
  • statusBarOrientation
  • open(_:options:completionHandler:)
  • keyWindows

Благодаря поддержке многооконного режима окна теперь являются сценами! Итак, начиная с iPadOS 13, все, что вы видите в переключателе приложений, представляет собой отдельную сцену.

UIScene API: взгляд с высоты птичьего полета

Поддержка нескольких окон использует UIScene API под капотом. Двумя наиболее важными классами этого API являются:

  • UIWindowScene - отвечает за управление несколькими окнами приложения.
  • UISceneSession - представляет постоянное состояние сцены. Несколько UIScenes хранят там определенную информацию, такую ​​как информация о роли и пользователе с сеансом сцены.

NSUserActivity используется для захвата состояния сцены. Это состояние используется для восстановления ранее использованной сцены или создания новой сцены с текущим просматриваемым контентом.

UIWindowSceneDelegate реализуется SceneDelegate. Этот протокол является новой записью в приложении, поскольку он содержит ссылки на UIWindow.

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

Не используете раскадровки? Вам необходимо вручную прикрепить UIWindow к сцене в классе SceneDelegate, как показано ниже:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }        window = UIWindow(frame: windowScene.coordinateSpace.bounds)        window?.windowScene = windowScene
}

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

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

Реализация

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

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

Следующий фрагмент кода программно добавляет на экран UIImageView:

let VCActivityType = "VCKey"
func setupImageView(){
photo = UIImageView(frame: .zero)
photo?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(photo!)
NSLayoutConstraint.activate([
photo!.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: marginConstant),
photo!.bottomAnchor.constraint(equalTo: self.stackView!.topAnchor, constant: -marginConstant),
photo!.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: marginConstant),
photo!.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -marginConstant),
])
}

Создание новой сцены по щелчку пользователя

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

let activity = NSUserActivity(activityType: VCActivityType) UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil, errorHandler: nil)

requestSceneSessionActivation отвечает за активацию существующей сцены или создание новой сцены.

Вот скриншот из приложения для iPad.

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

Создание новой сцены с помощью перетаскивания

Мы можем использовать API перетаскивания, передав NSUserActivity внутри DragItem NSItemProvider.

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

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

1. Соответствие UIDragInteractionDelegate

Нам нужно настроить взаимодействие перетаскивания на UIImageView.

photo?.isUserInteractionEnabled = true photo?.addInteraction(UIDragInteraction(delegate: self))

2. Реализация функции перетаскивания.

В приведенном ниже фрагменте мы передаем imageView NSItemProvider и регистрируем NSUserActivity с ним.

extension ViewController : UIDragInteractionDelegate{
    func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
        if let imageView = interaction.view as? UIImageView {
            guard let image = imageView.image else { return [] }
            let provider = NSItemProvider(object: image)
            
            let userActivity = NSUserActivity(activityType: VCActivityType)
            provider.registerObject(userActivity, visibility: .all)
            let item = UIDragItem(itemProvider: provider)
            return [item]
        }
        return []
    }
}

3. Добавление ActivityType в Info.plist

Наше приложение для iPad теперь готово для поддержки многооконного режима с помощью перетаскивания.

Мы установили CIFilters на изображениях, чтобы сравнивать одно и то же изображение с помощью разных фильтров.

Поддержка многооконности также совместима с Mac Catalyst.

Заключение

Итак, это все! Мы видели, как AppDelegate отходит на второй план и позволяет SceneDelegate выполнять основную работу по поднятию тяжестей, обрабатывая большинство событий жизненного цикла.

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

Полный исходный код этой статьи доступен на GitHub.

В следующих частях мы рассмотрим восстановление состояния и синхронизацию данных в нескольких окнах в iPadOS.

Вот и все. Надеюсь, вам понравилось.