简体   繁体   中英

Interactive sliding menu goes to completion as soon as pan gesture starts [Swift]

I am trying to implement a sliding menu that can be interactively dismissed by horizontal panning, same as the ones in Uber and Google apps. Everything works as expected except that, as soon as I start panning horizontally, dismiss goes to completion without following my finger. Any suggestion of where the problem may lie is very appreciated.

I subclassed UIPresentationController to define the presented width of my menu controller. I have custom presentation animator and dismiss animator, and a UIViewControllerTransitioningDelegate object to return them all to UIKit . I also implemented gestureRecognizerShouldBegin(_ gestureRecognizer:) method in my menu controller to allow vertical scrolling.

在此处输入图片说明

SlideDismissAnimator

class SlideDismissAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    let interactionController: SlideInteractionController?

    init(interactionController: SlideInteractionController?) {
        self.interactionController = interactionController
        super.init()
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.2
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        let fromCV = transitionContext.viewController(forKey: .from)!

        let initialFrame = transitionContext.finalFrame(for: fromCV)
        var finalFrame = initialFrame
        finalFrame.origin.x = transitionContext.containerView.frame.width // My menu slides in from right

        let duration = transitionDuration(using: transitionContext)

        UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: {
            fromCV.view.frame = finalFrame
        }) { _ in
   transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}

SlideInteractionController

class SlideInteractionController: UIPercentDrivenInteractiveTransition {

    var interactionInProgress = false

    private var shouldCompleteTransition = false
    private weak var collectionViewController: UICollectionViewController!

    init(collectionViewController: UICollectionViewController) {
        super.init()
        self.collectionViewController = collectionViewController
        if let menuController = collectionViewController as? MenuController {
            let gesture = UIPanGestureRecognizer(target: self, action: #selector(handleGesture))
            menuController.collectionView?.addGestureRecognizer(gesture)
            gesture.delegate = menuController
        }
    }

    @objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
        let translation = gestureRecognizer.translation(in: gestureRecognizer.view!.superview!)
        var progress = (translation.x / 100)
        progress = CGFloat(fminf(fmaxf(Float(progress), 0.0), 1.0))

        switch gestureRecognizer.state {
        case .began:
            interactionInProgress = true
            collectionViewController.dismiss(animated: true, completion: nil)

        case .changed:
            shouldCompleteTransition = progress > 0.5
            update(progress)

        case .cancelled:
            interactionInProgress = false
            cancel()

        case .ended:
            interactionInProgress = false
            if shouldCompleteTransition {
                finish()
            } else {
                cancel()
            }
        default:
            break
        }
    }
}

MenuController

class MenuController: UICollectionViewController, UIGestureRecognizerDelegate {

    var slideInteractionController: SlideInteractionController?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
        slideInteractionController = SlideInteractionController(collectionViewController: self)
    }

    ...

    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
            let translation = panGestureRecognizer.translation(in: collectionView)
            if translation.x > fabs(translation.y) {
                return true
            }
        }
        return false
    }
}

I made a sample Project that Use a tableView inside a View that acts as A side Menu drawer Presented over Current Context

my pan Handler

//MARK: Pan gesture Handler
    @objc func handlePanGesture(panGesture: UIPanGestureRecognizer)
    {
        ///Get the changes
        let translation = panGesture.translation(in: self.view)

        ///Make View move to left side of Frame
        if CGFloat(round(Double((panGesture.view?.frame.origin.x)!))) <= 0
        {
            panGesture.view!.center = CGPoint(x: panGesture.view!.center.x + translation.x, y: panGesture.view!.center.y)
            panGesture.setTranslation(CGPoint.zero, in: self.view)
        }

        ///Do not let View go beyond origin as 0
        if CGFloat(round(Double((panGesture.view?.frame.origin.x)!))) > 0
        {
            panGesture.view?.frame.origin.x = 0
            panGesture.setTranslation(CGPoint.zero, in: self.view)
        }

        ///States When Dragging
        switch panGesture.state
        {
        case .changed:
            self.setAlphaOfBlurView(origin: (panGesture.view?.frame.maxX)!)

        case .ended:
            if CGFloat(round(Double((panGesture.view?.frame.maxX)!))) >= self.view.frame.size.width*0.35
            {
                UIView.animate(withDuration: 0.7, animations: {
                    panGesture.view?.frame.origin.x = 0
                    panGesture.setTranslation(CGPoint.zero, in: self.view)
                })
            }
            else
            {
                UIView.animate(withDuration: 0.4, animations: {
                    panGesture.view?.frame.origin.x -= self.maximum_x
                    panGesture.setTranslation(CGPoint.zero, in: self.view)
                }, completion: { (success) in
                    if (success)
                    {
                        self.remove(asChildViewController: self.sideMenuVCObject, baseView: self.baseView)
                        self.baseView.removeFromSuperview()
                        self.blurView.removeFromSuperview()

                        //Remove Notification observer
                        NotificationCenter.default.removeObserver(self,name: NSNotification.Name(rawValue: "hideMenu"),object: nil)
                    }
                })
            }
            break
        default:
            print("Default Case")
        }
    }

Repository Link at GiHub

https://github.com/RockinGarg/Slide-Menu-Drawer.git

Working Video :

https://drive.google.com/open?id=13Q-bBkVlAX7uEweDyQGvNct-dXkBSveT

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM