Анимированное изменение макета с одновременным перекрестным растворением вида
У меня есть UIView
содержащий ряд UILabel
позиционируется с AutoLayout.
При повороте устройства я переключаю ограничения макета, чтобы анимировать метки в новые местоположения (ограничения макета выводятся из Layout
объект, который присваивается виду):
override func viewWillTransition(
to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator
) {
super.viewWillTransition(to: size, with: coordinator)
let isLandscape = size.width > size.height
let layout = isLandscape ? layoutLandscape : layoutPortrait
coordinator.animate(
alongsideTransition: { _ in self.layoutView.setLayout(layout) },
completion: nil
)
}
Этот эффект хорошо работает и обеспечивает плавный переход между различными макетами контента, оптимизированными для альбомной и портретной ориентации.
Однако во время перехода размер меток также изменяется из-за ограничений макета. Заметно, что хотя переход позиций меток происходит постепенно, формы меток меняются сразу в начале перехода, а затем они анимируются до своей конечной позиции.
Я хотел бы иметь возможность перекрестного смещения меток между их начальной и конечной формами, чтобы это происходило вместе с анимацией положения.
В идеале, я хотел бы сделать это таким же образом, чтобы добиться анимации положения, когда код обновления макета не учитывает, будет ли изменение анимировано или нет. Я ожидаю, что хотя бы небольшой компромисс может потребоваться, хотя!
Спасибо.
Я довольно близок к рабочему решению по этому вопросу. Это еще не материал для ответов, поэтому я буду помещать здесь заметки как "Материал, который я пробовал" - если он станет ответом, я передам его одному.
Фантастическая статья на objc.io показывает, как вы можете создать UILabel
подкласс, который скажет свою поддержку CALayer
оживить изменения его content
собственность с использованием CATransition
, Это самое чистое волшебство, но на удивление оно имеет смысл, и вы подозреваете, что, возможно, вы тоже можете надеть шляпу волшебника.
Итак, вы получите что-то вроде этого:
class ContentAnimatedLabel: UILabel {
override func action(for layer: CALayer, forKey event: String) -> CAAction? {
guard event == "contents" else {
return super.action(for: layer, forKey: event)
}
let transition = CATransition()
// Oh noes! A hard coded made up value!
// And where do we get the timing curve?! Curses.
transition.duration = 0.3
return transition
}
}
И, насколько это возможно, это работает! Содержимое меток красиво выгорает, а их положение интерполируется. И это выглядит довольно грандиозно.
Проблема в том, что этот код не знает, вызывается ли он из блока анимации. Действительно, он должен возвращать переход только при вызове из блока анимации и должен копировать свойства анимации, такие как длительность и время, из свойств, заданных блоком анимации.
Статья предлагает решение этой проблемы...
Реализация механизма анимации UIView является закрытой, поэтому нет простого флага, который мы можем проверить, чтобы увидеть, анимируется ли в данный момент представление, однако мы знаем одно: при анимации actionForLayer: forKey: UIView будет возвращать действительные CAActions для своих ключей свойства анимации, и когда не анимируется, он вернет [NSNull null] для них. Если мы просто выберем подходящий ключ, мы можем запросить UIView, чтобы выяснить, предоставляет ли он в данный момент действие для этого ключа, и использовать его, чтобы определить наш ответ для нашего пользовательского ключа. Мы будем использовать ключ "backgroundColor", поскольку это свойство UIView, которое обычно поддерживает анимацию, и, как мы знаем, возвращает CABasicAnimation в качестве своего действия (начиная с iOS 8, некоторые свойства, такие как "bounds", вместо этого возвращают закрытый класс, так что следите)
… Но это, похоже, не работает, по крайней мере, для iOS 11, на котором я тестирую.
когда UIKit
просит действия для content
ключ, как будто блок анимации закончен. Запрашиваемая super
действие, используемое другими ключами, всегда ничего не возвращает. Таким образом, у вас ничего не осталось, чтобы получить соответствующие параметры перехода.