iOS 10: нове в створення анімацій



Не так давно на 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 надає нові протоколи:



UIViewAnimating

Цей протокол відповідає за основні дії, які ми можемо виконати з аніматором: запуск, пауза, зупинка, перемотування, отримання поточного стану і позиції анімації. Більше інформації можна отримати в документації. Зупинимося на парі методів і властивостей трохи докладніше:

Перемотування анімації (scrubbing)
var fractionComplete: CGFloat { get set }

animator.fractionComplete = fraction

За допомогою такої властивості можна поетапно просуватися по анімації вперед/назад, отримуючи її стан у вибраний момент часу. Завдяки такій можливості користувач може взаємодіяти з інтерфейсом як з чимось живим і інтерактивним.

ПрикладІконка робота обертається і збільшується, рухаючись по горизонталі вправо. Спочатку анімація на паузі. На іконку додані UIPanGestureRecognizer (при виклику виставляє fraction для анімації) і UITapGestureRecognizer (при натисканні анімація відтворюється).

Спочатку тягнемо іконку праворуч і вручну проматываем анімацію (UIPanGestureRecognizer). Потім тапа по іконці і дивимося на анімацію, яка відтворюється сама (UITapGestureRecognizer).




Завершення анімації
func finishAnimation(at: UIViewAnimatingPosition)

Вхідний параметр at показує, в якій позиції повністю завершиться анімація.
Варіанти значень enum UIViewAnimatingPosition:

  • start;
  • end;
  • current (поточна позиція — будь-яка точка циклу анімації).
Параметр позиції може виявитися дуже корисним, коли потрібно закінчити анімацію в нестандартному стані. Наприклад, в середині циклу або на останньому кадрі.

(Тимчасова) зупинка анімації
func stopAnimation(_ withoutFinishing: Bool) 

Параметр withoutFinishing дозволяє зупинити анімацію повністю (як при використанні finishAnimation), або перевести в стан stopped, з якого анімацію можна продовжити з поточного місця.

Стан анімації
  • inactive
    Початковий стан — кожний новостворений об'єкт-аніматор починає з нього. Після завершення анімації (finishAnimation) він повертається до цього ж стану.
  • активний
    Аніматор переходить в такий стан після викликів startAnimation() або pauseAnimation().
  • stopped
    Стан аніматора після виклику stopAnimation, де withoutFinishing == false.


Наочне зображення зміни стану:



І ще кілька цікавих властивостей UIViewAnimating:

// Можна анімацію ставити на паузу або зупиняти
var isInterruptible: Bool 

// Спосіб обробки тачей (за замовчуванням false)
var isManualHitTestingEnabled: Bool 

При значенні за замовчуванням анимируемый об'єкт буде отримувати всі дотики користувача. Якщо виставити isManualHitTestingEnabled = true, то всі тачі буде отримувати не об'єкт у своєму поточному стані (presentation layer), а його модельний шар (model layer), тобто у фінальному стані, як ніби анімація вже скінчилася.

Прикладанімації виставлений isManualHitTestingEnabled = true. На іконку додано UITapGestureRecognizer (при натисканні текст лейблу внизу зміниться на Received touch). Як видно, іконка в русі не отримує торкання користувача, але якщо натиснути на передбачуване місце закінчення анімації (сама права частина поля), селектор спрацює, ніби ікона знаходиться саме там.



UIViewImplicitlyAnimating

Цей протокол успадковується від попереднього (UIViewAnimating) і надає важливу частину функціоналу: додавання анімацій через блок і створення completion block.

Одна з нових можливостей — це продовження анімації після паузи або зупинки, причому зі зміною timing function.

let newParameters = UICubicTimingParameters(animationCurve: .easeOut)
animator.continueAnimation(withTimingParameters: newParameters, durationFactor: 2.0)

Це може стати в нагоді, якщо потрібно змінити темп анімації після взаємодії з користувачем (зупинення чи призупинення анімації).

UITimingCurveProvider

Використовується при створенні об'єкта UIViewPropertyAnimator для установки timing function через UISpringTimingParameters або UICubicTimingParameters.



UICubicTimingParameters
Зараз UIKIt дає нам тільки чотири старі добрі функції швидкості (easeInOut, easeIn, easeOut, linear).
Але в новому API з'явився простір для фантазії дизайнерів — можливість створювати свої timing functions по двом контрольним точкам:



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 дозволяють отримати пружне поведінку.

Тут цікавим моментом є те, що швидкість тепер вектор, а не скаляр. Раніше швидкість була спрямована уздовж лінії на відміну від нового 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.

<habracut/>
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.