【问题标题】:Handling touches for nested UIScrollViews scrolling in the same direction处理嵌套 UIScrollViews 在同一方向滚动的触摸
【发布时间】:2015-02-11 14:25:17
【问题描述】:

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

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

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

在一个连续的动作中实现这一点的最佳方法是什么?

【问题讨论】:

  • 您可以在滚动视图之上放置一个透明的同级视图并使用它来拦截手势。然后,您将它们转发到一个滚动视图或另一个,具体取决于它们分别滚动了多远。或者——一个完全不同的想法:我敢打赌,有一些方法可以通过巧妙的自动布局约束来做到这一点......
  • 关于覆盖策略的棘手部分是 Scrollview B 仍然需要接受触摸(例如:点击按钮)。因此,仅根据滚动视图位置转发所有触摸是行不通的。
  • 你终于得到这个问题的解决方案了吗
  • 找到解决办法了吗?? :
  • @user2393462435 你有解决这个问题的办法吗?

标签: ios iphone uitableview uiscrollview scroll


【解决方案1】:

滚动视图 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;

【讨论】:

  • 如果scrollViewB 是放置在scrollViewA 上的水平scrollView 的子级,就像这个问题一样-stackoverflow.com/q/35745353/5615133。我有一个确切的要求。你能看看上面的共享问题吗?
【解决方案2】:

您要求这应该在一个连续动作中起作用,这就是答案:您只需要使用一个 UIScrollView,而不是两个。

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

对于架构,请使用现有图表并将 Scrollview B 替换为 View B。

【讨论】:

  • 我想知道这是否可以通过嵌套滚动视图来完成,方法是关闭除最外层滚动视图以外的所有滚动视图,并让这个最外层的父级在其 @987654326 中更新其他滚动视图的 frames @ 方法。特别是,如果它适用于嵌套的 UICollectionView 实例,这将很有吸引力。我的猜测是它不会起作用,但它会很好。也可以设置布局约束。
  • 实现这一点的一个非常好的方法是使用包含scollView 实例的NestedScrollViewHostView。它的layoutSubviews 方法将查找响应者链以找到最顶层的UIScrollView,拾取其框架,将其转换为本地坐标,并将其应用于包含的UIScrollView 子级(可能是集合或列表视图,当然)。添加scrollView 将关闭孩子的滚动。这将允许将UICollectionView 嵌套在UICollectionView 中,并让子级仅在它们变得可见时创建单元格。
  • @Benjohn 如果scrollViewB 是放置在scrollViewA 上的水平scrollView 的子级,就像这个问题一样——stackoverflow.com/q/35745353/5615133。我有一个确切的要求。你能看看上面的共享问题吗?
  • 恐怕我不明白您所链接的问题中的要求,抱歉。我尝试了我描述的技术,它似乎可以工作。不过我没有把它放到产品中,所以我不能保证它没有根本问题。它也是适度参与的,所以如果可以的话,我会想念它。
【解决方案3】:

同向滚动

当作为UIScrollView 的子视图的UIScrollView 都向相同方向滚动时,会发生相同方向滚动。如左图所示。

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

只需设置上图中提到的一些属性。它有效。

【讨论】:

  • 你能指定属性吗?
  • 只有当你是gestureRecognizer的代表时才会调用这个方法。但你不能成为 tableView 的panGestureRecognizer 的代表。
【解决方案4】:

我通过以下方式解决了这个问题。我对它不太满意,因为它在我看来太复杂了,但它可以工作(请注意,下面的代码是我的代码的简化、未经测试的版本,这是由于不同的 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 减速,它的行为类似于 2 阶段滚动,因为附加的平移手势识别器不会识别任何平移手势。

【讨论】:

  • 嗨@ReinhardManner 你能帮我解决我的问题吗? link我正在尝试实现你所做的但无法实现连续滚动
【解决方案5】:

在我的例子中,我解决了外部 ScrollView 的子类 UIScrollView。

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

    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool
    {
         return true
    }
}

【讨论】:

  • 嗨@DavideCandita 你能帮我解决我的问题吗? link 我尝试为我的外部 ScrollView 子类化 UIScrollView 但无法使其正常工作。您能否解释一下我如何在链接中将其应用于我的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-09-20
  • 2015-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-20
  • 2013-03-17
相关资源
最近更新 更多