OC: 工具類下載
pod 'PYToolBarScrollView'
swift:工具下載
pod 'PYToolBarScrollView_Swift'
一音婶、簡介
這個工具寫了很久贾富,一直不滿意,換了n種方法,最后毛瑟頓開喘先,用最平常的知識解決了問題回梧。雖然很簡單,但很巧妙浑娜。
- 適用結(jié)構(gòu):
1. 頂部有一個topView
2. 中間有個選項欄(toolBarView)
3. 底部有scrollVIew的集合(UITableView,UICollectionView)
- 效果:
1. 隨著底部的scrollView的滾動式散,topView與toolBarView也跟著上下滾動筋遭。
2. toolBarView的到頂部的時候懸停
- 主要解決的問題:
1. 解決了根據(jù)底部scrollView的不同contentOffset設(shè)置topView與toolBarView的高度問題
2. 解決了中間toolBar懸停的問題
3. 解決了底部scrollView左右滑動的問題
二、 知識點
三乖篷、工具結(jié)構(gòu)
整體由最低層的ScrollView
响驴、topView
、midToolBarView
撕蔼、bottomScrollView
豁鲤、還有bottomScrollViews
組成
1. 主要的包含關(guān)系
- 最底層scrollVIew
1.在他的上面有topView,midToolBarView鲸沮,bottomScrollView琳骡,bottomScrollViewArray
1. 這樣的話就可以做到讓bottomViews,midToolBarView讼溺,topView日熬,一起上下滾動,只修改最低層的scrollView的contentOffset就可以了
- 頂部的topView
為了擴展性肾胯,這個頂部的topView是由外部傳進(jìn)來
- 中間的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)生沖突
- 底部的BottomScrollView
1. 主要是承接bottomScrollViewArray敬肚,讓他們依次排列,并且可以左右滾動
2. 設(shè)置了分頁束析,每次到新的頁面都會向外發(fā)送index和ScrollView消息
3. 對數(shù)組長度進(jìn)行了判斷艳馒,避免了數(shù)組越界造成的崩潰
- bottomScrollViewArray
1. 這個是外部傳入的scrollView 的數(shù)組
2. 內(nèi)部監(jiān)聽了bottomScrollViewArray元素的contentOffset,對self.contentOffset進(jìn)行設(shè)置员寇,達(dá)到聯(lián)動效果
四弄慰、遇到的問題
1. 當(dāng)?shù)撞坑卸鄠€scrollView或者多個view的時候,解決底部scrollView的contentOffset不一致造成的self.contentOffset的滑動突兀的問題
造成這個問題的根本原因是:
- 我在外部傳入BottomScrollViewArray的時候會先判斷其是否為scrollView蝶锋,
如果是scrollView陆爽,那么監(jiān)聽了scrollView的contentOffset,并根據(jù)scrollView的contentOffset扳缕,改變self.contentOffset
- 在監(jiān)聽的會調(diào)函數(shù)中别威,根據(jù)監(jiān)聽到的ScrollView的滾動的contentOffset改變self.contentOffset
- 如果驴剔,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,當(dāng)你在滑動B的時候布讹,B的contentOffset發(fā)生改變,那么將對self.contentOffset重新賦值炒事,這時候,B的contentOffset.y為0挠乳,而self.contentOffset.y為30,則self.contentOffset會直接變成0
解決方案:
- 添加了一個OffsetY變量权薯。
在將要切換的底部的scrollView的時候?qū)與B進(jìn)行contentOffset.y差值計算。
在B滑動的時候把差值也算入到self.contentOffset中睡扬。
在滑動到頂部盟蚣,或者底部的時候卖怜,對offsetY進(jìn)行清零
但是還是有缺陷屎开,比如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. 當(dāng)前顯示的scrollView的contentSize滑動不到頂部档泽,底部的scrollView就會顯示不全
解決方法:
在滾動的時候判斷,當(dāng)前的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的擴展
- 對中間的toolBarView的擴展性燥滑,進(jìn)行了修復(fù)渐北。添加了一個PYMidView(繼承自UIView)突倍,他有個代理屬性盆昙,
var delegate: PYToolBarViewProtocol?
要求實現(xiàn)一個方法,返回對應(yīng)的toolBarView,- 事實上淡喜,你只要繼承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
進(jìn)行了重構(gòu)敌呈,具體思路差不多贸宏,只是提高了兼容性磕洪,與遺留bug的修復(fù)。
下一步析显,準(zhǔn)備兼容web 滑動的監(jiān)聽。具體看代碼:
OC: 工具類下載
pod 'PYToolBarScrollView'
swift:工具下載
pod 'PYToolBarScrollView_Swift'