Лекция 10. EmojiArt. CS193P Spring 2023.

Ниже представлен небольшой фрагмент Лекции 10 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 10 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны на платной основе.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

Демо. “Сброс” Drop фонового изображения background

Давай сделаем “сброс”  (drop) URL-адреса фонового изображения background. Это действительно очень очень легко. 
Куда мы хотим это “сбросить”?
По сути, мы хотим “сбросить” его поверх нашего ZStack, это наш документ.
Итак, модификатор .dropDestination.
Что мы сбрасываем?
URL-адрес:

Обратите внимание на этот URL.self, когда вы пишите .self на нижнем регистре, то это означает сам ТИП. Таким образом, я передаю .dropDestination ТИП URL.self в качестве аргумента, так что мой документ знает, что мы ожидаем при “сбросе”. В замыкании у нас есть массив URL-адресов urls и место “сброса” location.
Для фонового изображения background нам неважно место “сброса” location, но это нас явно интересует при “сбросе” эмодзи (смайликов), и мы должны поработать с этим location.
Это замыкание должно вернуть информацию о том, был ли “сброс” успешным, потому что некоторые “сбросы” на этот View могут вас не устраивать. Тогда вам придется сказать:”НЕТ, лети обратно, туда, откуда пришел”.
Я собираюсь вернуть return функцию func drop (urls, at: location, in: geometry), которую я собираюсь написать через секунду:

Давайте создадим private func drop для “сброса” URL-адресов urls, которые представляют собой массив [URL] в точке location, которая является CGPoint, в in geometry, которая является GeometryProxy и возвращает Bool, который и определяет, успешно ли произошел “сброс”:

Я разместил этот код в отдельной функции func потому, что я не люблю, когда в моих переменных var и особенно в var documentBody скапливается куча мусора, поэтому я хочу вынести этот код за пределы var documentBody. Никаких других причин нет, вы можете этого не делать.
Так что же мне делать в этой функции func drop?
Я собираюсь первым делом получить первый URL-адрес url, поэтому напишу:

Я не думаю, что это может случиться, но если по какой-то причине массив urls окажется пуст, я не хочу, чтобы произошло аварийное завершение приложения, поэтому использую if let.
Я не уверен, что это возможно и не думаю, что dropDestination вызовет ваше замыкание с пустым массивом urls. Я не знаю, почему бы вообще он мог это сделать, но просто из осторожности я безопасным образом получу первый элемент в массиве urls и использую его в качестве своего фонового изображения background.
Итак, мне нужно вызвать для документа document функцию Намерения (Intent) setBackground для этого URL-адрес url.
В этом случае я собираюсь вернуть return true.
В противном случае я верну return false:

Давайте попробуем это.

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

Еще одна вещь, которую вы можете сделать, это посмотреть на три точки . . . вверху экрана и увидеть там опцию «Add Another Window» (“Добавить еще одно окно”).
Да,я добавляю Safari:

Теперь у меня есть два окна на экране одновременно, и я могу очень просто выполнять Drag&Drop между ними.
Чтобы получить такую функциональность, вам нужно включить на вашем iPad опцию под названием “Stage Manager” ( “Менеджер сцены”). Один из способ сделать это — пойти в настройки Settings, там выбрать опцию “Multitasking & Gestures” и выбрать “Stage Manager”:

Другой способ включить “Stage Manager” ( “Менеджер сцены”) выполняется через Control Center, поищите в интернете: “Как включить Stage Manager?” 
В Safari я перешел на сайт поисковой системы Google и стал искать countryside.cartoon.background (фоновые изображения сельской местности  из мультфильмов), потому что это мои любимые фоновые изображения, изображения сельской местности:

Первым я вижу то изображение, которое я показывал вам раньше. Но мы можем выбрать любое из них. 
Вы просто нажимаете на нем и удерживаете, чтобы выбрать его, a потом можете начать перетаскивать его повсюду. Вы можете перетащить его и в наше приложение. Когда вы это сделали, смотрите, что произошло — посмотрите на этот маленький зеленый плюс “+”, он говорит вам, что вы можете “сбросить” сюда это изображение. Но заметьте, если я спущусь вниз, к эмодзи, маленький зеленый плюс “+” исчезает, его уже нет. Потому что там другой View, это мой ScrollingView и у него нет dropDestination. Поднимаемся наверх, и я получаю маленький зеленый плюс “+”. Давайте “сбросим” сюда выбранное фоновое изображение.
Интересно, что это не работает, “сброс” не произошел:

Я точно знаю, в чем проблема. Это проблема, с которой вы часто будете сталкиваться. Это действительно простая, но фундаментальная проблема.
Нет ничего неправильного с кодом, который мы написали для “сброса” фонового изображения background.
Проблема в коде, который заставляет нашу MVVM работать. 
Где наш @Published

Мы нигде не публикуем какие-либо изменения, поэтому модель EmojiArt изменилась, мы установили фоновое изображение background, все заработало, отлично, но мы никогда не публиковали наши изменения. Следовательно, это не приводило к перерисовке нашего View и не просит AsyncImage перерисовывать фонового изображения background. Вот и все.
Хороший урок.
Открываем другое окно. Давайте выберем что-нибудь, например, этого парня, перетащим его сюда. Видите? Наш красивый зеленый плюс — это нормально. “Сбрасываем” там.
 

Вот наше фоновое изображение. 
Давайте еще что-нибудь возьмём. Это большое изображение, поэтому его загрузка заняла секунду.
Но все время, пока оно загружалось, наш UI не был блокирован. Все происходило асинхронно. 
Это круто: у нас есть работающий Drag & Drop.

. . . . . . . . . . . . . .

Это небольшой фрагмент Лекции 10.
На Лекции 10 рассматриваются следующие вопросы:

  • Демонстрация готового проекта Emoji Art
  • Новый проект Emoji Art
  • Model
  • ViewModel
  • View. Две составные части
  • Структура struct ScrollingEmojis
  • Расширения extension. Файл Extensions.swift
  • Функции map и String.init
  • Показ на экране ScrollingEmojis
  • Формирование documentBody
  • ViewModel в EmojiArtDocumentView
  • Структура Emoji должна быть Identifiable
  • Добавление эмодзи func addEmoji
  • Шрифт font для EmojiArt.Emoji
  • Местоположение position для EmojiArt.Emoji
  • Демо с двумя эмодзи
  • Drag & Drop. AsyncImage.
  • Drag & Drop. Теория
  • Демо. “Сброс” Drop фонового изображения background
  • Демо. Drag & Drop эмодзи (смайликов). Пользовательский Transferable
  • Демо. “Сброс” эмодзи (смайликов). Разные системы координат

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

С полным перечнем Лекций и Домашних Заданий Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI» на русском языке можно познакомиться здесь.