PendingAnimationHandler
unknown
swift
3 years ago
4.4 kB
1
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...