Захват анимации контроллера

В статье я расскажу о странном поведении при вызове UIView.animate с флагом .repeated

Поступил запрос на создание экрана интро в приложении, в котором должна была присутствовать повторяющаяся анимация. Экран был сделан, анимация добавлена, но возникли непонятные артефакты на iPad. UIViewController в котором находится сам экран с анимацией презентовался с флагом .formSheet

func presentController() {
let blinkingController = BlinkingController()
blinkingController.preferredContentSize
= CGSize(width: 300, height: 600)

blinkingController.modalPresentationStyle = .formSheet
self.present(blinkingController, animated: true, completion: nil)
}

При презенте вьюконтроллера за ним появляется и едет тень, и неожиданным оказался тот факт, что после того как контроллер был показан, тень перешла в состояние непрерывного мерцания. В качестве подозреваемого сразу же оказалась повторяющаяся анимация вьюхи интро.

На картинке можно увидеть тестовый пример, успешно воспроизведенный по аналогии с тем, что возник в реальном проекте.

После недолгого исследования, выяснилось, что причиной такого поведения, является неудачный момент запуска повторяющейся анимации во вьюхе, которая принадлежит презентуемому контроллеру. Запуск повторяющейся анимации можно засунуть в один из методов жизненного цикла вью и вьюконтроллера, а именно:

  • viewDidLoad()
  • viewWillAppear()
  • willMove(toWindow:)
  • viewDidAppear()

Как оказалось, странное поведение появляется если засунуть запуск в метод willMove(toWindow:). Спросите, кому вообще - понадобилось что-то засовывать в этот метод? Ответ такой: в различных UI фреймворках c поддержкой MVVM на разных платформах есть понятие активации, деактивации экрана(например, в том же самом Caliburn.Micro есть соответствующие методы onActivate(), onDeactivate() в которых можно писать код, выполняющийся в моменты жизненного цикла вью и вьюмодели). В нашем проекте сигналом активации вьюмодели и вызов её метода onActivate() (в котором и размещался код триггерящий запуск анимации), запускается когда во вью вызывается willMove(toWindow:)

Были предприняты попытки понять откуда растут ноги и почему себя так странно ведет анимация тени контроллера. В частности была идея воспроизвести данное поведение, засунув блок, запускающий .repeated анимацию , в другой блок с обычной анимацией, но это дало обратный эффект, и тень контроллера перестала мерцать.

Если посмотреть иерархию вызовов методов вплоть до willMove(toWindow:) , то можно увидеть использование UITransitionView класс который использует Apple, но которого нет в публичном API, возможно проблема кроется именно там, но это требует дальнейших исследований.

Резюмируя, будьте осторожны с запуском повторяющихся анимаций во вью в методе willMove(toWindow:) , это может дать эффект который вы неожидали. В нашем случае проблема была решена запуском анимации в методе viewDidAppear()

Ссылки

--

--