处理嵌套 UIScrollViews 中的冲突手势

Posted

技术标签:

【中文标题】处理嵌套 UIScrollViews 中的冲突手势【英文标题】:Handling Conflicting Gestures in Nested UIScrollViews 【发布时间】:2014-04-18 14:08:00 【问题描述】:

所以我意识到嵌套滚动视图是一个危险信号,但是除了一个小问题之外,所有的当前设置实际上都运行良好。一个滚动视图管理在集合中的滚动,而另一个处理整个集合视图的缩放和平移。这一切都有效,但小问题来自放大和向下平移时,滚动视图在集合视图滚动时平移,导致视图滚动速度翻倍,并且感觉不到手指连接。

我最理想的情况是在可以平移时垂直滚动由外部滚动视图管理,然后当外部滚动视图无法再平移时由内部滚动视图处理。我写了这样的东西非常接近:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
    if (scrollView == _outerScrollView) 
        CGPoint offset = _outerScrollView.contentOffset;
        CGFloat height = _outerScrollView.frame.size.height;
        CGFloat contentHeight = _outerScrollView.contentSize.height;

        ScrollDirection scrollDirection;
        if (offset > _lastContentOffset)
            scrollDirection = ScrollDirectionUp;
         else 
            scrollDirection = ScrollDirectionDown;
        

        BOOL scrollIsAtTop =  offset.y <= 0;
        BOOL scrollIsAtBottom = offset.y + height >= contentHeight;

        //If there is a pan upward and we aren't at the top of the outer
        //scrollview cancel the gesture on the inner view
        //downward vice versa
        if (!((scrollIsAtTop && scrollDirection == ScrollDirectionUp) 
               || (scrollIsAtBottom && scrollDirection == ScrollDirectionDown))) 
            _innerCollectionView.panGestureRecognizer.enabled = NO;
            _innerCollectionView.panGestureRecognizer.enabled = YES;
         
    
    _lastContentOffset = offset.y;

这个几乎有效,一个副作用是当它到达底部时大平底锅向下停止,并要求用户开始一个新的手势来继续滚动内部集合。理想情况下,这种过渡会很顺利,但我很难找到一种方法来做到这一点。我再次意识到滚动视图中的滚动视图并不理想,但如果我能解决这个小问题,一切都会好起来的,而不是尝试重新设计整个事情。

关于如何以一种让平移手势获胜的方式处理双滚动,但当外部滚动视图不能再垂直平移时干净地过渡到内部集合的任何想法?

【问题讨论】:

您有可以快速制作的视频吗?或者压缩项目? 不容易,如果我不能很快找到修复程序,我可以制作一个重现它的演示项目,但这需要我一个小时左右,所以我会继续努力,看看是否在此期间,任何人都有任何好的建议。 你有没有想过这个问题?我目前有同样的问题。 @ArtSabintsev 我确实想出了一个最终奏效的(有点糟糕的)解决方案,我会发布并回答它的样子。 谢谢!我在嵌套的 UICollectionView 和它的 UIScrollView 超级视图之间有整个橡皮筋问题。我做了或多或少和你一样的事情,这让我感到安慰,我走在正确的轨道上,而且不是世界上唯一遇到这个烦人问题的人 :) 【参考方案1】:

所以,由于我从来没有得到任何答案,这就是我一直在使用的解决方案。本质上,如果内部集合视图不在顶部或底部,我将重置外部滚动视图在 scrollViewDidScroll 中的 y 偏移更改。代码如下所示:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
    if (scrollView == _outerScrollView) 
        if (![self innerScrollIsAtTop] && ![self innerScrollIsAtBottom] && !self.allowOuterScroll) 
            [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x, self.lastContentOffset)];
        
        self.lastContentOffset = scrollView.contentOffset.y;
    

使用这两个便利:

- (BOOL)innerScrollIsAtTop 
    return _innerCollectionView.contentOffset.y <= 0;


- (BOOL)innerScrollIsAtBottom 
    CGFloat zoom = self.zoomScale;
    CGFloat height = _innerCollectionView.frame.size.height;
    CGFloat contentHeight = _innerCollectionView.contentSize.height;
    CGPoint offset = _innerCollectionView.contentOffset;

    return offset.y + height / zoom >= contentHeight;

你需要 2 个类变量,一个 float 来保存外滚动的前一个 y 内容偏移量,以及一个 BOOL 来保存是否允许外滚动视图滚动,你可以将其设置为 YES 而缩放或以编程方式滚动。此解决方案修复了双滚动,但在 scrollviewDidScroll 中确实有一个繁琐的 hack,可能会在以后给您带来麻烦,您需要经常解决,但现在这是我一直在使用的解决方案。

【讨论】:

感谢您的帮助。不幸的是,它没有用。这是我的具体问题:***.com/questions/25793141/… 我今天晚些时候再看看 启用分页以使其快速向上/向下跳跃是一个不错的技巧。但我仍然想知道如何将外部滚动视图的速度传递给孩子的速度。 Twitter 在他们的个人资料页面上这样做,对我来说,他们是如何做到这一点的,这对我来说是个谜。

以上是关于处理嵌套 UIScrollViews 中的冲突手势的主要内容,如果未能解决你的问题,请参考以下文章

嵌套的 uiscrollviews 和自定义处理事件路由

处理滚动嵌套 UIScrollViews 在同一方向滚动

UIScrollView嵌套TableView手势冲突问题

嵌套 UIScrollViews 中的滚动问题

当ViewPager嵌套在ScrollView/ListView里时,手势冲突如何处理?

iOS 嵌套 UIScrollViews 使用 AutoLayout