首页 > 解决方案 > 取消 UIPercentDrivenInteractiveTransition 时如何禁用 UINavigationBar 动画

问题描述

我已经实现了一个自定义的交互式过渡,用于检测中间屏幕的滑动UIPanGestureRecognizer以及.UIPercentDrivenInteractiveTransitionUIViewControllerAnimatedTransitioning

现在,我在UIPercentDrivenInteractiveTransition取消时遇到问题,但UINavigationBar弹出动画仍然是这样的动画

例子

当出现这种情况时,是否有禁用或取消动画的方法?

这是我的代码https://github.com/kanottonp/NavBarBugPOC

ViewController.swift

@IBOutlet weak var button: UIButton!
static var count = 1

private var percentDrivenInteractiveTransition: UIPercentDrivenInteractiveTransition!
private var panGestureRecognizer: UIPanGestureRecognizer!

override func viewDidLoad() {
    super.viewDidLoad()
    addGesture()
    self.navigationController?.setNavigationBarHidden(false, animated: true)
    self.title = "Hello \(ViewController.count)"
    ViewController.count += 1
}

@IBAction func onTouch(_ sender: Any) {
    guard let newVC = storyboard?.instantiateViewController(withIdentifier: "ViewController") else {
        return
    }
    self.navigationController?.pushViewController(newVC, animated: true)
}

private func addGesture() {
    guard panGestureRecognizer == nil else {
        return
    }
    guard self.navigationController?.viewControllers.count > 1 else {
        return
    }
    panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture(_:)))
    panGestureRecognizer.cancelsTouchesInView = true;
    panGestureRecognizer.delaysTouchesBegan = true;
    panGestureRecognizer.maximumNumberOfTouches = 1
    self.view.addGestureRecognizer(panGestureRecognizer)
    self.navigationController?.interactivePopGestureRecognizer?.delegate = panGestureRecognizer as? UIGestureRecognizerDelegate
    
}

@objc private func handlePanGesture(_ panGesture: UIPanGestureRecognizer) {
    let percent = max(panGesture.translation(in: view).x, 0) / view.frame.width
    switch panGesture.state {
        
    case .began:
        navigationController?.delegate = self
        if panGesture.velocity(in: view).x > 0 {
            _ = navigationController?.popViewController(animated: true)
        }
    case .changed:
        if let percentDrivenInteractiveTransition = percentDrivenInteractiveTransition {
            percentDrivenInteractiveTransition.update(percent)
        }
        
    case .ended:
        let velocity = panGesture.velocity(in: view).x
        // Continue if drag more than 50% of screen width or velocity is higher than 300
        if let percentDrivenInteractiveTransition = percentDrivenInteractiveTransition {
            if percent > 0.5 || velocity > 300 {
                percentDrivenInteractiveTransition.finish()
            } else {
                percentDrivenInteractiveTransition.cancel()
            }
        }
        
    case .cancelled, .failed:
        percentDrivenInteractiveTransition.cancel()
        
    default:
        break
    }
}

扩展 ViewController: UINavigationControllerDelegate

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

public func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
    
    navigationController.delegate = nil
    
    if panGestureRecognizer.state == .began && panGestureRecognizer.velocity(in: view).x > 0 {
        percentDrivenInteractiveTransition = UIPercentDrivenInteractiveTransition()
        percentDrivenInteractiveTransition.completionCurve = .easeInOut
    } else {
        percentDrivenInteractiveTransition = nil
    }
    
    return percentDrivenInteractiveTransition
}

SlideAnimatedTransitioning.swift

扩展 SlideAnimatedTransitioning:UIViewControllerAnimatedTransitioning

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    // use animator to implement animateTransition
    
    let animator = interruptibleAnimator(using: transitionContext)
    animator.startAnimation()
}

func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating {
    
    if let propertyAnimator = propertyAnimator {
        return propertyAnimator
    }
    
    let containerView = transitionContext.containerView
    let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
    let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
    
    let fromView = transitionContext.view(forKey: .from)!
    let toView = transitionContext.view(forKey: .to)!
    
    
    toView.frame = transitionContext.finalFrame(for: toViewController)
    toView.frame = CGRect(x: toView.frame.origin.x, y: toView.frame.origin.y, width: toView.frame.size.width, height: toView.frame.size.height + toView.frame.origin.y)

    let width = containerView.frame.width
    
    var offsetLeft = fromView.frame
    offsetLeft.origin.x = width
    
    var offscreenRight = toView.frame
    offscreenRight.origin.x = -width / 3.33;
    
    toView.frame = offscreenRight;
    
    toView.layer.opacity = 0.9
    
    containerView.insertSubview(toView, belowSubview: fromView)

    let animator = UIViewPropertyAnimator(duration: transitionDuration(using: transitionContext), timingParameters: UICubicTimingParameters(animationCurve: .easeInOut))
    
    animator.addAnimations {
        toView.frame = CGRect(x: fromView.frame.origin.x, y: toView.frame.origin.y, width: toView.frame.width, height: toView.frame.height)
        fromView.frame = offsetLeft
        toView.layer.opacity = 1.0

    }
    
    animator.addCompletion { (success) in
        toView.layer.opacity = 1.0
        fromView.layer.opacity = 1.0
        fromViewController.navigationItem.titleView?.layer.opacity = 1

        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        
        self.propertyAnimator = nil

    }
    
    self.propertyAnimator = animator
    return animator
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    if transitionContext?.transitionWasCancelled == true { return 0 }
    return 2
}

标签: iosswiftanimationuinavigationbaruiviewanimationtransition

解决方案


推荐阅读