简体   繁体   English

如何将点击手势添加到变暗的视图背景?

[英]How to add tap gesture to a dimmed view background?

I've been trying this for awhile.我已经尝试了一段时间了。 The code below is my UIPresentationController.下面的代码是我的 UIPresentationController。 When a button is pressed, I add a dimmed UIView and a second modal (presentedViewController) pops up halfway.当按下一个按钮时,我添加了一个变暗的 UIView 并且第二个模态 (presentedViewController) 中途弹出。

I added the tap gesture recognizer in the method presentationTransitionWillBegin() I don't know why the tap gesture is not being registered when I click on the dimmed UIView.我在方法 presentationTransitionWillBegin() 中添加了点击手势识别器我不知道为什么当我点击变暗的 UIView 时点击手势没有被注册。

I've tried changing the "target" and adding the gesture in a different place.我试过改变“目标”并在不同的地方添加手势。 Also looked at other posts, but nothing has worked for me.还查看了其他帖子,但对我没有任何帮助。

Thanks谢谢

import UIKit

class PanModalPresentationController: UIPresentationController {

    override var frameOfPresentedViewInContainerView: CGRect {
        var frame: CGRect = .zero
        frame.size = size(forChildContentContainer: presentedViewController, withParentContainerSize: containerView!.bounds.size)
        frame.origin.y = containerView!.frame.height * (1.0 / 2.0)
        print("frameOfPresentedViewInContainerView")
        return frame
    }

    private lazy var dimView: UIView! = {
        print("dimView")
        guard let container = containerView else { return nil }

        let dimmedView = UIView(frame: container.bounds)
        dimmedView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        dimmedView.isUserInteractionEnabled = true

        return dimmedView
    }()

    override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
        print("init presentation controller")
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
    }

    override func presentationTransitionWillBegin() {

        guard let container = containerView else { return }
        print("presentation transition will begin")

        container.addSubview(dimView)
        dimView.translatesAutoresizingMaskIntoConstraints = false
        dimView.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
        dimView.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
        dimView.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive = true
        dimView.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
        dimView.isUserInteractionEnabled = true

        let recognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
        dimView.addGestureRecognizer(recognizer)

        container.addSubview(presentedViewController.view)
        presentedViewController.view.translatesAutoresizingMaskIntoConstraints = false
        presentedViewController.view.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
        presentedViewController.view.widthAnchor.constraint(equalTo: container.widthAnchor).isActive = true
        presentedViewController.view.heightAnchor.constraint(equalTo: container.heightAnchor).isActive = true

        guard let coordinator = presentingViewController.transitionCoordinator else { return }
        coordinator.animate(alongsideTransition: { _ in
            self.dimView.alpha = 1.0
        })

        print(dimView.alpha)
    }

    override func dismissalTransitionWillBegin() {
        guard let coordinator = presentedViewController.transitionCoordinator else {
            print("dismissal coordinator")
            self.dimView.alpha = 0.0
            return
        }
        print("dismissal transition begin")
        coordinator.animate(alongsideTransition: { _ in
            self.dimView.alpha = 0.0
        })
    }

    override func containerViewDidLayoutSubviews() {
        print("containerViewDidLayoutSubviews")
        presentedView?.frame = frameOfPresentedViewInContainerView
//        presentedViewController.dismiss(animated: true, completion: nil)
    }

    override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize {
        print("size")
        return CGSize(width: parentSize.width, height: parentSize.height * (1.0 / 2.0))
    }

    @objc func handleTap(_ sender: UITapGestureRecognizer) {
        print("tapped")
        //        presentingViewController.dismiss(animated: true, completion: nil)
        presentedViewController.dismiss(animated: true, completion: nil)
    }
}

I can't tell what the frame/bounds of your presentedViewController.view is but even if it's top half has an alpha of 0 it could be covering your dimView and receiving the tap events instead of the dimView - since presentedViewController.view is added as a subview on top of dimView.我不知道你的 presentedViewController.view 的框架/边界是什么,但即使它的上半部分的 alpha 为 0,它也可能覆盖你的 dimView 并接收点击事件而不是 dimView - 因为 presentedViewController.view 被添加为dimView 之上的子视图。

You may have to wait until after the controller is presented and add the gesture to its superview's first subview.您可能必须等到 controller 出现后,再将手势添加到其父视图的第一个子视图中。 I've used this before to dismiss a custom alert controller with a background tap.我之前用它来关闭带有后台点击的自定义警报 controller。 You could probably do something similar:你可能会做类似的事情:

viewController.present(alertController, animated: true) {
     // Enabling Interaction for Transparent Full Screen Overlay
     alertController.view.superview?.subviews.first?.isUserInteractionEnabled = true
     let tapGesture = UITapGestureRecognizer(target: alertController, action: #selector(alertController.dismissSelf))
     alertController.view.superview?.subviews.first?.addGestureRecognizer(tapGesture)
}

Hmm, try using this instead.嗯,试试用这个代替。 Let me know how it goes.让我知道事情的后续。 It works for me.这个对我有用。

class PC: UIPresentationController {

    /*
     We'll have a dimming view behind.
     We want to be able to tap anywhere on the dimming view to do a dismissal.
     */

    override var frameOfPresentedViewInContainerView: CGRect {

        let f = super.frameOfPresentedViewInContainerView
        var new = f
        new.size.height /= 2
        new.origin.y = f.midY
        return new
    }

    override func presentationTransitionWillBegin() {

        let con = self.containerView!
        let v = UIView(frame: con.bounds)
        v.backgroundColor = UIColor.black
        v.alpha = 0
        con.insertSubview(v, at: 0)

        let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        v.addGestureRecognizer(tap)

        let tc = self.presentedViewController.transitionCoordinator!
        tc.animate(alongsideTransition: { _ in
            v.alpha = 1
        }, completion: nil)
    }

    @objc func handleTap() {
        print("tapped")
        self.presentedViewController.dismiss(animated: true, completion: nil)
    }

    override func dismissalTransitionWillBegin() {

        let con = self.containerView!
        let v = con.subviews[0]

        let tc = self.presentedViewController.transitionCoordinator!
        tc.animate(alongsideTransition: { _ in
            v.alpha = 0
        }, completion: nil)

    }
}

I took a look at your project just now.我刚才看了你的项目。 The problem is in your animation controller. If you comment out the functions in your transition delegate object that vend animation controllers, everything works fine.问题出在您的 animation controller 中。如果您在转换委托 object 中注释掉出售 animation 控制器的函数,则一切正常。

But just looking at your animation controller, what you wanted to achieve was to have your new vc slide up / slide down.但是只要看看你的 animation controller,你想要实现的是让你的新 vc 向上/向下滑动。 And in fact, you don't even need a custom animation controller for this;事实上,您甚至不需要自定义 animation controller; the modalTransitionStyle property of a view controller has a default value of coverVertical , which is just what you want I think.视图 controller 的modalTransitionStyle属性的默认值为coverVertical ,我认为这正是您想要的。

In any case though, you can still use the presentation controller class I posted before, as it has same semantics from your class, just without unnecessary overrides.不过无论如何,您仍然可以使用我之前发布的演示文稿 controller class,因为它与您的 class 具有相同的语义,只是没有不必要的覆盖。

Optional选修的

Also just a tip if you'd like, you have these files right now in your project:如果你愿意,也只是一个提示,你现在在你的项目中有这些文件:

PanModalPresentationDelegate.swift
PanModalPresentationController.swift
PanModalPresentationAnimator.swift
TaskViewController.swift
HomeViewController.swift

What I normally do is abbreviate some of those long phrases, so that the name of the file and class conveys the essence of its nature without long un-needed boilerplate.我通常做的是缩写其中一些长短语,以便文件名和 class 传达其本质的本质,而无需冗长的样板。

So HomeViewController and TaskViewController would be Home_VC and Task_VC .所以HomeViewControllerTaskViewController将是Home_VCTask_VC Those other 3 files are all for the presentation of one VC;其他3个文件都是给一个VC的presentation; it can get out of hand very quickly.它很快就会失控。 So what I normally do there is call my presentation controller just PC and nest its declaration inside the VC class that will use it (in this case that's Task_VC ).所以我通常在那里做的是将我的演示文稿 controller 称为PC并将其声明嵌套在将使用它的 VC class 中(在本例中为Task_VC )。 Until the time comes where it needs to be used by some other VC too;直到其他一些 VC 也需要使用它的时候; then it's more appropriate to put it in its own file and call it Something_PC but I've never actually needed to do that yet lol.那么将它放在自己的文件中并称之为Something_PC更合适,但我实际上还不需要这样做,哈哈。 And the same for any animation controllers ex.对于任何 animation 控制器都是一样的。 Fade_AC , Slide_AC etc. I tend to call transition delegate a TransitionManager and nest it in the presented VC's class. Makes it easier for me to think of it as just a thing that vends AC's / a PC. Fade_ACSlide_AC等。我倾向于将转换委托称为TransitionManager并将其嵌套在所呈现的 VC 的 class 中。这让我更容易将其视为只是一个出售 AC/PC 的东西。

Then your project simply becomes:然后你的项目就变成了:

Home_VC.swift
Task_VC.swift

And if you go inside Task_VC , you'll see a nested TransitionManager and PC .如果你在 Task_VC 中Task_VC ,你会看到一个嵌套的TransitionManagerPC

But yeah up to you.但是,是的,取决于你。

The dimmedView is behind presented view. dimmedView 在呈现的视图后面。 You have a couple options to correct that.您有几个选项可以更正该问题。

First, is allow touches to pass through the top view, it must override pointInside:首先,允许触摸通过顶视图,它必须覆盖 pointInside:

- (BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    for (UIView *subview in self.subviews) {
        if ([subview hitTest:[self convertPoint:point toView:subview] withEvent:event]) {
            return TRUE;
        }
    }

    return FALSE;
}

Another options is to instead add the gesture recognizer to the presentedViewController.view, instead of the dimmedView.另一种选择是将手势识别器添加到 presentedViewController.view,而不是 dimmedView。 And, if you allow PanModalPresentationController to adopt the UIGestureRecognizerDelegate, and it as the delegate to the recognizer, you can determine if you should respond to touches, by implementing shouldReceive touch:而且,如果您允许 PanModalPresentationController 采用 UIGestureRecognizerDelegate,并将其作为识别器的委托,您可以通过实施 shouldReceive touch 来确定是否应该响应触摸:

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if (touch.view == presentedViewController.view) {
            return true
        }
    
        return false
    }

If you use the second option, don't forget to remove the gesture recognizer in dismissalTransitionWillBegin or dismissalTransitionDidEnd!如果您使用第二个选项,请不要忘记删除 dismissalTransitionWillBegin 或 dismissalTransitionDidEnd 中的手势识别器!

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

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