简体   繁体   中英

Dismiss UIPopoverPresentationController with any gesture, not just tap

So I have a simple UIPopoverPresentationController that displays some content.

User can dismiss it by tapping anywhere on the screen (default popover behaviour).

I want the popover to be dismissed if the user does any kind of tap or gesture on the screen. Preferably drag gesture.

Any idea if this is possible? And how?

try using touchesBegan:withEvent method

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if let touch = touches.first {
            if touch.view == self.view {
                self.dismiss()
            } else {
                return
            }

        }
    }

VC is the view presented in the popover. in the presentViewController:animated:completion: block

[self presentViewController:vc animated:YES completion:^{
UIView *v1 = vc.view.superview.superview.superview;

            for (UIView* vx in v1.subviews) {
                Class dimmingViewClass =  NSClassFromString(@"UIDimmingView");
                if ([vx isKindOfClass:[dimmingViewClass class]])
                {
                    UIPanGestureRecognizer* pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(closePopoverOnSwipe)];
                    [vx addGestureRecognizer:pan];
                }
            }
}];

you have a UIDimmingView that holds the tap gesture that will close. just add to it. I am using the Class dimmingViewClass = NSClassFromString(@"UIDimmingView"); to avoid making direct use of undocumented APIs. I have not tried yet to send this hack to apple, but will try next week. I hope it will pass. But I tested this and it did call my selector.

I resolved this problem using custom view:

typealias Handler = (() -> Void)?


final class InteractionView: UIView {

    var dismissHandler: Handler = nil

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        return self
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.dismissHandler?()
    }
}

In the viewDidAppear configure this view and add to popover containerView:

fileprivate func configureInteractionView() {
    let interactionView = InteractionView(frame: self.view.bounds)
    self.popoverPresentationController?.containerView?.addSubview(interactionView)
    interactionView.backgroundColor = .clear
    interactionView.isUserInteractionEnabled = true
    interactionView.dismissHandler = { [weak self] in
        self?.hide()
    }
}

fileprivate func hide() {
    self.dismiss(animated: true, completion: nil)
}

my solution for this problem. for example if you create a class UIViewController named MyPopoverViewController to present PopViewController. then in the viewDidLoad() or viewWillAppear(_ animated:) Method add two GestureRecognizer as follows:

protocal MyPopoverControllerDelegate {
    func shouldDismissPopover()
}

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

    // back trace to root view, if it is a UIWindows, add PanGestureRecognizer
    // and LongPressGestureRecognizer, to dismiss this PopoverViewController
    for c in sequence(first: self.view, next: { $0.superview}) {
        if let w = c as? UIWindow {

            let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(dismissPopover(gesture:)))
            w.addGestureRecognizer(panGestureRecognizer)

            let longTapGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(dismissPopover(gesture:)))
            w.addGestureRecognizer(longTapGestureRecognizer)
        }
    }

    @objc private func dismissPopover(gesture: UIGestureRecognizer) {
        delegate?.shouldDismissPopover()
    }
}

then in your main ViewController, which this PopOverViewController presents, implements the Method of the Protocol.

extension YourMainViewController: MyPopoverControllerDelegate {
    func shouldDismissPopover() {
        self.presentedViewController?.dismiss(animated: true, completion: nil)
    }
}

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