zoukankan      html  css  js  c++  java
  • UIScrollView嵌套的完美解决方案

    UIScrollView嵌套的完美解决方案

    做iOS开发,不可避免的会遇到UIScrollView的嵌套问题,之前也曾遇到过,吭哧吭哧做完了,效果不理想,和产品大战好几回合,就那样了。不可避免的,又一次遇到了这个问题,就和同事一起研究了一下,彻底解决了这个问题。写了一个demo,以后再遇到就直接用了。今天主要是总结一下实现难点。免得自己过段时间又忘了,也给有同样困扰的你一个思路。

    需求

    如图:

    要求:上滑的时候先滑headerView,headerView滑出屏幕时,tableView吸顶且开始滑动。下滑时先滑tableView,滑到顶部第一个cell出现,则开始滑headerView。 这是一个最简单的scrollView嵌套需求,后面还会有进阶的需求。

    具体方案

    其实嵌套最大的问题就是手势冲突问题,上层的ScrollView会拦截手势,导致手指在上层ScrollView滑动的时候,下层ScrollView不动。所以我们首先要让手势冲突时,两个手势都去响应。这样,我们滑动的时候,两个scrollView都会滑动。

    第一步 上层scrollView不拦截手势

     
    extension TopScrollView: UIGestureRecognizerDelegate {
        //手势冲突的时候同时响应
        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
            return true
        }
    }
    

    第二步 创建上下文对象

    上下两层scrollView滑动时候都需要对方的offset来计算,所以我们创建一个上下文对象,让两个scrollView都持有,避免了频繁正反向传值的问题。

     
    class SyncScrollContext {
        var maxOffsetY: CGFloat = 0 //上层最大的滑动距离
        var outerOffset: CGPoint = CGPoint.zero //上层offset
        var innerOffset: CGPoint = CGPoint.zero //下层offset
    }

    第三步 滑动的时候计算滑动优先级

    下层scrollView的contentOffset变化时计算: ~~~ class BottomScrollView: UIScrollView {

     
    class BottomScrollView: UIScrollView {
    
        var syncScrollContext: SyncScrollContext?
        
        override var contentOffset: CGPoint {
            didSet {
                if contentOffset.y != oldValue.y {
                    //下层scrollView滑动
                    if syncScrollContext.innerOffset.y > 0 {
                        // 上层的scrollView滑动,则下层的scrollView保持最大滑动距离
                        contentOffset.y = syncScrollContext.maxOffsetY
                    } else {
                        //否则,上层不动,下层滑动
                    }
                    //同步offset到上下文
                    syncScrollContext.outerOffset = contentOffset
                }
            }
        }
     }
    

    上层的scrollView的contentOffset变化时计算:

     
    class TopScrollView: UITableView {
        
        var syncScrollContext: SyncScrollContext?
        
        override var contentOffset: CGPoint {
            didSet {
                if contentOffset.y != oldValue.y {
                    //上层滑动
                    guard let syncScrollContext = syncScrollContext else { return }
                    if syncScrollContext.outerOffset.y < syncScrollContext.maxOffsetY {
                        //下层的offset < 下层可滑动最大值,说明下层还需要滑动,上层不动offset为0
                        contentOffset.y = 0
                    }
                    //不管怎么样,滑动即同步offset到上下文
                    syncScrollContext.innerOffset = contentOffset
                }
            }
        }
    }
    

    第四步 两个ScrollView嵌套,并正确设置下层scrollView的contentSize

    在下层BottomScrollView里面,添加topScrollView并设置contentSize。下层scrollView的contentSize的高 = headerView.height + topScrollView.height。这样,当下层scrollView滑了y(y = headerView的高度)的时候,下层scrollView滑到底了,这时候c下层scrollView无法滑动,也就不存在手势冲突,上层scrollView自动开始响应,流畅的滑动起来了

     
    topScrollView.frame = CGRect(x: 0, y: offsetY,  bounds.width, height: bounds.height)
    
    contentSize = CGSize( bounds.width, height: topScrollView.frame.maxY)

    到这里,就已经大功告成了!demo下载:https://github.com/wangdachui/ScrollViewNested

    进阶的需求

    上下滑的同时,还要求左右滑:

     

    具体就不多讲了,有兴趣看源码。 demo下载:https://github.com/wangdachui/TabScrollView

  • 相关阅读:
    [Bzoj2152]聪聪可可
    [2019杭电多校第七场][hdu6655]Just Repeat
    [2019杭电多校第七场][hdu6651]Final Exam
    [2019杭电多校第七场][hdu6646]A + B = C(hash)
    [2019杭电多校第六场][hdu6641]TDL
    [2019杭电多校第六场][hdu6638]Snowy Smile(维护区间最大子段和)
    abc179f
    Codeforces Round #680A
    Codeforces Round #680B
    Codeforces Round #681 D
  • 原文地址:https://www.cnblogs.com/6duxz/p/10044873.html
Copyright © 2011-2022 走看看