iOS 吸頂,雙 ScrollView 處理

場(chǎng)景

雙ScrollView

向上滾動(dòng)時(shí),先 rootScrollView 向上滾動(dòng),等到 header 消失,containerScrollView 到達(dá)頂部時(shí),containerScrollView 再開始滾動(dòng)。向下滾動(dòng)時(shí),等到 containerScrollView 滾動(dòng)到頂部不能繼續(xù)滾動(dòng)時(shí),rootScrollView 才可以開始滾動(dòng),顯示 header。

方法

  • 根據(jù) scrollView 的都偏移量,設(shè)置 rootScrollView 和 containerScrollView 的 isScrollEnabled。缺點(diǎn):scrollView 的 isScrollEnabled 切換時(shí),平移手勢(shì)會(huì)中斷,滾動(dòng)不連貫。

  • rootScrollView 和 containerScrollView 可以同時(shí)識(shí)別平移手勢(shì),限制其 contentOffset 。(下面會(huì)詳細(xì)描述)

    1. 設(shè)置 rootScrollView 和 containerScrollView 可以同時(shí)滾動(dòng)
    2. containerScrollView.bounces = false,否則也會(huì)出現(xiàn)滑動(dòng)不連貫
    3. rootScrollView 可以滾動(dòng)時(shí),containerScrollView 不能滾動(dòng),保持 contentOffset 不變
    4. containerScrollView 可以滾動(dòng)時(shí),rootScrollView 不能滾動(dòng),保持 contentOffset 不變

代碼實(shí)例

設(shè)置 rootScrollView 和 containerScrollView 可以同時(shí)滾動(dòng)

只要其中一個(gè) scrollView 實(shí)現(xiàn)了 UIGestureRecognizerDelegate,并且 gestureRecognizer(_:shouldRecognizeSimultaneouslyWith otherGestureRecognizer:) 允許和另一個(gè) scrollView 同時(shí)識(shí)別平移手勢(shì)。這里簡單起見,直接返回 true。

class MyScrollView: UIScrollView, UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

rootScrollView 可以滾動(dòng)時(shí),containerScrollView 不能滾動(dòng),保持 contentOffset 不變

保持 containerScrollView 的 contentOffset.y 不變,在 containerScrollView 的 delegate 的 scrollViewDidScroll(_:) 中設(shè)置 contentOffset.y = 0。在設(shè)置之前,要判斷 containerScrollView 能否滾動(dòng)。不能根據(jù) containerScrollView.contentOffset.y 判斷,因?yàn)檫M(jìn)入 scrollViewDidScroll(_:) 時(shí),containerScrollView.contentOffset.y 已經(jīng)發(fā)生改變。所以判斷條件為,rootScrollView.contentOffset.y 等于 containerScrollView 在 rootScrollView 中的偏移量時(shí),containerScrollView 可以滾動(dòng)。

    var containerScrollViewOffsetY: CGFloat {
        return containerScrollView.convert(containerScrollView.bounds, to: rootScrollView).minY
    }

    func canContainerScrollViewScroll() -> Bool {
        return rootScrollView.contentOffset.y >= containerScrollViewOffsetY
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView === containerScrollView {
            guard !canContainerScrollViewScroll() else {
                return
            }
            containerScrollView.contentOffset.y = 0
        }
    }

containerScrollView 可以滾動(dòng)時(shí),rootScrollView 不能滾動(dòng),保持 contentOffset 不變

同理,在 rootScrollView 的 scrollViewDidScroll(_:) 中,不能根據(jù) rootScrollView.contentOffset.y 判斷 rootScrollView 是否可以滾動(dòng),而是通過
containerScrollView.contentOffset.y == 0 判斷。

    func canRootScrollViewScroll() -> Bool {
        return containerScrollView.contentOffset.y == 0
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView === rootScrollView {
            guard !canRootScrollViewScroll() else {
                return
            }
            scrollView.contentOffset.y = containerScrollViewOffsetY
        }
    }

完整代碼


import UIKit
import SnapKit

class ViewController: UIViewController {
    
    let rootScrollView = MyScrollView()
    let headerView = UIView()
    let containerScrollView = UIScrollView()
    let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        setupUI()
    }

    func setupUI() {
        let rootView = UIView()
        view.addSubview(rootView)
        rootView.snp.makeConstraints { make in
            make.edges.equalTo(view.safeAreaLayoutGuide)
        }
        
        rootView.addSubview(rootScrollView)
        rootScrollView.addSubview(headerView)
        rootScrollView.addSubview(containerScrollView)
        containerScrollView.addSubview(label)
        
        rootScrollView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        rootScrollView.delegate = self
//        rootScrollView.bounces = false
        
        headerView.backgroundColor = .blue
        headerView.snp.makeConstraints { make in
            make.left.right.top.equalToSuperview()
            make.width.equalTo(rootView)
            make.height.equalTo(100)
        }
        
        containerScrollView.snp.makeConstraints { make in
            make.left.right.bottom.equalToSuperview()
            make.top.equalTo(headerView.snp.bottom)
            make.height.equalTo(rootView)
        }
        containerScrollView.delegate = self
        // 2. containerScrollView.bounces = false
        containerScrollView.bounces = false
        
        label.snp.makeConstraints { make in
            make.width.equalTo(rootView)
            make.edges.equalToSuperview()
        }
        label.numberOfLines = 0
        var text = ""
        for i in 1...100 {
            text += "\(i)\n"
        }
        label.text = text
        label.backgroundColor = .lightGray
    }
    
    var containerScrollViewOffsetY: CGFloat {
        return containerScrollView.convert(containerScrollView.bounds, to: rootScrollView).minY
    }
    
    func canRootScrollViewScroll() -> Bool {
        return containerScrollView.contentOffset.y == 0
    }
    
    func canContainerScrollViewScroll() -> Bool {
        return rootScrollView.contentOffset.y >= containerScrollViewOffsetY
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView === rootScrollView {
            guard !canRootScrollViewScroll() else {
                return
            }
            // 4. scrollView 不能滾動(dòng),保持 contentOffset 不變
            scrollView.contentOffset.y = containerScrollViewOffsetY
        } else if scrollView === containerScrollView {
            guard !canContainerScrollViewScroll() else {
                return
            }
            // 3. containerScrollView 不能滾動(dòng),保持 contentOffset 不變
            containerScrollView.contentOffset.y = 0
        }
    }
}

class MyScrollView: UIScrollView, UIGestureRecognizerDelegate {
    // 1. 設(shè)置 rootScrollView 和 containerScrollView 可以同時(shí)滾動(dòng)
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容