На Лекции 6 курса Стэнфорда CS193p — «Developing Application for iOS» («Разработка приложений для iOS») вначале рассматриваются две короткие темы: Наблюдатели Свойства (Properties Observer) и @State. На Наблюдателях Свойства (Properties Observer), как синтаксической конструкции языке Swift, профессор долго не задерживается, а вот @State, этому маленькому временному хранилищу, используемое в вашем View.
Далее профессор полностью погружается в основную тему дня — Анимацию. Это великолепная Лекция на эту тему, нигде вы не найдете столько материала, собранного на тему анимации, и столько нюансов использования различных типов анимации. Рассматриваются четыре чрезвычайно важных аспекта Анимации:
- Неявная и явная анимация,
- Анимация Views (через их модификаторы ViewModifiers, которые реализуют Animatable протокол)
- Transitions (анимируют появление / исчезновение Views с помощью определенных ViewModifiers)
- Анимация Shapes (через Animatable протокол)
Когда пользователь смотрит на анимацию, он видит нечто, что уже изменилось в Model или уже изменилось где-то ещё или по крайней мере изменилось в @State.
Что-то, что уже произошло, это не может быть сделано другим способом. В противном случае, все переменные vars в вашей Model постоянно бы изменялись по мере продолжения анимации, а это просто несостоятельная архитектура, верно?
Итак, ваша Model изменяется, ваш View изменяется и происходит анимация этих изменений прямо перед глазами пользователя. Она показывает вам их очень недавнее прошлое.
Что в SwiftUI может быть анимировано?
Анимация в SwiftUI работает только для Views, которые находятся в контейнере, который уже на экране.
Анимировать можно появление и исчезновение Views и опять, если они находятся в контейнере, который уже на экране.
Также можно анимировать изменения в аргументах анимируемых модификаторов ViewModifiers наподобие .opacity (непрозрачность) и .rotation (вращение). Модификаторы подобные этим знают, как себя анимировать. Во второй половине Лекции 6 в демонстрационном примере создается один из этих модификаторов, свой собственный, изменения аргументов которого можно анимировать.
Также можно анимировать изменения в аргументах при создании геометрических фигур Shapes.
Если вы создаете геометрическую фигуру Shape с определенными аргументами, которые конфигурируете некоторым способом, а затем изменяете их, то можно анимировать переход геометрической фигуры Shape в новое состояние.
Между прочим, это все изменения, которые можно анимировать: просто модификаторы ViewModifiers, геометрические фигуры Shapes, и появление / исчезновение Views. Всё, ничего больше нет для анимации изменений.
Как же заставить анимацию начать анимировать?
Есть два способа сделать это.
Один — это неявная анимация, когда вы просто метите View и говорите: “Когда один из модификаторов этого View изменится, мы будем анимировать это изменение.” Такая неявная анимация будет автоматически запускаться каждый раз, когда модификаторы на этом View изменяются, она будет анимировать их изменение.
Другой способ — это явная анимация, когда вы собираетесь вызвать некоторый код, который приведет к некоторым изменениям в модификаторах ViewModifiers или геометрических фигурах Shapes или Views, которые собираются появиться на экране или исчезнуть с экрана.
Мы “оборачиваем” этот код путем вызова функции func withAnimation и внутри фигурных скобок {…} мы размещаем код, все изменения в котором, и будут анимированы: все аргументы модификаторов ViewModifiers, которые изменяются, все эти Views, которые появляются и исчезают. Все анимации будут происходить вместе в одной одновременной анимации.
Итак, это явная анимация.
Transitions определяют, как происходит анимация появление (arrival) и исчезновение (departure) Views. Помните, что эти Views должны находиться в контейнерах, которые уже находятся на экране.
Итак, transition — это ничего более, чем пара модификаторов ViewModifiers, и это всё. Один из этих модификаторов ViewModifiers модифицирует View до такого вида, каким, как предполагается, должен быть View, когда он находится на экране, а другой из модификаторов ViewModifier модифицирует View до такого вида, каким, как предполагается, должен быть View, когда его нет на экране. Другими словами, тогда, когда View ещё не появился на экране или тогда, когда он только что покинул экран.
Transitions API — это Type-erased, то есть “стертый ТИП”.
Мы используем структуру struct AnyTransition, которая “стирает ТИП” лежащий в основе модификаторов ViewModifiers и тем самым существенно облегчает работу с transitions.
Фактическая анимация создается ViewModifiers и Shapes. Каково их участие в целой анимационной системе?
По сути, система анимации делит продолжительность анимации
duration на крохотные кусочки в зависимости от “кривой анимации” curve. А затем просит Shapes и ViewModifiers, которые являются Animatable, нарисовать этот кусочек, нарисовать этот кусочек, нарисовать этот кусочек, и они рисуются снова и снова и снова и затем все это собирается вместе и превращается маленький фильм, что, собственно, и представляет собой анимацию.
Вот как это работает, невероятно элегантно и просто.
Взаимодействие между системой анимации и ViewModifiers и Shapes осуществляется с помощью единственной переменной var animatableData. Переменная var animatableData находится в протоколе Animatable, и это единственная там переменная var. Всё, что вам необходимо сделать — реализовать протокол Animatable, то есть создать переменную var animatableData. Если вы являетесь геометрической фигурой Shape или модификатором ViewModifier, то вы можете участвовать в этой анимации маленькими кусочками.
Все эти концепции были воплощены в демонстрационном приложении Memorize для анимации переворота карт, создания новой игры и начисления бонусов при быстром поиске «совпадающих» карт. Анимация — мощная подсистема SwiftUI, так что нам не понадобилось добавлять слишком много строк кода в наше приложение, чтобы оно делало такие сумасшедшие вещи.
Код демонстрационного примера для Лекции 6 находится на Github для iOS 13 в папке Memorize L6.
Русскоязычный неавторизованный конспект Лекции 6, хронометрированный через каждые 5 минут, и представленный в виде PDF-файла, который можно скачать и использовать offline, а также в формате Google Doc доступны на платной основе.