【问题标题】:How to add tap gesture to a dimmed view background?如何将点击手势添加到变暗的视图背景?
【发布时间】:2020-05-15 18:20:23
【问题描述】:

我已经尝试了一段时间。下面的代码是我的 UIPresentationController。当一个按钮被按下时,我添加了一个暗淡的 UIView 并且第二个模态(presentedViewController)在中途弹出。

我在方法presentationTransitionWillBegin()中添加了点击手势识别器 我不知道为什么当我点击变暗的 UIView 时没有注册点击手势。

我尝试更改“目标”并将手势添加到不同的位置。还看了其他帖子,但对我没有任何帮助。

谢谢

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)
    }
}

【问题讨论】:

  • 在 frameOfPresentedView 中,您使用 sizeForChild 将呈现的视图设置为容器视图的一半大小,使其最终位于屏幕的下半部分。但是在presentationWillBegin 中,您对呈现的视图施加了约束,以使其与容器的全尺寸相匹配。那里有一个不匹配。也许这会导致问题?你需要这些限制吗?
  • 我删除了这些约束,但变暗的视图仍然没有响应点击手势

标签: ios swift uigesturerecognizer uipopoverpresentationcontroller


【解决方案1】:

我不知道您的presentedViewController.view 的框架/边界是什么,但即使它的上半部分的alpha 为0,它也可能覆盖您的dimView 并接收点击事件而不是dimView - 因为presentedViewController.view被添加为 dimView 之上的子视图。

【讨论】:

    【解决方案2】:

    您可能必须等到控制器出现后才将手势添加到其父视图的第一个子视图。我以前用它来关闭带有背景点击的自定义警报控制器。你可能会做类似的事情:

    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)
    }
    

    【讨论】:

      【解决方案3】:

      嗯,试试用这个吧。让我知道事情的后续。它对我有用。

      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)
      
          }
      }
      

      【讨论】:

      • 谢谢,但它不起作用哈哈。我复制粘贴了你的代码。视图变暗了。然后我点击视图,没有任何反应。视图不会消除暗淡。我正在使用 Swift 4 和 Xcode 9。
      • ? 哈哈。好吧,你甚至把“窃听”打印出来了吗?还要在该 PC 类中添加一个带有一些打印语句的 deinit。也许 PC 实例在演示后立即取消初始化?我正在使用 Swift 5 和 Xcode 11.3
      • 没有“tap”打印出来哈哈!是的,我会尝试添加 deinit。谢谢!
      • 哈哈,不客气。我们会弄清楚的。最终。
      • 嗯..没什么哈哈..没有“窃听”打印出来。也没有打印出 deinit 消息。还有其他想法吗?
      【解决方案4】:

      我刚才看了你的项目。问题出在您的动画控制器中。如果你在你的转场委托对象中注释掉那些出售动画控制器的函数,一切都会正常工作。

      但是看看你的动画控制器,你想要实现的是让你的新 vc 向上/向下滑动。事实上,您甚至不需要为此定制动画控制器;视图控制器的modalTransitionStyle 属性的默认值为coverVertical,这正是您想要的。

      无论如何,您仍然可以使用我之前发布的演示控制器类,因为它与您的类具有相同的语义,只是没有不必要的覆盖。

      可选

      如果您愿意,也只是一个提示,您的项目中现在有这些文件:

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

      我通常做的是缩写其中一些长短语,以便文件和类的名称传达其本质的本质,而无需冗长的样板。

      所以HomeViewControllerTaskViewController 将是Home_VCTask_VC。其他3个文件都是为了一个VC的介绍;它会很快失控。所以我通常在那里做的是调用我的演示控制器 PC 并将其声明嵌套在将使用它的 VC 类中(在本例中为 Task_VC)。直到时间到来,它也需要被其他一些 VC 使用;那么将它放在自己的文件中并称之为Something_PC 更合适,但我实际上从未需要这样做,哈哈。对于任何动画控制器都是如此。 Fade_ACSlide_AC 等。我倾向于将转换委托称为TransitionManager 并将其嵌套在提供的 VC 类中。让我更容易将其视为出售 AC/PC 的东西。

      那么你的项目就变成了:

      Home_VC.swift
      Task_VC.swift
      

      如果您进入Task_VC,您会看到嵌套的TransitionManagerPC

      但是,取决于你?。

      【讨论】:

        【解决方案5】:

        dimmedView 位于呈现视图的后面。你有几个选项可以纠正这个问题。

        首先是允许触摸通过顶视图,它必须覆盖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;
        }
        

        另一种选择是将手势识别器添加到presentedViewController.view,而不是dimmedView。而且,如果您允许 PanModalPresentationController 采用 UIGestureRecognizerDelegate,并将其作为识别器的委托,您可以通过实现 shouldReceive touch 来确定是否应该响应触摸:

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

        如果您使用第二个选项,请不要忘记在dismissalTransitionWillBegin 或dismissalTransitionDidEnd 中移除手势识别器!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多