![](https://habrastorage.org/files/8e2/be1/d56/8e2be1d56402499595bb3a961078b8fe.gif)
Не так давно на WWDC 2016 был анонсирован обновленный интерфейс работы с интерактивными анимациями в iOS 10: теперь у разработчиков появился гибкий инструмент их создания, управления и модификации. В этой статье речь пойдет о том, какие произошли изменения и что из себя представляет новое API.
Центральным классом нового API является UIViewPropertyAnimator, предоставляющий свой функционал на основе двух протоколов UIViewAnimating и UIViewImplicitlyAnimating, которые будут рассмотрены ниже.
UIViewPropertyAnimator
С помощью данного класса можно:
- создавать интерактивные анимации (их можно останавливать, ставить на паузу, прерывать, обрабатывать касания пользователя);
- модифицировать уже запущенные;
- перематывать текущую анимацию на любой момент;
- проигрывать в обратном порядке;
- настраивать timing functions.
Timing functions
Под timing functions (или easing functions) понимаются функции скорости анимации, которые влияют на темп изменения того или иного анимируемого свойства. Сейчас поддерживается четыре типа: easeInOut, easeIn, easeOut, linear.
Пример использования:
// Скорость анимации
let parameters = UICubicTimingParameters(animationCurve: .easeIn)
// Конфигурирование аниматора с учетом длительности и скорости
let animator = UIViewPropertyAnimator(duration: 5.0, timingParameters: parameters)
// Набор анимаций (как во всем знакомом UIView.animate(withDuration:, animations:))
animator.addAnimations {
view.center = CGPoint(x: 150, y: 200)
view.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI_2))
view.backgroundColor = UIColor.red()
}
// Completion block
animator.addCompletion { _ in
view.backgroundColor = UIColor.orange()
}
// Запуск проигрывания анимации
animator.startAnimation()
Может показаться, что такая реализация более многословна по сравнению со старым API, зато теперь есть аниматор, который можно реиспользовать/изменять/сохранять и наслаждаться остальными преимуществами наличия цельного объекта.
Также Apple предоставляет новые протоколы:
![](https://habrastorage.org/files/2fc/ca1/acf/2fcca1acfc9946fe8a67ad1afaf8fd94.png)
UIViewAnimating
Этот протокол отвечает за основные действия, которые мы можем выполнить с аниматором: запуск, пауза, остановка, перемотка, получение текущего состояния и позиции анимации. Больше информации можно получить в документации. Остановимся на паре методов и свойств чуть подробнее:
Перемотка анимации (scrubbing)
var fractionComplete: CGFloat { get set }
animator.fractionComplete = fraction
С помощью такого свойства можно поэтапно продвигаться по анимации вперед/назад, получая ее состояние в выбранный момент времени. Благодаря такой возможности пользователь может взаимодействовать с интерфейсом как с чем-то живым и интерактивным.
Пример
Иконка робота вращается и увеличивается, двигаясь по горизонтали вправо. Изначально анимация на паузе. На иконку добавлены UIPanGestureRecognizer (при вызове выставляет fraction для анимации) и UITapGestureRecognizer (при тапе анимация проигрывается).
Сначала тянем иконку вправо и вручную проматываем анимацию (UIPanGestureRecognizer). Затем тапаем по иконке и смотрим на анимацию, которая проигрывается сама (UITapGestureRecognizer).
![](https://habrastorage.org/files/120/ee1/cdb/120ee1cdb55044ea9db667b75092dc3e.gif)
Сначала тянем иконку вправо и вручную проматываем анимацию (UIPanGestureRecognizer). Затем тапаем по иконке и смотрим на анимацию, которая проигрывается сама (UITapGestureRecognizer).
![](https://habrastorage.org/files/120/ee1/cdb/120ee1cdb55044ea9db667b75092dc3e.gif)
Завершение анимации
func finishAnimation(at: UIViewAnimatingPosition)
Входной параметр at показывает, в какой позиции полностью завершится анимация.
Варианты значений enum UIViewAnimatingPosition:
- start;
- end;
- current (текущая позиция — любая точка цикла анимации).
Параметр позиции может оказаться очень полезным, когда потребуется закончить анимацию в нестандартном состоянии. Например, в середине цикла или на последнем кадре.
(Временная) остановка анимации
func stopAnimation(_ withoutFinishing: Bool)
Параметр withoutFinishing позволяет либо остановить анимацию полностью (как при использовании finishAnimation), либо перевести в состояние stopped, из которого анимацию можно продолжить с текущего места.
Состояние анимации
- inactive
Начальное состояние — каждый новый созданный объект-аниматор начинает с него. После завершения анимации (finishAnimation) он возвращается к этому же состоянию. - active
Аниматор переходит в такое состояние после вызовов startAnimation() или pauseAnimation(). - stopped
Состояние аниматора после вызова stopAnimation, где withoutFinishing == false.
Наглядное изображение изменения состояния:
![](https://habrastorage.org/files/d84/fe8/34f/d84fe834ffe949f2b6e7643da42029dc.png)
И еще несколько интересных свойств UIViewAnimating:
// Можно ли анимацию ставить на паузу или останавливать
var isInterruptible: Bool ?
// Способ обработки тачей (по умолчанию false)
var isManualHitTestingEnabled: Bool
??При значении по умолчанию анимируемый объект будет получать все касания пользователя. ?Если выставить isManualHitTestingEnabled = true, то все тачи будет получать не объект в своем текущем состоянии (presentation layer), а его модельный слой (model layer), то есть в финальном состоянии, как будто анимация уже кончилась.
Пример
Для этой анимации выставлен isManualHitTestingEnabled = true. На иконку добавлен UITapGestureRecognizer (при тапе текст лейбла внизу изменится на Received touch). Как видно, иконка в движении не получает касания пользователя, но если тапнуть на предполагаемое место окончания анимации (самая правая часть поля), селектор сработает, как будто иконка находится именно там.
![](https://habrastorage.org/files/1ce/917/3fc/1ce9173fcb284a0ab78b70287528a118.gif)
![](https://habrastorage.org/files/1ce/917/3fc/1ce9173fcb284a0ab78b70287528a118.gif)
UIViewImplicitlyAnimating
Этот протокол наследуется от предыдущего (UIViewAnimating) и предоставляет важную часть функционала: добавление анимаций через блок и создание completion block.
Одна из новых возможностей — это продолжение анимации после паузы или остановки, причем с изменением timing function.
let newParameters = UICubicTimingParameters(animationCurve: .easeOut)
animator.continueAnimation(withTimingParameters: newParameters, durationFactor: 2.0)
Это может пригодиться, если нужно изменить темп анимации после взаимодействия с пользователем (остановки или паузы анимации).
UITimingCurveProvider
Используется при создании объекта UIViewPropertyAnimator для установки timing function через UISpringTimingParameters или UICubicTimingParameters.
![](https://habrastorage.org/files/c4b/3e9/85d/c4b3e985da4b4b4db7dfea93bd8db6d9.png)
UICubicTimingParameters
Сейчас UIKIt дает нам только четыре старые добрые функции скорости (easeInOut, easeIn, easeOut, linear).
Но в новом API появился простор для фантазии дизайнеров — возможность создавать свои timing functions по двум контрольным точкам:
![](https://habrastorage.org/files/b90/a1b/7e8/b90a1b7e844f4897b19b6a1939f99c0a.png)
let controlPoint1 = CGPoint(x: 0.2, y: 0.1)
let controlPoint2 = CGPoint(x: 0.8, y: 0.8)
let parameters = UICubicTimingParameters(controlPoint1: controlPoint1, controlPoint2: controlPoint2)
animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: parameters)
UISpringTimingParameters
UISpringTimingParameters позволяют получить упругое поведение.![](https://habrastorage.org/files/3d1/18b/526/3d118b526d204429ab53f534166f2c80.gif)
![](https://habrastorage.org/files/3d1/18b/526/3d118b526d204429ab53f534166f2c80.gif)
Здесь интересным моментом является то, что скорость теперь вектор, а не скаляр. Раньше скорость была направлена вдоль линии в отличие от нового API, где расчет идет по двум осям, а значит, анимация выглядит более натурально.
А еще Apple открыли для использования spring equation, то есть теперь можно устанавливать любые коэффициенты и настройки для UISpringTimingParameters, но надо учитывать, что в этом случае длительность игнорируется и высчитывается соответственно параметрам:
// Spring equation
init(mass: CGFloat, stiffness: CGFloat, damping: CGFloat, initialVelocity velocity: CGVector)
Заключение
После просмотра лекции и нескольких примеров кода остается общее впечатление, что добавилось разнообразие и возможности для экспериментов с timing functions и модификацией анимаций, а API стало более многословным, но гибким. Будем надеяться, разработчикам теперь станет еще проще и приятнее добавлять интерактив в свои приложения!
Подробнее познакомиться с примерами можно в лекции Advances in UIKit Animations and Transitions.
Поделиться с друзьями
grayich
На завлекательной анимации тень и блик неправильные.
А ведь подобные анимации могут откладываться паттернами в детских и других несформированных разумах. К чему это может привести?