将触摸和手势从UIScrollview转发到视图

8

我有一些关于手势和触摸转发的问题。虽然我已经尝试了很多,但是我无法让它按照我的需求工作。

基本上,我想用两个手指控制双屏上的一个滚动视图,并将其他东西转发给覆盖在其上的 iPad 视图。

为了能够控制双屏上的滚动视图,我对 UIScrollView 进行了子类化,并将其添加为覆盖在 iPad 屏幕上的透明背景视图。

然后,我使用代理将其拼接起来,以便将其拖动等事件转发到双屏上的滚动视图。这个方法完美地运作。

正如我所写的,我希望滚动视图仅响应使用两个手指滚动的事件,因此我将其设置为:

ScrollView.panGestureRecognizer.minimumNumberOfTouches = 2;

但是 scrollview 会吃掉所有的触摸事件,我不知道如何将除了两个手指触摸之外的其他内容传递到后面的视图中。我认为需要覆盖默认行为。

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

这个技巧应该能解决问题,但我无法准确检测屏幕上手指的数量。

4个回答

5
我想分享/解释这个问题的解决方案。也要指出,Hatfinch的输入引导了我正确的方向。非常感谢你!
当试图将视图/滚动视图作为覆盖顶层时,问题在于顶层不知道它的“下一响应者”。 将视图/滚动视图放置在底层视图中将解决此问题。 您可能需要微调底层滚动视图内任何滚动视图的触摸行为,以使行为正确(例如设置最大触摸数量)。
解决方案是子类化UIScrollview,重写以下方法(请参见user1085093的答案)并将其添加为iPad屏幕的底层视图。
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

// If not dragging, send event to next responder
if (!self.dragging){
    [self.nextResponder touchesBegan: touches withEvent:event];
}
else{
    [super touchesBegan: touches withEvent: event];
}}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{

// If not dragging, send event to next responder
if (!self.dragging){
    [self.nextResponder touchesMoved: touches withEvent:event];
}
else{
    [super touchesMoved: touches withEvent: event];
}}


-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

// If not dragging, send event to next responder
if (!self.dragging){
    [self.nextResponder touchesEnded: touches withEvent:event];
}
else{
    [super touchesEnded: touches withEvent: event];
}}

我已经按照如下方式设置了ScrollView:

我这样设置了ScrollView:

 TopLayerScrollView *newScrollView = [[TopLayerScrollView alloc] init];
[newScrollView setBackgroundColor:[UIColor clearColor]];
[newScrollView setFrame:self.tabBarController.view.frame];
[newScrollView setContentSize:dualScreenViewController.scrollContent.contentSize];
newScrollView.showsHorizontalScrollIndicator = NO;
newScrollView.showsVerticalScrollIndicator = NO;
newScrollView.delegate = self;
newScrollView.bounces = NO;
[newScrollView scrollsToTop];
newScrollView.panGestureRecognizer.minimumNumberOfTouches = 2;

self.topLayerScrollView = newScrollView;
[newScrollView release];

[self.tabBarController.view removeFromSuperview];
[topLayerScrollView addSubview:self.tabBarController.view];
[window addSubview:topLayerScrollView];
[topLayerScrollView bringSubviewToFront:self.tabBarController.view];

底层滚动视图的代理方法:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (scrollView.dragging || scrollView.tracking)
{
    [dualScreenViewControlleremphasized text.scrollContent setContentOffset:CGPointMake(scrollView.contentOffset.x, scrollView.contentOffset.y) animated:NO];
    self.tabBarController.view.frame = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, self.tabBarController.view.frame.size.width, self.tabBarController.view.frame.size.height);
}}

这个解决方案非常好。 另一个解决方案是将scrollview作为一个覆盖的视图,如我最初所想。问题在于让顶层视图知道位于其下面的视图(nextResponder)。为了实现这一点,您必须子类化UIScrollview并创建一个UIResponder属性,您必须在界面构建器文件或运行时期间连接。这样,覆盖的scrollview就会知道下一个响应者是谁。 请参见morningstar的答案


请参见 https://developer.apple.com/videos/play/wwdc2012/223/,其中Josh Shaffer“强烈反对”我们手动调用touches...方法来处理下一个响应者或其他对象。该视频中还提供了解决方案。 - Oleksii Nezhyborets

2

很遗憾,在这里你不能使用 -pointInside:withEvent: ,因为系统会在确定触摸点所在的视图(或触摸点所在的视图)之前,决定它们是否满足手势识别器的要求。

我认为你最好的选择是将当前位于滚动视图后面的视图实际上成为滚动视图的内容视图,并在每次滚动视图的内容偏移改变时移动该视图,以使其在屏幕上保持相同的位置。(这听起来很昂贵,但实际上并不是。)因此,你的滚动视图不再是一个覆盖层,而更像是一个底层。

你还可以尝试调整 UIScrollView.delaysContentTouches-[UIScrollView touchesShouldBegin:withEvent:inContentView:] 等来优化行为。


非常感谢您。我在最后添加了一个解释,说明了我是如何解决这个问题的。 - Tom

1

更新 Xcode 6.4,Swift 1.2:

我还对 UIScrollView 进行了子类化:

import UIKit

class WBMScrollView: UIScrollView
{
    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
    {
        self.nextResponder()?.touchesBegan(touches, withEvent: event)
        super.touchesBegan(touches, withEvent: event)
    }
}

在应用的主视图控制器ViewController.swift中,我进行了测试:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        println(" touches began")
    }

它运行成功了。ScrollView已经通过IB添加,并连接到ViewController.swift


1
使用我的应用程序时,我需要将单指触摸事件转发到滚动视图的父视图控制器,同时仍然允许滚动视图缩放。使用下一个响应者并没有起作用,因为我还添加了另一个重叠的视图,它最终成为下一个响应者并窃取了触摸事件。最终,我基于Tom的答案做了一些事情,只需传递父视图控制器并直接将触摸事件发送到它。这是Swift中的代码:
class CustomScrollView: UIScrollView {

    var parentViewController: UIViewController?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        parentViewController?.touchesBegan(touches, with: event)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        parentViewController?.touchesMoved(touches, with: event)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        parentViewController?.touchesEnded(touches, with: event)
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接