如何取消UIScrollView中的滚动

17

我使用UIScrollView在iPhone上制作大尺寸(大于320px)的UI。

我创建了一个UIScrollView实例,并在其上添加了一些子视图。 问题是,我希望只有当用户触摸子视图外部时才启用滚动, 并在用户触摸其中一个子视图时停止滚动。

我阅读了文档并尝试查找示例,但没有找到好的线索。 如果您有任何想法,请帮助我。


@Chris Hanson:为什么你把iPhone和UIScrollView的标签删除了?那些是正确的标签 - cocoa-touch可以添加,但不应该单独出现在这里。 - Emil
我没有移除或编辑那些标签 :( - fish potato
因为在我进行那次编辑的时候,用类名和冗余信息来污染标签空间并不是一个好主意。现在依然如此,只是我放弃了修复这个烂摊子。 - Chris Hanson
6个回答

36

UIScrollView有一个scrollEnabled属性,允许你通过编程来禁用滚动。它还有一个代理(UIScrollViewDelegate),允许你查看诸如滚动开始/结束等事件。似乎你可以通过这些选项以某种方式组合起来实现一些功能。


谢谢您的回复。我现在会尝试您的解决方案。 - fish potato
2
不错的回答,有点模糊但足以开始。+1 - Dan Rosenstark

8

你真正感兴趣的属性是canCancelContentTouches,我现在正在测试这个问题,因为我和你一样也有同样的问题。

如果这个属性的值为NO,无论手指如何移动,一旦内容视图开始跟踪,滚动视图都不会滚动。

如果这不能满足你的需求,可以子类化UIScrollView并重写touchesShouldBegin:withEvent:inContentView方法,这是被接受答案所建议的。


5
最简单的方法是将delaysContentTouches设置为NO,这样滚动视图的默认行为就是,任何被识别为UIControl的东西会立即将触摸事件传递给控件,而其他一切则会进行滚动。

2
如果将其设置为NO,则行为与OP想要的相反。如果将其设置为YES,情况也不会好多少 :) - Dan Rosenstark

5

如果您想检测UIScrollView中任何子视图中的触摸事件,您需要继承UIScrollView并重写touchesShouldBegintouchesShouldCancelInContentView方法,这些方法是专为此目的创建的。

除此之外,没有其他方法可以识别UIScrollView中子视图中的触摸。因为UIScrollView倾向于自己处理所有的触摸事件,并不会将它们传递给其子视图。

祝一切顺利。


谢谢你的回答。 我尝试重写touchesShouldBegin / touchesShouldCancel...方法,但这些方法从未被调用过。 也许我的代码有问题,但我可以通过重写touchesBegin/touchesMoved/touchedEnded方法来停止滚动。 谢谢! - fish potato
你是在UIScrollView的子类中做这个吗?方法只有在那时才会被调用。所以,你的UIViewController应该使用UIScrollView的子类作为视图,而不是使用UIView作为其视图。 - lostInTransit
是的,我在UIScrollView的子类中尝试过这个。我的实现有些问题(只覆盖touchesBegan...),所以我一直在努力找到为什么touchesShouldBegin方法从未被调用的原因。 - fish potato
我也曾经遇到过同样的问题。对我来说,问题出在底层子视图(UIImageView)没有设置userInteractionEnabled为YES。 - Ken Joyner
这个答案是错误的,不应该被接受。你根本不需要子类化UIScrollView。UIScrollView中存在一个bug,会破坏UIView - 如果你修复了UIView,所有的触摸事件都能完美地工作(它们会穿过去到UIViewController),而且滚动视图也会按预期工作。秘密在于,在UIView子类中正确设置nextResponder,以便当Apple意外删除它时能正常工作。 - Adam

2
您还可以继承UIScrollViewController并重写touchesBegan,touchesMoved和touchesEnded方法。如果您的实现从未调用过超类实现,则它将不会滚动。

谢谢你的回答。我尝试了你的建议,它起作用了! - fish potato

1

我的问题是有一个用户可以在其中绘制的视图,但滚动视图却会劫持触摸并滚动而不是绘制。这是我的解决方案:

在我的绘图视图中,我有:

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

    // Disable scroll on touch begin
    setContainerScrollingEnabled(false)
    super.touchesBegan(touches, with: event)
}

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

    // Reenable scroll on touch end
    setContainerScrollingEnabled(true)
    super.touchesEnded(touches, with: event)
}

private func setContainerScrollingEnabled(_ enabled: Bool) {

    // loop up superviews to find the scrollview
    func scrollingSuperview(of view: UIView) -> UIScrollView? {
        guard let superview = view.superview else { return nil }
        if let scrolling = view.superview as? UIScrollView { return scrolling }
        return scrollingSuperview(of: superview)
    }

    // and switch on/off scrolling
    let scrollView = scrollingSuperview(of: self)
    scrollView?.isScrollEnabled = enabled
}

在包含滚动视图的视图控制器中,我有以下内容:
// Not sure what exactly this does, but I think it allows the drawing view to have precedence. This was the missing piece of the puzzle.
scrollView.delaysContentTouches = false

这对我有用。顺便说一句,你可能也想在touchesCancelled上启用滚动! - Jared Updike

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