PendingAnimationHandler
unknown
swift
3 years ago
4.4 kB
4
Indexable
class PendingAnimationHandler {
private var animationsCount: Int = 0
private let lock = NSLock()
private var pendingCompletions: [() -> Void] = []
private var isAnimationRunning: Bool { animationsCount != 0 }
var debugIsAnimationRunning: Bool {
animationsCount != 0
}
func animationStarted() {
lock.critical {
animationsCount += 1
}
}
func animationEnded() {
lock.critical {
animationsCount -= 1
if !isAnimationRunning {
pendingCompletions.forEach { $0() }
pendingCompletions = []
}
}
}
func afterAnimations(_ closure: @escaping () -> Void) {
lock.critical {
if isAnimationRunning {
pendingCompletions.append(closure)
} else {
closure()
}
}
}
}
final class PollAnimationViewController: UIViewController {
...
let animationsHelper = PendingAnimationHandler()
...
@objc private func someFuncTriggeresUpdate() {
var nextView: UIView
...
// Обновляем. Анимация - 2 секунды
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
cell.containerView.swap(to: nextView, animated: true, using: animationsHelper)
}
// имитируем обновление tabelView
DispatchQueue.main.asyncAfter(deadline: .now() + 1.1) {
let numbers = [1, 2, 3]
.map { _ -> Int in Int.random(in: 0...100) }
.map { "\($0)" }
animationsHelper.afterAnimations {
self.viewModel = ViewModel(titles: numbers)
tableView.reloadData()
}
}
}
}
class ContainerView: UIView {
var delegate: ContainerViewDelegate?
...
func swap(
to nextView: UIView,
animated: Bool,
using animationsHelper: PendingAnimationHandler
) {
guard shouldChangeView(to: nextView) else { return }
layoutNextView(nextView: nextView)
if animated { animateChange(nextView: nextView, duration: 2, using: animationsHelper) }
else { changeWithoutAnimation(nextView: nextView) }
}
private func layoutNextView(nextView: UIView) {
nextView.translatesAutoresizingMaskIntoConstraints = false
addSubview(nextView)
NSLayoutConstraint.activate([
nextView.leadingAnchor.constraint(equalTo: leadingAnchor),
nextView.trailingAnchor.constraint(equalTo: trailingAnchor),
nextView.topAnchor.constraint(equalTo: topAnchor),
])
bottomC?.isActive = false
layoutIfNeeded()
}
private func animateChange(nextView: UIView, duration: Int, using animationsHelper: PendingAnimationHandler) {
animationsHelper.animationStarted()
let heightConstraint = heightAnchor.constraint(equalToConstant: 30)
heightConstraint.isActive = true
nextView.alpha = 0
nextView.transform = CGAffineTransform(
translationX: 0,
y: nextView.frame.height
)
UIView.animate(withDuration: TimeInterval(duration)) { [weak self] in
guard let `self` = self else { return }
self.contentView.alpha = 0
self.contentView.transform = CGAffineTransform(
translationX: 0,
y: self.contentView.frame.height
)
nextView.transform = CGAffineTransform(
translationX: 0,
y: 0
)
nextView.alpha = 1
self.delegate?.containerViewNeedsLayout(self, height: nextView.frame.height, animated: true)
self.superview?.layoutIfNeeded()
} completion: { [weak self] _ in
guard let `self` = self else { return }
self.contentView.removeFromSuperview()
heightConstraint.isActive = false
self.bottomC = nextView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
self.bottomC?.isActive = true
self._contentView = nextView
animationsHelper.animationEnded()
}
}
private func changeWithoutAnimation(nextView: UIView) { ... }
private func shouldChangeView(to nextView: UIView) -> Bool { !(nextView === contentView) }
}Editor is loading...