CABasicAnimation с UINavigatonController и пользовательскими анимациями идет не так - PullRequest
1 голос
/ 26 мая 2020

Здесь ниже вы можете увидеть результат моего кода. Обратите внимание, что второй раз, когда я нажимаю кнопку «Назад», анимация не работает.

С уважением

enter image description here

Я часами пытался понять, в чем проблема с моей анимацией, но не могу найти источник проблемы. Я безуспешно искал ответ в inte rnet. Итак, это моя проблема.

Я помещаю анимацию в finish(), если переход превышает 50% от view.bounds.width и cancel() в противном случае

Это мои шаги

  1. Обработка анимации до менее 50%
  2. Анимация возвращается
  3. Нажмите кнопку Назад в UINavigationItem и анимации работает быстро

Это мой код в Animator

class Animator: UIPercentDrivenInteractiveTransition, UIViewControllerAnimatedTransitioning {

    private var pausedTime: CFTimeInterval = 0
    private var isLayerBased: Bool  { operation == .pop  }

    let animationDuration = 1.0

    weak var storedContext: UIViewControllerContextTransitioning?

    var interactive: Bool = false
    var operation: UINavigationController.Operation = .push

    func handlePan(recognizer: UIScreenEdgePanGestureRecognizer)
    {
        // This part is only for get percent of the translation in the screen with the finger

        let translation = recognizer.translation(in: recognizer.view!.superview!)
        var progress: CGFloat = abs(translation.x / recognizer.view!.superview!.bounds.width)
        progress = min(max(progress, 0.01), 0.99)

        switch recognizer.state {
        case .changed:
            update(progress)

        case .cancelled, .ended:
            if progress < 0.5 {
                cancel()
            } else {
                finish()
            }
            interactive = false

        default:
            break
        }

    }

    override func update(_ percentComplete: CGFloat) {
        super.update(percentComplete)

        if isLayerBased {
            let animationProgress = TimeInterval(animationDuration) * TimeInterval(percentComplete)
            storedContext?.containerView.layer.timeOffset = pausedTime + animationProgress
        }
    }

    override func cancel() {
        if isLayerBased {
            restart(forFinishing: false)
        }
        super.cancel()
    }

    override func finish() {
        if isLayerBased {
            restart(forFinishing: true)
        }
        super.finish()
    }


    private func restart(forFinishing: Bool)
    {
        let transitionLayer = storedContext?.containerView.layer
        transitionLayer?.beginTime = CACurrentMediaTime()
        transitionLayer?.speed = forFinishing ? 1 : -1
    }


    func animateTransition(using transitionContext: UIViewControllerContextTransitioning)
    {
        if interactive && isLayerBased {
            let transitionLayer = transitionContext.containerView.layer

            self.pausedTime = transitionLayer.convertTime(CACurrentMediaTime(), from: nil)
            transitionLayer.speed = 0
            transitionLayer.timeOffset = pausedTime
        }
        self.storedContext = transitionContext

      if operation == .pop {
        let fromVC = transitionContext.viewController(forKey: .from)!
        let toVC = transitionContext.viewController(forKey: .to)!

        let animation = CABasicAnimation(keyPath: "transform")
        animation.fromValue = NSValue(caTransform3D: CATransform3DIdentity)
        animation.toValue = NSValue(caTransform3D: CATransform3DMakeScale(0.001, 0.001, 1))

        animation.duration = animationDuration
        animation.delegate = self

        fromVC.view.layer.removeAllAnimations()
        fromVC.view.layer.add(animation, forKey: nil)

      } else if operation == .push {

        // The Push Animation
        // ...
      }

   }
}


extension Animator: CAAnimationDelegate {

    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        if let context = self.storedContext
        {
            print("COMPLETE TRANSITION & ANIMATION STOP")
            context.completeTransition(!context.transitionWasCancelled)
        }
        self.storedContext = nil
    }
}

Это был мой аниматор class. В моем MainViewController единственный интересный код:

class MainViewController: UIViewController, UINavigationControllerDelegate {

    let animator = Animator()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationController?.delegate = self
    }

    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {

        animator.operation = operation
        return animador
    }

    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        if ! animator.interactive {
            return nil
        }
        return animador
    }

}

А в моем DetailViewController у меня это

class DetailViewController: UIViewController {

    weak var animator: Animator?

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        if let masterVC = navigationController!.viewControllers.first as? ViewController {
            animador = masterVC.animador
        }
        let pan = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(didPan(recognizer:)))
        pan.edges = .left
        view.addGestureRecognizer(pan)
    }

    @objc func didPan(recognizer: UIScreenEdgePanGestureRecognizer)
    {
        if recognizer.state == .began
        {
            animador?.interactive = true
            self.navigationController?.popViewController(animated: true)
        }
        animador?.handlePan(recognizer: recognizer)
    }

...