在UIScrollView中嵌套的UICollectionView之间的连续垂直滚动

Art*_*sev 11 objective-c uiscrollview ios uicollectionview ios8

虽然我知道嵌套的scrollViews并不理想,但我们的设计师为我提供了这种设置,所以我尽力让它工作.让我们开始!

查看层次结构

  • 的UIView
    • UIScrollView(仅限垂直滚动)
      • 的UIImageView
      • UICollectionView#1(仅水平滚动)
      • UIImageView(与以前的UIImageView不同)
      • UICollectionView#2(仅垂直滚动)

重要的提示

我的所有视图都是使用程序化自动布局定义的.UIScrollView的子视图层次结构中的每个连续视图都对之前的视图具有y坐标依赖关系.

问题

为简单起见,让我们稍微修改一下命名法:

  • _outerScrollView 会参考 UIScrollView
  • _innerScrollView 会参考 UICollectionView #2

我希望我_outerScrollView将触摸事件路由到_innerScrollView其contentSize的底部.当我向后滚动时,我想反过来发生.

目前,我有以下代码:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    CGFloat bottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);
    if (bottomEdge >= [_outerScrollView contentSize].height) {
        _outerScrollView.scrollEnabled = NO;
        _innerScrollView.scrollEnabled = YES;
    } else {
        _outerScrollView.scrollEnabled = YES;
        _innerScrollView.scrollEnabled = NO;
    }
}
Run Code Online (Sandbox Code Playgroud)

其中初始条件(在任何滚动发生之前)设置为:

outerScrollView.scrollEnabled = YES;
innerScrollView.scrollEnabled = NO;
Run Code Online (Sandbox Code Playgroud)

怎么了?

在触摸视图时,outerScrollView滚动直到其底部边缘,然后具有橡皮筋效果,因为_outerScrollView.bounces = YES;如果我再次触摸视图,则innerScrollView滚动直到它到达其底部边缘.在返回的路上,相反的橡皮筋效果会以相反的顺序发生.我想要发生的是两个子视图之间的流畅运动.

显然,这是由于scrollEnabled代码片段中条件设置的条件.我想弄清楚的是如何在击中边缘时将一个scrollView的速度/速度路由到下一个scrollView.

对此问题的任何帮助将不胜感激.

其他说明

  1. 这对我不起作用:https://github.com/ole/OLEContainerScrollView
  2. 我正在考虑把在UIScrollView的层次的一切(除了UICollectionView#2)内UICollectionView#2 supplementaryView.不确定这是否有效.

Art*_*sev 6

弄清楚了!

第一:

_scrollView.pagingEnabled = YES;

第二:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == _scrollView || scrollView == _offersCollectionView) {

        CGFloat offersCollectionViewPosition = _offersCollectionView.contentOffset.y;
        CGFloat scrollViewBottomEdge = [scrollView contentOffset].y + CGRectGetHeight(scrollView.frame);

        if (scrollViewBottomEdge >= [_scrollView contentSize].height) {
            _scrollView.scrollEnabled = NO;
            _offersCollectionView.scrollEnabled = YES;
        } else if (offersCollectionViewPosition <= 0.0f && [_offersCollectionView isScrollEnabled]) {
            [_scrollView scrollRectToVisible:[_scrollView frame] animated:YES];
            _scrollView.scrollEnabled = YES;
            _offersCollectionView.scrollEnabled = NO;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

哪里:

  • _scrollView 是个 _outerScrollView
  • _offersCollectionView_innerScrollView(我原来的帖子中的UICollectionView#2).

这是现在发生的事情:

  • 当我向上滑动(因此视图向下移动)时,offersCollectionView接管整个视图,将其他子视图移出视图.
  • 如果我向下滑动(使视图向上),其余的子视图将重新聚焦scrollView的反弹效果.


sho*_*hoe 5

接受的答案对我不起作用。这是做了什么:

定义一个子类UIScrollView

class CollaborativeScrollView: UIScrollView, UIGestureRecognizerDelegate {

    var lastContentOffset: CGPoint = CGPoint(x: 0, y: 0)

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return otherGestureRecognizer.view is CollaborativeScrollView
    }
}
Run Code Online (Sandbox Code Playgroud)

由于不可能将触摸重新路由到另一个视图,因此确保外部滚动视图可以在内部滚动视图停止后继续滚动的唯一方法是它是否一直在接收触摸。但是,为了防止外部移动而内部移动,我们必须将其锁定而不将其设置isScrollEnabledfalse,否则它将停止接收触摸并且无法从内部停止的地方拾取当我们想要滚动通过内部的顶部或底部时。

这是通过将 a 分配UIScrollViewDelegate给滚动视图并实现scrollViewDidScroll(_:)如下所示:

class YourViewController: UIViewController, UIScrollViewDelegate {

    private var mLockOuterScrollView = false

    @IBOutlet var mOuterScrollView: CollaborativeScrollView!
    @IBOutlet var mInnerScrollView: CollaborativeScrollView!

    enum Direction {
        case none, left, right, up, down
    }

    func viewDidLoad() {
        mOuterScrollView.delegate = self
        mInnerScrollView.delegate = self
        mInnerScrollView.bounces = false
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard scrollView is CollaborativeScrollView else {return}
        let csv = scrollView as! CollaborativeScrollView

        //determine direction of scrolling
        var directionTemp: Direction?
        if csv.lastContentOffset.y > csv.contentOffset.y {
            directionTemp = .up
        } else if csv.lastContentOffset.y < csv.contentOffset.y {
            directionTemp = .down
        }
        guard let direction = directionTemp else {return}

        //lock outer scrollview if necessary
        if csv === mInnerScrollView {
            let isAlreadyAllTheWayDown = (mInnerScrollView.contentOffset.y + mInnerScrollView.frame.size.height) == mInnerScrollView.contentSize.height
            let isAlreadyAllTheWayUp = mInnerScrollView.contentOffset.y == 0
            if (direction == .down && isAlreadyAllTheWayDown) || (direction == .up && isAlreadyAllTheWayUp) {
                mLockOuterScrollView = false
            } else {
                mLockOuterScrollView = true
            }
        } else if mLockOuterScrollView {
            mOuterScrollView.contentOffset = mOuterScrollView.lastContentOffset
        }

        csv.lastContentOffset = csv.contentOffset
    }
}
Run Code Online (Sandbox Code Playgroud)

就是这样。当您开始滚动内部滚动视图时,这将阻止您的外部滚动视图滚动,并在内部滚动视图一直滚动到其末端之一时再次拾取它。