[英]How to add tap gesture to a dimmed view background?
我已经尝试了一段时间了。 下面的代码是我的 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)
}
}
我不知道你的 presentedViewController.view 的框架/边界是什么,但即使它的上半部分的 alpha 为 0,它也可能覆盖你的 dimView 并接收点击事件而不是 dimView - 因为 presentedViewController.view 被添加为dimView 之上的子视图。
您可能必须等到 controller 出现后,再将手势添加到其父视图的第一个子视图中。 我之前用它来关闭带有后台点击的自定义警报 controller。 你可能会做类似的事情:
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)
}
嗯,试试用这个代替。 让我知道事情的后续。 这个对我有用。
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)
}
}
我刚才看了你的项目。 问题出在您的 animation controller 中。如果您在转换委托 object 中注释掉出售 animation 控制器的函数,则一切正常。
但是只要看看你的 animation controller,你想要实现的是让你的新 vc 向上/向下滑动。 事实上,您甚至不需要自定义 animation controller; 视图 controller 的modalTransitionStyle
属性的默认值为coverVertical
,我认为这正是您想要的。
不过无论如何,您仍然可以使用我之前发布的演示文稿 controller class,因为它与您的 class 具有相同的语义,只是没有不必要的覆盖。
选修的
如果你愿意,也只是一个提示,你现在在你的项目中有这些文件:
PanModalPresentationDelegate.swift
PanModalPresentationController.swift
PanModalPresentationAnimator.swift
TaskViewController.swift
HomeViewController.swift
我通常做的是缩写其中一些长短语,以便文件名和 class 传达其本质的本质,而无需冗长的样板。
所以HomeViewController
和TaskViewController
将是Home_VC
和Task_VC
。 其他3个文件都是给一个VC的presentation; 它很快就会失控。 所以我通常在那里做的是将我的演示文稿 controller 称为PC
并将其声明嵌套在将使用它的 VC class 中(在本例中为Task_VC
)。 直到其他一些 VC 也需要使用它的时候; 那么将它放在自己的文件中并称之为Something_PC
更合适,但我实际上还不需要这样做,哈哈。 对于任何 animation 控制器都是一样的。 Fade_AC
、 Slide_AC
等。我倾向于将转换委托称为TransitionManager
并将其嵌套在所呈现的 VC 的 class 中。这让我更容易将其视为只是一个出售 AC/PC 的东西。
然后你的项目就变成了:
Home_VC.swift
Task_VC.swift
如果你在 Task_VC 中Task_VC
,你会看到一个嵌套的TransitionManager
和PC
。
但是,是的,取决于你。
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 中的手势识别器!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.