Лекция 10. Жесты. CS193P Spring 2021.

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

На прошлой Лекции 9 мы узнали многое о многопоточности. Мы также стали специалистами по технологии Drag & Drop (перетягивание и «сброс)», теперь пришло время использовать этот механизм для формирования нашего фонового изображения background. Мы хотим иметь возможность перетаскивать (Drag) изображение из Safari, “сбрасывать” его на наш документ и формировать background нашего документа. В таком сценарии мы будет отвечать только за “сброс” (Drop), перетаскивание (Drag) выполняется Safari. Так что нам даже не нужен модификатор .onDrag, нам достаточно иметь просто .onDrop.

В нашем documentBody уже есть модификатор .onDrop, предназначенный для «сброса» наших маленьких эмодзи из палитры, теперь мы заинтересованы также в получении URL-адресов изображения .url, а также самого изображения .image. Профессор показывает, как просто можно дополнять перечень «сбрасываемых» и загружаемых объектов в модификатор .onDrop.

Но дело в том, что сброс URL-адреса изображения предполагает последующую его загрузку из интернета, а это может занять несколько секунд или даже минуту при медленном интернете. В этом случае, если не предпринять надлежащие меры, наше приложение может просто «замереть», заставить пользователя нервничать и он может буквально “убить” наше приложение, a затем вообще стереть его со своего устройства. Необходимость любой ценой сохранить отзывчивость UI для пользователя вынуждает нас использовать многопоточность, о которой мы говорили на слайдах на прошлой лекции. На этом стэнфордском курсе использование многопоточности рассматривается исключительно в этом очень важном аспекте, связанным с НЕ блокировкой UI. Мы не будем блокировать UI, если все, что может заблокировать наш UI, будет выполняться на другом потоке. О том, как это можно сделать, было рассказано на прошлой Лекции 9, а на этой Лекции профессор конкретно демонстрирует это в приложении EmojiArt.

В задаче «сброса» изображения из Safari переплелись эти две важные технологии: Drag & Drop и многопоточность. Поэтому профессор Пол Хэгерти методично и подробно проводит через все этапы ее решения, попутно привлекая такие конструкции Swift и SwiftUI, как перечисление enum с ассоциированными данными, встроенный модификатор .overlay и сконструированный ViewOptionalImage.

После того, как фоновое изображение «сброшено» на наш документ EmojiArtDocument, мы должны уметь изменять его масштаб и перемещать по экрану. Делать это мы будем с помощью такого источника ввода информации как жесты Gesture, то есть с помощью пальцев.

Сначала профессор теоретически на слайдах рассматривает как дискретные жесты типа «двойного» Tap, так и НЕ-дискретные жесты типа Pinch и Pan, их особенности и тонкости реализации. 

Демонстрация жестов в приложении EmojiArt начинается с создания дискретного «двойного Tap» жеста, призванного «подогнать» размер документа EmojiArtDocument ко всему доступному пространству на экране. На примере этого в общем-то простого жеста профессор рассматривает все стадии «внедрения» «распознавания» жеста в ваш View через модификатор .gesture с функцией внутри, которая возвращает some Gesture, через создания самого жеста TapGesture, с модификатором .onEnded, воздействующим на @State переменную, изменения которой вызывают обновление View.

Но это просто «разминка» перед созданием НЕ-дискретного жеста  Pinch SwiftUI — MagnificationGesture), который позволяет увеличивать (zoom in) и уменьшать (zoom out) масштаб EmojiArt документа. Здесь уже при создании жеста MagnificationGesture() работают оба модификатора жеста:  .onEnded и .updating, и участвуют уже 3 переменных: @State переменная, @GestureState переменная и вычисляемая переменная, которая является комбинацией первых двух переменных и именно она определяет, как обновляется  View. Модификатор .onEnded устанавливает @State переменную при окончании жеста, а .updating модификатор обновляет @GestureState переменную на основании оперативной информации о положении пальцев на экране во время выполнения жеста, которая для жеста MagnificationGesture() — очень простая, это число CGFloat соответствующее текущему масштабу.

Абсолютно точно такая же схема работает и для жеста PanSwiftUI —  DragGesture()), за исключением того, что оперативной информации о положении пальца на экране во время выполнения жеста более сложная — это структура struct  и у нее есть location, то есть где в данный момент находится палец, также есть startLocation, то есть откуда начинался жест. Даже есть время time. Это время time обновляется каждый раз, когда происходит движение пальцев, так что вы можете узнать, как быстро движется палец.

Некоторые разработчики находят реализацию НЕ-дискретных жестов в SwiftUI. несколько запутанной, так как там задействован inout параметр из старого Мира. Если честно, то я больше нигде не видела такого четкого изложения всех аспектов использования НЕ-дискретных жестов в SwiftUI. На Лекции 10 представлен универсальный код для жестов Pinch и Pan, который практически в неизменном виде можно использовать и в вашем приложении.

На следующей Лекции 11 мы добавим в приложение EmojiArt UI, который вы видели раньше при демонстрации окончательной версии приложения EmojiArt  и в  котором мы могли бы давать имена различным палитрам, выбирать между ними, управлять их появлением и все такое. 

P.S. GCD почти полностью заменена встроенным в Swift асинхронным API, изложенным на WWDC 2021.

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

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

Лекция 10. Жесты. CS193P Spring 2021.: 2 комментария

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