UIView.animate, вызываемое в didMoveToSuperview, неожиданно влияет на все подпредставления. - PullRequest
0 голосов
/ 17 апреля 2019

Я работаю над пользовательским UIView.Я получил все это работает должным образом, но мои требования к дизайну диктуют, что части представления должны анимироваться при нагрузке.

Мое представление настроено следующим образом, и я решил анимировать ограничения: UIView layout

Поэтому я назвал UIView.animate() в didMoveToSuperview() следующим образом:

override func didMoveToSuperview() {
    animateArrow()
}

private func animateArrow() {
    UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseOut, .autoreverse, .repeat], animations: {
        self.arrowLeadingConstraint.constant += 15
        self.layoutIfNeeded()
    }, completion: nil)
}

Я больше ничего не делаю.Сама по себе анимация влияет только на ведущее ограничение моего вида изображения стрелки.Как и ожидалось, и как следует.Я могу убедиться в этом, когда запускаю анимацию при взаимодействии с пользователем, как показано ниже.

Arrow animation on user interaction

Теперь проблема заключается в том, что при вызове из didMoveToSuperview() анимация как-то влияет на все подпредставления моего пользовательского UIView ...

Animation on didMoveToSuperview

Что я делаю не так?

Ответы [ 3 ]

1 голос
/ 17 апреля 2019

Назовите анимационный код здесь:

override func draw(_ rect: CGRect) {
    super.draw(rect)
    animateArrow()
}

Или

как предложено @ holex комментарий: выполнить передачу макета до:

private func animateArrow() { 
   self.layoutIfNeeded(); 
   self.arrowLeadingConstraint.constant = 31; 
   UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseOut, .autoreverse, .repeat]) { 
       self.layoutIfNeeded() 
   } 
}

Также добавьте наблюдателя в свой init, если анимация останавливается при нажатии home:

NotificationCenter.default.addObserver(self, selector: #selector(enteredForeground(_:)), name: .UIApplicationWillEnterForeground, object: nil)
1 голос
/ 17 апреля 2019

кажется, что дьявол находится в самом вашем animateArrow() методе, если вы немного измените метод, например, например. это:

private func animateArrow() {
    self.layoutIfNeeded()
    self.arrowLeadingConstraint.constant = 31 // = 15 + 16 from your original code

    UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseOut, .autoreverse, .repeat], animations: {

        self.layoutIfNeeded()
    }, completion: nil)
}

Тада , анимация будет работать должным образом, как вы ожидали.


почему ...?

Мое объяснение здесь может быть не академическим, но я надеюсь, что оно поможет читателям лучше понять.

Итак, вкратце, когда вы имеете дело с ограничениями, вы неявно имеете дело с набором предопределенных отношений между представлением и его окружением . вот почему вы не можете успешно анимировать отдельное ограничение (ваша первоначальная попытка), потому что только эти отношения являются анимируемыми в этом контексте, а не ограничения.

поэтому вы сможете анимировать обновление всех отношений только после вы определили новые ограничения для вашего макета - и в принципе за кулисами, которые могут привести анимировать кадр каждого затронутого вида за один раз.

вы можете прочитать больше о том, что такое ограничения и как оценка работает с Auto-Layout от Apple , если вы заинтересованы в этом.

1 голос
/ 17 апреля 2019

Использование didMoveToSuperview может быть не лучшей идеей.Перед запуском анимации необходимо убедиться, что макет для всех представлений на экране выполнен, что не всегда может быть верно для didMoveToSuperview.

. Я бы переместил триггер анимации внутри viewDidAppearв viewController или в didLayoutSubviews, который также находится в viewcontroller.

...