处理触摸嵌套的UIScrollView以相同的方向滚动

我有两个嵌套的UIScrollViews,都在垂直方向滚动。 我需要滚动到外部滚动视图,以允许内部滚动视图滚动之前的最大范围。 内滚动视图不应滚动,直到外滚动视图达到其最大范围。 这是一个例子: 嵌套Scrollviews图

在左图中, Scrollview B的垂直拖动应该移动Scrollview A和滚动Scrollview B不应该可滚动(但仍然需要能够接收触摸/点击)。 一旦Scrollview A达到其最大范围(当Scrollview B到达屏幕的顶部时),则滚动Scrollview B应该滚动。 这需要一个连续的工作。

我试图从ScrollView AscrollViewDidScroll: delegate方法切换ScrollView BscrollEnabled ,但是这似乎不是一个可行的解决scheme,因为它不能在一个连续的运动中工作(例如:用户需要释放在Scrollview B到达屏幕的顶部之后再次触摸)。

实现这个目标的最好方法是什么?

你的要求,这应该工作在一个连续的动议叫出答案:你只需要使用一个UIScrollView,而不是两个。

如果你只有一个滚动视图,你可以通过覆盖滚动视图的layoutSubviews方法并重新调整它的内容来根据contentOffset的当前值来执行你的视差效果。 确保contentSize总是反映完整的高度(如果需要,甚至可以在layoutSubviews中更新contentSize)。

对于体系结构,请使用现有的图表,并将View Scrollview Breplace为View B.

我以下面的方式解决了这个问题。 我不是很满意,因为它看起来太复杂了,但是它的工作原理(请注意,下面的代码是我的代码的简化,未经testing的版本,这是由于不同的UI更复杂):

我有3个属性,控制滚动:

 @property (nonatomic, assign) CGFloat currentPanY; @property (nonatomic, assign) BOOL scrollA; @property (nonatomic, assign) BOOL scrollB; 

两步滚动:

禁用B滚动,并启用A滚动。
这允许滚动A。

当A达到其最大位置时,禁用A滚动,并启用滚动B:

 -(void)scrollViewDidScroll: (UIScrollView *)scrollView { if (scrollView.contentOffset.y >= self.maxScrollUpOffset) { [scrollView setContentOffset:CGPointMake(0, self.maxScrollUpOffset) animated:NO]; self.scrollviewA.scrollEnabled = NO; self.scrollviewB.scrollEnabled = YES; self.scrollB = YES; } } 

这产生以下效果:
当A向上滚动时,它将在达到最大尺寸时停止滚动。 然而B不会开始滚动,因为A的平移手势识别器不会将其动作转发给B的平移手势识别器。因此,人们必须抬起手指并开始第二次滚动。 然后,B将滚动。 这提供了两步滚动。

连续滚动:

对于连续滚动,B必须滚动,而开始滚动A的手指继续向上移动。 为了检测这一点,我添加了一个进一步的平移手势识别器A,并使它能够与A和B的内置手势识别器同时检测手势:

  - (BOOL)gestureRecognizer:(UIPanGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UISwipeGestureRecognizer *)otherGestureRecognizer { return YES; } 

在这个附加的平移手势识别器的作用下,我计算了在达到A的滚动限制之后手指向上移动的距离。 通过这个距离,B然后以编程方式滚动:

 - (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)recognizer { if (recognizer.state != UIGestureRecognizerStateChanged) { self.currentPanY = 0; self.scrollB = NO; self.scrollA = NO; } else { CGPoint currentTranslation = [recognizer translationInView:self.scrollviewA]; CGFloat currentYup = currentTranslation.y; if (self.scrollA || self.scrollB) { if (self.currentPanY == 0) { self.currentPanY = currentYup; } CGFloat additionalYup = self.currentPanY - currentYup; if (self.scrollA) { CGFloat offset = self.scrollviewA.scrollUpOffset + additionalYup; if (offset >= 0) { self.scrollviewA.contentOffset = CGPointMake(0, offset); } else { self.scrollviewA.contentOffset = CGPointZero; } } else if (self.scrollB){ self.scrollviewB.contentOffset = CGPointMake(0, additionalYup); } } } } 

还有一个缺点:
如果您开始滚动,抬起手指,并让scrollview减速,它将performance得像2阶段滚动,因为额外的平移手势识别器将不会识别任何平移手势。

滚动视图A的手势识别器将需要传递给手势识别器,以便滚动视图B具有连续的运动,我相当肯定是不可能的。 为什么不把两个滚动视图的内容结合起来,然后你就可以有一个连续的动作。 这段代码将把scrollView A和B的内容合并成一个A.

 UIScrollView* scrollViewA = ...; UIScrollView* scrollViewB = ...; NSArray* subviews = scrollViewB.subviews; for (int i = 0; i < subviews.count; i++) { UIView* subview = [subviews objectAtIndex:i]; CGRect frame = subview.frame; frame.origin.y += scrollViewA.contentSize.height; subview.frame = frame; [scrollViewA addSubview:subview]; } CGSize size = scrollViewA.contentSize; size.height += scrollViewB.contentSize.height; scrollViewA.contentSize = size; 

在我的情况下,我解决了外部滚动视图的子类UIScrollView。

 class MYOuterScrollView: UIScrollView, UIGestureRecognizerDelegate { override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool { return true } func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } } 

同向滚动

UIScrollView的子视图UIScrollView都以相同的方向滚动时,会发生相同方向的滚动。 这在左图中显示。

如果您想查看详细信息, 请点击此链接https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/UIScrollView_pg/NestedScrollViews/NestedScrollViews.html

在这里输入图像说明

只要设置一些在上面的图像中提到的属性。 有用。