Лекция 15. Интеграция с UIKit. CS193P Весна 2021г.

Это Лекция 15 курса Stanford CS193p, весна 2021 года.

Хотя название Лекции «Интеграции с UIKit«, задача этой Лекции — более широкая, заставить работать наше документо-ориентированное приложение EmojiArt на iPhone. До сих пор функциональные возможности приложения EmojiArt в большей степени были ориентированы на iPad. Запуск этого приложения на iPhone показал, что большая часть кода прекрасно адаптируется самой SwiftUI, например, popover на iPad автоматически преобразуется в полноэкранный sheet на iPhone, но не все работает так гладко.

Например, нам пришлось добавить ещё три способа получения фонового изображения background для нашего документа, ибо перетягивание его из Safari, находящегося тут же на экране iPad, в случае iPhone не работает.

Вот эти способы :

  • копирование  и вставка (Copy & Paste) изображения c  Pasteboard
  •  изображение с фотокамеры
  •  изображение из Библиотеки Фотографий (Photo Library)

Для этого мы должны были решить вопрос размещения множества кнопок Button, соответствующих различным способам получения фонового изображения документа, на панели инструментов toolbar, а также вопросы интеграции UIKit API  фотокамеры и Библиотеки Фотографий (Photo Library) в SwiftUI приложение.

 Адаптивная панель инструментов compactableToolbar.

В виду того, что на iPhone в портретном режиме не удается разместить множество кнопок Button из-за нехватки места, была придумана и реализована компактная панель инструментов compactableToolbar. Она представляет собой обычную панель инструментов toolbar, которая размещает множество кнопок Button, представленных @ViewBuilder, на панели инструментов toolbar, за исключением случая, когда не хватает места, в этом случае все кнопки размещаются в контекстном меню для одной единственной кнопки.

Факт нехватки достаточного места определяется с помощью концепции Size Class, которая существует на iOS устройствах как по горизонтали, так и по вертикали. Size Class — это просто перечисление enum, в котором два значения: compact и regular. Использовании концепции Size Class в SwiftUI коде намного упрощает написание кода и делает это единообразно во всех приложениях по сравнению с попыткой посмотреть на фактическое количество пикселей, когда вы можете прийти к не совсем правильным решениям при определенных обстоятельствах.

Компактная панель инструментов compactableToolbar реализована в виде классического ViewModifier с @ViewBuilder замыканием в качестве аргумента. 

Как работает UIKit.

UIKit — это старый способ разработки приложений для iOS. Когда появился SwiftUI, то он в значительной степени делал всё, что делает UIKit, но есть кое-что, что мы все же хотели бы интегрировать в SwiftUI из UIKit, в первую очередь, это касается фотокамеры, которую мы будем интегрировать в наше приложение EmojiArt. Кроме того, есть код, написанный множеством разработчиков, который также желательно интегрировать в SwiftUI. К счастью, это довольно просто сделать, и мы увидим, как именно это делается. 

В UIKit нет MVVM, вместо этого там то, что  называется MVC (Model View  Controller). В MVC архитектуре Views как бы сгруппированы вместе и управляются тем, что называется Controller.

В SwiftUI у нас нет никаких Controllers, там Views  — это просто Views, и мы представляем их на экране, когда хотим. Но в UIKit Views размещаются на экране совершенно по-другому. По сути, мы представляем на экране Controller, а уже Controller управляет своими Views. Из-за того, что у нас есть Controller интеграция между SwiftUI и UIKit требует 2-х точек интеграции.

Эти две точки интеграции очень похожи, одна из них — UIViewRepresentable, это SwiftUI View, которое представляет UIKit  View, а другая точка — UIViewControllerRepresentable, также SwiftUI View, но представляющее UIKit Controller и все Views, которыми этот Controller управляет.

Кроме того, UIKit является объектно-ориентированным. Это не функциональное программирование. Это совершенно другой Мир, он не декларативный, он не реактивный, ничего из этого в этом Мире нет. Он интенсивно использует концепцию, называемую делегированием (delegation).  Поэтому, выполняя интеграцию между SwiftUI и UIKit, мы должны обеспечить делегатом delegate наши UIKit View или Controller, чтобы они могли полноценно функционировать, потому что в большинстве случаев им нужен этот делегат delegate, чтобы делать что-то по существу.

Надо сказать, что Apple создала на удивление простой API для такой интеграции SwiftUI и UIKit и Пол Хэгерти подробно рассматривает его для интеграции UIKit  API фотокамеры и UIKit API Библиотеки Фотографий (Photo Library) в SwiftUI приложение EmojiArt. В нем предлагается добавить еще один способ получения фонового изображения непосредственно с фотокамеры (.camera) или из библиотеки фотографий (.photoLibrary). Для фотокамеры используется UIImagePickerController, а для библиотеки фотографий новый PHPicker, который появился только в iOS 14.

В обоих случаях для интеграции используется UIViewControllerRepresentable. Оба UIKit API имеют делегата delegate, так что вы наглядно сможете увидеть, как они интегрируется в SwiftUI. В роли делегата delegate выступает координатор Coordinator, который извлекает информацию из методов делегата delegate с помощью замыкания.

Код демонстрационного примера для Лекции 15 находится на Github для iOS 14 в папке  EmojiArtL 15.

Русскоязычный неавторизованный конспект Лекции 15, иллюстрированный, хронометрированный и представленный в виде PDF-файла, который можно скачать и использовать offline, а также в формате Google Doc доступны на платной основе.