前言
最近有不少朋友遇到了這樣的需求:一個界面上有一個headerView
、一個toolbar
和一個tableview
,在tableview
向上滾動時,headerView
和toolbar
也向上移動,在headerView
完全消失時,toolbar
就固定在導航條下面,tableview
就固定在toolbar
下面。
對于這個需求,其實并不難實現。但是有很多朋友都不知道怎么做,找不到思路,而有的朋友有點思路,但是實現出來的效果很不理想,向上或者向下滾動一點點,整個headerView一下子就消失或者出現。
聽一聽我的思路吧:首先這里將headerView
,toolbar
,tableview
放在同一個view上,也就是controller的view上。初始狀態下,將這三個view垂直方向擺放,正好放滿,且一個接著一個放。
接下來,我們一起來學習吧!
學習ObjectiveC版
現在讓我來講講我的思路。首先,向上滾動時,每次滾動都是很小的距離,但是每次獲取到的contentOffset.y都不是移動的距離,而是當前tableview的偏移距離,因此我們需要計算出實際需要移動的距離。
現在我們需要一個變量來計算上一次移動后的偏移位置,在這一次新的偏移時,我們就可以計算出兩者的差值,這個差值實際上就是視圖需要移動的距離。現在我們假設上次偏移量為previousY
,而當前的回調到代理方法中所獲取到的contentOffset.y
與previousY
的差值就是我們本次需要移動的距離。
向上滾動的情況:
向上滾動的條件是:contentOffset.y > 0
計算兩次回調的滾動差:fabs(y - previousOffsetY)值:
CGFloat bottomY = self.headerView.bottomY - fabs(y - previousOffsetY);
當前headerView
的底部y值,減去實際需要移動的距離,就可以獲取新的headerView.bottomY
值。
將下來就是更新別個兩個視圖的frame了,但是我們還需要注意邊界,以防bottomY
值超出邊界范圍。
bottomY = bottomY >= 0 ? bottomY : 0;
self.headerView.bottomY = bottomY;
self.stickyView.y = self.headerView.bottomY;
self.tableView.frame = CGRectMake(0,self.stickyView.bottomY,scrollView.width,self.view.height - self.stickyView.bottomY);
然后我們還需要更新記錄新的偏移量:
previousOffsetY = y;
這樣就可以了嗎?事實上還是有不足的地方,因為當我們一起不松手滑動,然后重復向上向下滑動,由于沒有將previousOffsetY
,因此突然完全消失了或者突然完全出現了。那么,我們還需要在適當的時機還原值。
// 如果一直不松手滑動,重復向上向下滑動時,如果沒有設置還原為0,則會出現馬上到頂的情況。
if (previousOffsetY >= self.headerView.height) {
previousOffsetY = 0;
}
最后,我們向上滾動時的效果是可以實現了,但是這樣會反復修改,因此我們可以稍微控制一下條件,當已經不再需要移動了,我們就不向下執行就可以了。
if (self.headerView.bottomY <= 0) {
return;
}
向下滾動的情況:
向下滾動的條件是: contentOffset.y < 0
對于向下滾動時,我們不需要記錄上一次的偏移量,因為我們在向下滾動時,這個偏移值就是實際我們需要累加的值。也就是直接獲取其值的絕對值,然后加上當前headerView.bottomY
的就可以計算出新的bottomY
值。
CGFloat bottomY = self.headerView.bottomY + fabs(y);
我們計算出的bottomY
,也要計算出其有效范圍。
bottomY = bottomY <= self.headerView.height ? bottomY : self.headerView.height;
剩下的就是更新其他視圖的frame
就可以了。
self.headerView.bottomY = bottomY;
self.stickyView.y = self.headerView.bottomY;
self.tableView.frame = CGRectMake(0,self.stickyView.bottomY,scrollView.width,self.view.height-self.stickyView.bottomY);
源代碼
喜歡就Star:下載StickyUpDownDemo
學習Swift版
這里的邏輯跟ObjectiveC
版的是一樣的,這里就不細說了。如果您沒有學習過ObjectiveC
,就直接學習了Swfit
,若不太懂邏輯,可聯系我。
這里只是放核心代碼出來,應該是很容易懂的。
// MARK: - UIScrollViewDelegate
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView === tableView {
if scrollView.contentOffset.x == 0 {
let y = scrollView.contentOffset.y
// 向上滾動
if y > 0 {
struct Diff {
static var previousY: CGFloat = 0.0
}
guard headerView.bottomY > 0.0 else {
return
}
var bottomY = headerView.bottomY - fabs(y - Diff.previousY)
bottomY = bottomY >= 0.0 ? bottomY : 0.0
headerView.bottomY = bottomY
stickyView.y = headerView.bottomY
tableView.frame = CGRectMake(0, stickyView.bottomY, tableView.width, self.view.height - stickyView.bottomY)
Diff.previousY = y
if Diff.previousY >= headerView.height {
Diff.previousY = 0
}
}
// 向下滾動
else if y < 0 {
if headerView.y >= 0 {
return
}
var bottomY = headerView.bottomY + fabs(y)
bottomY = bottomY <= headerView.height ? bottomY : headerView.height
headerView.bottomY = bottomY
stickyView.y = headerView.bottomY
tableView.frame = CGRectMake(0, stickyView.bottomY, tableView.width, self.view.height - stickyView.bottomY)
}
}
}
}
最后
本demo在開發中還是挺實用的,如果不想因為某個小功能而使用第三方庫,那么可以學習自己去實現!