简体   繁体   English

iOS - 关闭在其视图之外触摸的呈现视图控制器

[英]iOS - Dismiss presented view controller touching outside its View

I have a CustomPresentationController which animates in and out with custom animations;我有一个CustomPresentationController ,它使用自定义动画进出动画;

This specific controller gets presented, more less at 50% of the screen size, and when I present it, I add a shadow-gray view to the presentingViewController so it adds some depth.这个特定的控制器被呈现,在屏幕尺寸的 50% 处更少,当我呈现它时,我向presentingViewController添加了一个阴影灰色视图,因此它增加了一些深度。

I can only dismiss the presentedViewController if I tap the cancel button in the NavBar which I call the default dismiss(:) method.如果我点击NavBarcancel按钮,我只能关闭presentedViewController ,我调用默认的dismiss(:)方法。

What I'm trying to accomplish is to detect a tap outside the presentedViewController , maybe inside the gray zone, so I can dismiss the presentedViewController , somehow like dismissing an ActionSheet but I've failed to do it.我想要完成的是检测presentedViewController之外的点击,可能在灰色区域内,这样我就可以关闭presentedViewController ,就像关闭ActionSheet但我没有做到。 Let me explain what I've tried so far.让我解释一下我到目前为止所做的尝试。

I tried to add a UITapGestureRecognizer to the shadow-gray view but since I'm presenting a different controller, the app-engine might think that since the shadow view isn't on the top hierarchy view it might not be accessible so it 'blocks' the recognizer - whenever I tap it, the gesture handles doesn't fire.我试图将一个UITapGestureRecognizer添加到阴影灰色视图中,但由于我展示了一个不同的控制器,应用引擎可能认为由于阴影视图不在顶层视图中,所以它可能无法访问,因此它“阻塞” ' 识别器 - 每当我点击它时,手势句柄都不会触发。

I'm implementing now in addition a swipe down to dismiss, which I can make it easily, but I really wanted the tap-outside feature to work as well.我现在正在实施另外一个向下滑动以关闭,我可以很容易地做到这一点,但我真的希望tap-outside功能也能正常工作。

Any hint on how can I approach this?关于如何解决这个问题的任何提示?

The apps image is the following:应用程序图像如下:

截图

My solution:我的解决方案:

In presenting view controller (aka ViewControllerA):在呈现视图控制器(又名 ViewControllerA)时:

let storyboard = UIStoryboard(name: "Main", bundle: nil)
                
let vcb = storyboard.instantiateViewController(withIdentifier: "ViewControllerB") as! ViewControllerB // ViewControllerB is the presented view controller 
        
vcb.modalPresentationStyle = .custom
        
vcb.transitioningDelegate = self

modalRatio = Float(0.5) // modalRatio is an object property 
   
self.present(pvc, animated: true)

ViewControllerA shall also implement Transitioning delegate: ViewControllerA 还应实现转换委托:

extension ViewControllerA: UIViewControllerTransitioningDelegate {
        
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
                
        return PartialSizePresentController(presentedViewController: presented, presenting: presenting, withRatio: modalRatio ?? 0.5) // modal ratio is configurable using modalRatio property
        
    }
    
}

Then, implement the presentation controller (aka PartialSizePresentController), so that it also handles tap gesture:然后,实现呈现控制器(又名 PartialSizePresentController),以便它也处理点击手势:

class PartialSizePresentController: UIPresentationController {
    
    let heightRatio : CGFloat
    
    init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, withRatio ratio: Float = 0.5) {
        
        heightRatio = CGFloat(ratio)
        
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
        
    }

    override var frameOfPresentedViewInContainerView: CGRect {
        
        guard let cv = containerView else { fatalError("No container view available") }
        
        return CGRect(x: 0, y: cv.bounds.height * (1 - heightRatio), width: cv.bounds.width, height: cv.bounds.height * heightRatio)
        
    }
    
    override func presentationTransitionWillBegin() {
        
        let bdView = UIView(frame: containerView!.bounds)
        
        bdView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        
        containerView?.addSubview(bdView)
        
        bdView.addSubview(presentedView!)
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PartialSizePresentController.handleTap(_:)))
        
        bdView.addGestureRecognizer(tapGesture)
        
    }
    
    @objc func handleTap(_ sender: UITapGestureRecognizer) {
        presentedViewController.dismiss(animated: true, completion: nil)
    }
}

Try with my below code:尝试使用我的以下代码:

You need to implement this method inside you presented controller which you are working with as a popup.您需要在您作为弹出窗口使用的控制器中实现此方法。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
   // Here write down you logic to dismiss controller     
}

Hope this will work.希望这会奏效。 :D :D

I just had to implement this in one of my app.我只需要在我的一个应用程序中实现它。

I made it worked by adding a button that covers the entire view and this button, once tapped triggers the VC to be dismissed.我通过添加一个覆盖整个视图的按钮来实现它,这个按钮一旦被点击就会触发 VC 被关闭。

Once the button is added you can add your custom View on top.添加按钮后,您可以在顶部添加自定义视图。

So far it looks like it's working pretty well.到目前为止,它看起来运行良好。

My code below (I do everything programmatically, no storyboard)我的代码如下(我以编程方式执行所有操作,没有故事板)

    //—————————————————————————————
    // MARK: View Life Cycle
    //—————————————————————————————

override func viewDidLoad() {
    super.viewDidLoad()
    view.backgroundColor = UIColor.clear //VC view background transparent
    setupUI()
}

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

    //Animate blackView opacity to 1 to give some depth
    UIView.animate(withDuration: 0.4, delay: 0.2, options: .curveEaseInOut, animations: {
        self.blackView.alpha = 1 
    })
}

    //————————————————
    // MARK: Setup UI
    //————————————————

    let blackView: UIView = {
        let view = UIView()
        view.alpha = 0.0
        view.backgroundColor = UIColor.black.withAlphaComponent(0.6)
        return view
    }()
    //Invisible button which covers the entire view that can be tapped 
    lazy var dismissLayerBtn: UIButton = {
        let btn = UIButton()
        btn.addTarget(self, action: #selector(tapToDismiss), for: .touchUpInside)
        return btn
    }()

    @objc func tapToDismiss() {
        print("tapToDimiss")
        self.dismiss(animated: true, completion: nil)
    }

    let milestonePickerView: MilestonePickerView = {
        let view = MilestonePickerView(frame: .zero)
        return view
    }()

    func setupUI() {

        view.addSubview(blackView)
        view.addSubview(dismissLayerBtn)
        view.addSubview(milestonePickerView) //Important to add the customView after the button.

        blackView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)

        dismissLayerBtn.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)

        milestonePickerView.anchor(top: nil, left: view.leftAnchor, bottom: view.bottomAnchor, right: view.rightAnchor, paddingTop: 0, paddingLeft: 20, paddingBottom: 40, paddingRight: 20, width: 0, height: 400)

//I'm using a custom extension to setup constraints (anchors) 
   } 

If you're using storyboard, make sure you put the invisible button under the custom view.如果您使用故事板,请确保将不可见按钮放在自定义视图下。

I hope this helps.我希望这会有所帮助。

// it works for me // 这个对我有用

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    var touch: UITouch? = touches.first
    if touch?.view == yourView {
        navigationController?.popViewController(animated: true)
        dismiss(animated: true, completion: nil)
    }
}

You were on the right track with the UITapGestureRecognizer .您使用UITapGestureRecognizer在正确的轨道上。 Just make sure you implement the shouldRecognizeSimultaneouslyWith as such:只要确保你实现shouldRecognizeSimultaneouslyWith如下:

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

This should allow the gesture to fire correctly.这应该允许手势正确触发。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在ios中从另一个视图中关闭呈现的视图控制器 - Dismiss Presented View Controller from Another View in ios 检测呈现视图之外的触摸以将其关闭 - Detect a touch outside of presented view to dismiss it 解雇由模态视图控制器呈现的UIAlertController - dismiss UIAlertController presented by a modal view controller 旋转后关闭呈现的视图 controller - Dismiss the presented view controller after rotataion 如何在视图外部点击iPad上关闭popover View控制器:IOS 9 - How to dismiss popover View controller on iPad on tapping outside the view : IOS 9 关闭视图控制器 iOS - Dismiss View Controller iOS 关闭从模态呈现的视图控制器翻转的视图控制器 - Dismiss view controller flipped from modally presented view controller 如果存在多个视图控制器,如何关闭模态视图控制器 - How to dismiss modal view controller if there are multiple view controller are presented 将以模态形式呈现的视图控制器转移给其他基础视图控制器 - dismiss a modally presented view controller to a different underlying view controller 编辑 UITextView 并在表视图控制器中触摸键盘外的任何位置时无法关闭键盘 - Can't dismiss keyboard when editing UITextView and touching anywhere outside of keyboard in table view controller
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM