ScrollView嵌套tableView聯動滾動最佳實踐

前言

隨着業務的發展,頁面的複雜度愈來愈高,嵌套滾動視圖的方式也愈來愈受設計師們的青睞,在各大電商App十分常見。以下Demo圖:git

可是這樣的交互官方並不推薦,並且對開發來講確是不那麼友好,須要處理滾動手勢的衝突,頁面的多層級嵌套都給開發帶來了必定程度的麻煩。接下里我聊聊咱們的實現思路。github

思路和過程

對應這種頁面結構應該毫無疑問是最底層是一個縱向滾動的scrollView,它的頁面上面放一個固定高度的header,緊接着下面一個支持橫向滾動切換的容器scrollView,容器上面纔是各個頁面具體的tableView,以下圖:bash

struct

思路一

最早想到的是,既然是滾動視圖那麼咱們是否能夠經過滾動視圖的可滾動屬性來作呢,在初始時把最上層具體業務的tableView禁止滾動,那麼根據事件響應鏈,滾動事件事件會由底層的ScrollView接收並處理,在到達最大偏移量以後,禁用底層的ScrollView滾動,同時開啓上層的tableView,使得上層能夠滑動,想起來是有必定可行性的,惋惜,事實現實是殘酷的,效果以下:app

這樣會致使當偏移量到達臨界值時,因爲設置了scrollEnable屬性和最大偏移量,這次滾動手勢會被截斷,須要再次拖拽才能繼續滾動,顯然,這樣的效果是沒法接受的。ui

思路二

這是同事提供的思路,在作這個時和同事有過討論,他們以前有這樣的交互頁面,使用的是自定義手勢,但因爲UIScrollView是有彈性效果的,通常的滑動手勢作不到這一點的,因此須要引入UIDynamic模擬力場,實現阻尼效果。想了一下,雖然有必定的可行性,可是爲了一個聯動滑動,要作這麼多的事情,感受比較繁瑣,並且自定義手勢作的模擬彈性效果可能和原生ScrollView的效果仍是有必定的差距,因此選擇放棄。spa

思路三

回到咱們思路一,除了邊界位置會阻斷聯動滾動外,其餘效果仍是能夠的,那麼能不能經過手段解決這個問題呢?既然能寫到了這裏,那麼毫無疑問,確定是能夠的。經過手勢穿透,即讓一個滑動手勢既做用於底層的ScrollView又能做用於上層的業務tableView,同時控制他們的滾動便可達成目的。經過讓底層的scrollView實現一個手勢識別的協議,同時響應滾動事件:設計

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
複製代碼

根據官方文檔描述:代理

Asks the delegate if two gesture recognizers should be allowed to recognize gestures simultaneously.
複製代碼

表達的意思是詢問委託是否容許兩個手勢識別器同時識別手勢,那麼咱們實現這個協議,」穿透「手勢,分別在底層容器和上層業務中實現滾動視圖的代理方法func scrollViewDidScroll(_ scrollView: UIScrollView),分別控制他們的可滾動狀態和偏移量則能實現目的。部分實現以下:code

底層容器ScrollView:cdn

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        headerView.isHidden = scrollView.contentOffset.y >= maxOffset ? true : false
        if !superCanScroll {
            scrollView.contentOffset.y = maxOffset
            currentVC.childCanScroll = true
        } else {
            if scrollView.contentOffset.y >= maxOffset {
                scrollView.contentOffset.y = maxOffset
                superCanScroll = false
                currentVC.childCanScroll = true
            }
        }
    }
複製代碼

上層業務tableView:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if !childCanScroll {
            scrollView.contentOffset.y = 0
        } else {
            if scrollView.contentOffset.y <= 0 {
                childCanScroll = false
                superCanScrollBlock?(true)
            }
        }
    }
複製代碼

經過底層ScrollView是否達到最大偏移量控制header的顯示隱藏和對應的偏移量及可滾動狀態,在業務tableView使用回調將ScrollView的可滾動狀態回調達到狀態同步。整體來講仍是比較清晰,更多細節請看QFMultipleScrollView

相關文章
相關標籤/搜索