允许UIScrollView及其子视图响应触摸

我希望我的UIScrollView及其子视图都可以接收子视图中的所有触摸事件。 每个人都可以以自己的方式回应。

或者,如果点击手势被转发到子视图,一切都会好起来的。

很多人都在这个普遍的领域挣扎。 以下是一些相关的问题:

UIScrollView如何从其子视图中窃取
如何从UIScrollView窃取触摸?
如何在UIScrollView中取消滚动

顺便说一句,如果我重写hitTest:withEvent:在滚动视图中,只要userInteractionEnabled为YES,就会看到触摸。 但这并不能真正解决我的问题,因为:

1)那时候,我不知道这是不是一个水龙头。
2)有时我需要将userInteractionEnabled设置为NO。

编辑:澄清,是的,我想对平底锅不同的水龙头。 点击应该通过子视图来处理。 平底锅可以通过滚动视图以通常的方式处理。

首先是免责声明。 如果在UIScrollView中将userInteractionEnabled设置为NO ,则不会将触摸事件传递给子视图。 据我所知,除了一个例外,没有办法解决这个问题:在UIScrollView的superview上截取touch事件,特别是将这些事件传递给UIScrollView的子视图。 说实话,我不知道你为什么要这样做。 如果你想禁用特定的UIScrollViewfunction(比如…好,滚动),你可以做到这一点很容易,而不禁用UserInteraction。

如果我理解你的问题,你需要点击事件由UIScrollView处理, 传递给子视图? 在任何情况下(无论是什么手势),我认为你正在寻找的是协议方法gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:在协议UIGestureRecognizerDelegate 。 在你的子视图中,无论你使用什么手势识别器,都可以在手势识别器上设置一个委托(可能是任何一个设置UIGestureReconginzer类)。 重写上述方法并返回YES 。 现在,这个手势将会与任何可能“偷走”手势的识别器(在你的情况下,一个水龙头)一起被识别。 使用这种方法,你甚至可以微调你的代码,只发送特定types的手势到子视图或只在某些情况下发送手势。 它给你很多控制。 只要确保阅读有关的方法,尤其是这部分:

当由gestureRecognizer或其他GestureRecognizer识别手势将阻止其他手势识别器识别其手势时调用此方法。 请注意,返回YES保证允许同时识别; 另一方面,返回“否”不能保证防止同时识别,因为另一个手势识别器的委托人可能返回YES。

当然,有一个警告:这只适用于手势识别器。 所以你可能仍然有问题,如果你想使用touchesBegan:touchesEnded等来处理触摸。 当然,您可以使用hitTest:将原始触摸事件发送到子视图,但是为什么? 为什么在UIView使用这些方法来处理事件,当你可以附加一个UIGestureRecognizer到一个视图,并获得所有的function是免费的? 如果你需要触摸处理的方式,没有标准的UIGestureRecognizer可以提供, UIGestureRecognizer 子类,并在那里处理触摸。 这样,您可以获得UIGestureRecognizer所有function以及您自己的自定义触摸处理。 我真的认为苹果打算用UIGestureRecognizerreplace开发人员在UIView使用的大多数(如果不是全部的话)触摸处理代码。 它允许代码重用,并且在缓解什么代码处理什么触摸事件时处理起来要容易得多。

我不知道这是否可以帮到你,但是我也遇到类似的问题,我希望scrollview能够处理双击,但是只需要点击一下即可。 这是CustomScrollView使用的代码

 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch* touch = [touches anyObject]; // Coordinates CGPoint point = [touch locationInView:[self.subviews objectAtIndex:0]]; // One tap, forward if(touch.tapCount == 1){ // for each subview for(UIView* overlayView in self.subviews){ // Forward to my subclasss only if([overlayView isKindOfClass:[OverlayView class]]){ // translate coordinate CGPoint newPoint = [touch locationInView:overlayView]; //NSLog(@"%@",NSStringFromCGPoint(newPoint)); BOOL isInside = [overlayView pointInside:newPoint withEvent:event]; //if subview is hit if(isInside){ Forwarding [overlayView touchesEnded:touches withEvent:event]; break; } } } } // double tap : handle zoom else if(touch.tapCount == 2){ if(self.zoomScale == self.maximumZoomScale){ [self setZoomScale:[self minimumZoomScale] animated:YES]; } else { CGRect zoomRect = [self zoomRectForScrollView:self withScale:self.maximumZoomScale withCenter:point]; [self zoomToRect:zoomRect animated:YES]; } [self setNeedsDisplay]; } } 

当然,有效的代码应该被改变,但是在这一点上,你应该有所有的信息来决定你是否必须转发事件。 你可能需要在touchesMoved:withEvent:方法中实现这个。

希望这可以帮助。

我有这个相同的问题,但与UIPageViewController内的UIPageViewController ,所以它不得不稍微处理。

通过更改UIScrollView上的每个识别器的cancelsTouchesInView属性为false我能够接触到UIPageViewController内的button的UIPageViewController

我这样做是通过添加这个代码到viewDidLoad

 guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else { print("No gesture recognizers on scrollview.") return } for recognizer in recognizers { recognizer.cancelsTouchesInView = false } 

如果您需要的是触摸和滚动之间的差异,那么您可以testing触摸是否已移动。 如果这是一个水龙头然后touchHasBeenMoved不会被调用,那么你可以认为这是一个触摸。

在这一点上,你可以设置一个布尔值来指示一个movnent是否正在计算,并将这个布尔值设置为其他方法中的一个条件。

我在路上,但如果这是你所需要的,我可以稍后再解释。

一个黑客的方式来实现你的目标 – 不是100%确切的 – 是inheritanceUIWindow并覆盖 – (void)sendEvent:(UIEvent *)事件;

一个简单的例子:

在SecondResponderWindow.h头

 //SecondResponderWindow.h @protocol SecondResponderWindowDelegate - (void)userTouchBegan:(id)tapPoint onView:(UIView*)aView; - (void)userTouchMoved:(id)tapPoint onView:(UIView*)aView; - (void)userTouchEnded:(id)tapPoint onView:(UIView*)aView; @end @interface SecondResponderWindow : UIWindow @property (nonatomic, retain) UIView *viewToObserve; @property (nonatomic, assign) id <SecondResponderWindowDelegate> controllerThatObserves; @end 

在SecondResponderWindow.m中

 //SecondResponderWindow.m - (void)forwardTouchBegan:(id)touch onView:(UIView*)aView { [controllerThatObserves userTouchBegan:touch onView:aView]; } - (void)forwardTouchMoved:(id)touch onView:(UIView*)aView { [controllerThatObserves userTouchMoved:touch onView:aView]; } - (void)forwardTouchEnded:(id)touch onView:(UIView*)aView { [controllerThatObserves userTouchEnded:touch onView:aView]; } - (void)sendEvent:(UIEvent *)event { [super sendEvent:event]; if (viewToObserve == nil || controllerThatObserves == nil) return; NSSet *touches = [event allTouches]; UITouch *touch = [touches anyObject]; if ([touch.view isDescendantOfView:viewToObserve] == NO) return; CGPoint tapPoint = [touch locationInView:viewToObserve]; NSValue *pointValue = [NSValue valueWithCGPoint:tapPoint]; if (touch.phase == UITouchPhaseBegan) [self forwardTouchBegan:pointValue onView:touch.view]; else if (touch.phase == UITouchPhaseMoved) [self forwardTouchMoved:pointValue onView:touch.view]; else if (touch.phase == UITouchPhaseEnded) [self forwardTouchEnded:pointValue onView:touch.view]; else if (touch.phase == UITouchPhaseCancelled) [self forwardTouchEnded:pointValue onView:touch.view]; } 

这不是100%符合你的期望 – 因为你的第二个响应者视图不能通过-touchDidBegin处理触摸事件本身,所以必须实现SecondResponderWindowDelegate。 然而,这个破解允许你处理额外响应者的触摸事件。

该方法受MITHIN KUMAR TapDetectingWindow的启发和扩展

Interesting Posts