【问题标题】:Detecting horizontal swipes in a UIScrollView with a vertical scroll使用垂直滚动检测 UIScrollView 中的水平滑动
【发布时间】:2010-12-01 04:01:26
【问题描述】:

我有一个大约 600 像素高和 320 像素宽的 UIScrollView。所以我允许用户垂直滚动。

我也在尝试捕捉视图上的水平滑动。问题似乎是,当用户通过一些意外的垂直移动水平滑动时,UIScrollView 滚动并且我的 touchesEnded 委托方法永远不会被调用。

这是我的代码:

- (void)touchesEnded: (NSSet *)touches withEvent: (UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint currentPosition = [touch locationInView:self];

    if (currentPosition.x + 35 < gestureStartPoint.x)
    {  
        NSLog(@"Right");
    }    
    else if (currentPosition.x - 35 > gestureStartPoint.x)
    {
        NSLog(@"Left");
    }    
    else if (!self.dragging)
    {
        [self.nextResponder touchesEnded: touches withEvent:event]; 
    }

    [super touchesEnded: touches withEvent: event];
}

有谁知道即使涉及垂直拖动,我如何才能让它工作?

【问题讨论】:

    标签: ios uiscrollview uikit


    【解决方案1】:

    UIScrollView 试图根据触摸开始后的立即移动来确定哪些触摸要传递到其内容,哪些是滚动。基本上,如果触摸看起来是一个滚动,它会处理那个手势并且内容永远不会看到它;否则,手势会通过(第一次触摸的延迟非常短)。

    根据我的经验,我已经能够在处理仅垂直滚动的UIScrollView 的内容中捕获水平滑动——它基本上默认情况下对我来说是有效的。我通过将contentSize 设置为与滚动视图的frame 的宽度相同来做到这一点,这足以告诉UIScrollView 它不会处理水平滚动。

    不过,听起来您在使用默认行为时遇到了问题。一个硬件问题是很难模拟笔记本电脑触控板上的手指滑动。如果我是你,我会使用鼠标或最好在设备本身上测试默认的UIScrollView 设置。我发现这些输入法在传达滑动方面效果更好。

    如果这不起作用,这里有一段来自 Apple 的 UIScrollView 文档的非常相关的段落:

    因为滚动视图没有滚动条,所以它必须知道触摸是表示滚动意图还是跟踪内容中的子视图的意图。为了做出这个决定,它通过启动一个定时器来临时拦截一个触摸事件,并在定时器触发之前,查看触摸的手指是否有任何移动。如果时间触发而位置没有显着变化,则滚动视图将跟踪事件发送到内容视图的触摸子视图。如果用户在计时器结束之前将手指拖得足够远,则滚动视图会取消子视图中的任何跟踪并自行执行滚动。子类可以覆盖 touchesShouldBegin:withEvent:inContentView:pagingEnabledtouchesShouldCancelInContentView: 方法(由滚动视图调用)来影响滚动视图处理滚动手势的方式。

    总之,您可以通过继承 UIScrollView 并覆盖建议的方法来尝试他们的建议。

    【讨论】:

    • 泰勒,感谢您的回复。我可以检测到水平“滑动”,但前提是它们的 Y 变化非常小。如果我在 y 轴上大致移动超过 +-5 个像素,则 ScrollView 似乎接管并开始垂直滚动。这不太现实,因为我需要(在设备上)几次尝试才能在 Y 轴上进行这种微小变化的滑动。您是否遇到过类似的行为?
    【解决方案2】:

    如果@Tyler 的方法不起作用,请尝试在 UIScrollView 上放置一个视图,并在该视图的 touchesBegan 中处理任何水平滑动,并将垂直滑动传递给下一个响应者。您可以稍微模糊一点,将水平移动多于垂直的任何内容作为水平滑动处理,并将更纯的垂直滑动传递给 UISCrollView(通过 nextResponder)。

    【讨论】:

    • 新的观点是没有得到任何接触?它是滚动视图的孩子,还是它上面的兄弟?是否设置为接受用户交互?
    【解决方案3】:

    Swift 4 解决方案

    在视图控制器的 viewDidLoad() 中注册两个手势识别器

    let leftGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
    leftGesture.direction = .left
    leftGesture.delegate = self
    view.addGestureRecognizer(leftGesture)
    
    let rightGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(_:)))
    rightGesture.direction = .right
    rightGesture.delegate = self
    view.addGestureRecognizer(rightGesture)
    

    实现滑动操作

    @objc func handleSwipe(_ sender: UISwipeGestureRecognizer) {
        // do swipe left/right based on sender.direction == .left / .right
    }
    

    为了同时实现水平滑动和垂直滚动,让你的视图控制器实现 UIGestureRecognizerDelegate

    class MyViewController: UIViewController, UIGestureRecognizerDelegate
    
    ...
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                           shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
    

    最后一步解决了当您在 y 轴上移动超过 +/- 5 个像素时,滚动视图接管并开始垂直滚动的问题。

    【讨论】: