PYScrollToolBarView(解決了很多手勢沖突)

scrollToolBarView演示.gif

OC: 工具類下載
pod 'PYToolBarScrollView'
swift:工具下載
pod 'PYToolBarScrollView_Swift'

一、簡介

這個工具寫了很久,一直不滿意,換了n種方法,最后毛瑟頓開,用最平常的知識解決了問題。雖然很簡單,但很巧妙。

  1. 適用結(jié)構(gòu):
    1. 頂部有一個topView
    2. 中間有個選項欄(toolBarView)
    3. 底部有scrollVIew的集合(UITableView,UICollectionView)
  2. 效果:
    1. 隨著底部的scrollView的滾動,topView與toolBarView也跟著上下滾動。
    2. toolBarView的到頂部的時候懸停
  3. 主要解決的問題:
    1. 解決了根據(jù)底部scrollView的不同contentOffset設(shè)置topView與toolBarView的高度問題
    2. 解決了中間toolBar懸停的問題
    3. 解決了底部scrollView左右滑動的問題

二、 知識點

  1. scrollView的一些知識,看這里
  2. 關(guān)于toolBarView的封裝,看這里
  3. CoreGraphics的知識,看這里

三、工具結(jié)構(gòu)

整體由最低層的ScrollView、topView、midToolBarView、bottomScrollView、還有bottomScrollViews組成
1. 主要的包含關(guān)系

  1. 最底層scrollVIew
    1.在他的上面有topView,midToolBarView,bottomScrollView,bottomScrollViewArray
    1. 這樣的話就可以做到讓bottomViews,midToolBarView,topView,一起上下滾動,只修改最低層的scrollView的contentOffset就可以了
  2. 頂部的topView
    為了擴展性,這個頂部的topView是由外部傳進來
  3. 中間的toolBarView
    1. 這個是選項欄,也就是點擊相應(yīng)的按鈕,底部的BottomScrollView就會相應(yīng)相應(yīng)的界面
    2. toolBarView的點擊事件有傳出到外部
    3. toolBarView的titleArray應(yīng)該與BottomScrollView中的bottomScrollViewArray數(shù)目一致
    4. 點擊滑動到相應(yīng)的ScrollView界面
    5. 與下部的BottomScrollView滑動不會產(chǎn)生沖突
  4. 底部的BottomScrollView
    1. 主要是承接bottomScrollViewArray,讓他們依次排列,并且可以左右滾動
    2. 設(shè)置了分頁,每次到新的頁面都會向外發(fā)送index和ScrollView消息
    3. 對數(shù)組長度進行了判斷,避免了數(shù)組越界造成的崩潰
  5. bottomScrollViewArray
    1. 這個是外部傳入的scrollView 的數(shù)組
    2. 內(nèi)部監(jiān)聽了bottomScrollViewArray元素的contentOffset,對self.contentOffset進行設(shè)置,達到聯(lián)動效果

四、遇到的問題

1. 當?shù)撞坑卸鄠€scrollView或者多個view的時候,解決底部scrollView的contentOffset不一致造成的self.contentOffset的滑動突兀的問題
造成這個問題的根本原因是:

  1. 我在外部傳入BottomScrollViewArray的時候會先判斷其是否為scrollView,如果是scrollView,那么監(jiān)聽了scrollView的contentOffset,并根據(jù)scrollView的contentOffset,改變self.contentOffset
  2. 在監(jiān)聽的會調(diào)函數(shù)中,根據(jù)監(jiān)聽到的ScrollView的滾動的contentOffset改變self.contentOffset
  3. 如果,bottomScrollViewArray中有A、B兩個scrollView做下面操作
    1. A滾動30的距離(這時候self.contentOffset.y跟隨A變成了30)
    2. 現(xiàn)在切換到了B,這時候就會出現(xiàn)問題
    3. 因為B的contentOffset.y為0,而self.contentOffset為30,當你在滑動B的時候,B的contentOffset發(fā)生改變,那么將對self.contentOffset重新賦值,這時候,B的contentOffset.y為0,而self.contentOffset.y為30,則self.contentOffset會直接變成0

解決方案:

  1. 添加了一個OffsetY變量。
    在將要切換的底部的scrollView的時候?qū)與B進行contentOffset.y差值計算。
    在B滑動的時候把差值也算入到self.contentOffset中。
    在滑動到頂部,或者底部的時候,對offsetY進行清零
    但是還是有缺陷,比如A的contentOffset.y 為0,而self.contentOffset.y已經(jīng)到最大,那么切換到A,向下拉,也會有self直接掉下來的突兀感

代碼:

 ///布局bottomScrollView的subView (把subView添加到了bottomScrollViewView里面)
    private func setupBottomScrollViewSubView(_ contentOffsetY: CGFloat) {
        for index: NSInteger in 0 ..< self.bottomViewArray.count {
            //布局subview
            let view: UIView = self.bottomViewArray[index]
            self.bottomScrollView.addSubview(view)
            view.frame = CGRect(x: kToolBarScrollViewW * CGFloat(index), y:0, width: kToolBarScrollViewW, height: kBottomScrollViewH + contentOffsetY)
            //如果要是是ScrollView的子類那么監(jiān)聽contentOffset
            if view is UIScrollView {
                let scrollView: UIScrollView = view as! UIScrollView
                scrollView.addObserver(self, forKeyPath: "contentOffset", options: .new, context: nil)
            }
        }
    }

 ///通知的方法
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "contentOffset" {
//            print(change?[NSKeyValueChangeKey.newKey] ?? "----- 沒有紙")
            let scrollView: UIScrollView = object as! UIScrollView
            //獲取偏移量
            let newValue: CGPoint = change?[NSKeyValueChangeKey.newKey] as! CGPoint
            self.newValue = newValue;
            //改變scrollView偏移的位置
            if scrollView.contentOffset.y <= 0{
                if newValue.y < 0 {
                    self.offset = 0
                }
                self.contentOffset = CGPoint(x: 0, y: 0)
            }
            if scrollView.contentOffset.y >= self.kTopViewH {
                if newValue.y > self.kTopViewH {
                    self.offset = 0
                }  
                self.contentOffset = CGPoint(x: 0, y: self.kTopViewH)
            }
//            let isScrollBottom = Int(scrollView.contentSize.height - self.contentOffset.y) <= Int(scrollView.frame.size.height);
            if scrollView.contentSize.height <= scrollView.frame.size.height + kTopViewH {   
                let insertY = scrollView.frame.size.height + kTopViewH - scrollView.contentSize.height   
                scrollView.contentInset = UIEdgeInsetsMake(0, 0, insertY, 0)
            }else{
                scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
            }
            self.contentOffset = CGPoint(x: 0, y: newValue.y + self.offset)
        }
    }

2. 當前顯示的scrollView的contentSize滑動不到頂部,底部的scrollView就會顯示不全
解決方法:
在滾動的時候判斷,當前的scrollView的滑動范圍,是否足以讓self滑動到頂部

 if scrollView.contentSize.height <= scrollView.frame.size.height + kTopViewH {
                
                let insertY = scrollView.frame.size.height + kTopViewH - scrollView.contentSize.height
                
                scrollView.contentInset = UIEdgeInsetsMake(0, 0, insertY, 0)
            }else{
                scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
            }
            
            self.contentOffset = CGPoint(x: 0, y: newValue.y + self.offset)
        }

更新:2017.11.15

1. PYMidView的擴展

  1. 對中間的toolBarView的擴展性,進行了修復(fù)。添加了一個PYMidView(繼承自UIView),他有個代理屬性,var delegate: PYToolBarViewProtocol?要求實現(xiàn)一個方法,返回對應(yīng)的toolBarView,
  2. 事實上,你只要繼承PYMidView,然后在里面布局子控件,并且,把代理屬性設(shè)置成自己,實現(xiàn)代理方法,就可以完美適應(yīng)任何產(chǎn)品需求。

*代碼

import UIKit
class PYMidView: UIView {
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    var delegate: PYToolBarViewProtocol?
    private var isFirstSetToolBarUI: Bool = true
    
    override func layoutSubviews() {
        if isFirstSetToolBarUI {
            self.delegate?.registerToolBarView().displayUI()
            layoutIfNeeded()
            isFirstSetToolBarUI = false
        }
    }
}

更新:2018.1.5

進行了重構(gòu),具體思路差不多,只是提高了兼容性,與遺留bug的修復(fù)。
下一步,準備兼容web 滑動的監(jiān)聽。具體看代碼:

OC: 工具類下載
pod 'PYToolBarScrollView'
swift:工具下載
pod 'PYToolBarScrollView_Swift'

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

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

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