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, width: bounds.width, height: bounds.height)

contentSize = CGSize(width: bounds.width, height: topScrollView.frame.maxY)

 

到這里,就已經大功告成了!demo下載:https://github.com/wangdachui/ScrollViewNested

進階的需求

上下滑的同時,還要求左右滑:

 

具體就不多講了,有興趣看源碼。 demo下載:https://github.com/wangdachui/TabScrollView


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM