ios - 取消 UIPercentDrivenInteractiveTransition 时如何禁用 UINavigationBar 动画
问题描述
我已经实现了一个自定义的交互式过渡,用于检测中间屏幕的滑动UIPanGestureRecognizer
以及.UIPercentDrivenInteractiveTransition
UIViewControllerAnimatedTransitioning
现在,我在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
}