[iOS] 列表滑動展開隱藏頭部HeaderView


平常遇到大多數的帶有列表的應用都會遇到這個場景:在列表頂端有一個Header,當向上滑動列表時,壓縮header,向下滑動列表到頭時,展開header。這種樣式在例如微博,twitter這些展示動態的界面里很常見。這種效果怎么實現呢?下面介紹我用的方法。

新博客:wossoneri.com

先看一下效果圖

首先看一下BiliBili客戶端的視頻瀏覽界面。默認界面Header完全展開,並且Header顯示AV號(別亂想,就是視頻編號了)以及播放按鈕。滑動之后Header被壓縮,按鈕移到AV號左邊。

bilibiliTop

我就照着界面簡單實現了主要功能,比較簡陋。對於按鈕移動的動畫就沒有去花時間還原了,畢竟這里主要是為了實現滾動壓縮、展開Header,動畫不討論。

myScroll

實現思路

如圖所示:

首先在要將該界面分成兩部分:一個ScrollHeader,一個UITableView

  • ScrollHeader占據屏幕上方,高度為展開后的高度
  • UITableView占據整個屏幕,這樣可以完全滾動。為了讓內容不被ScrollHeader遮蓋,設置contentOffset屬性即可

我這里用的ScrollHeader是作為獨立的控件使用,與UITableViewHeaderView並無關系

之后將ScrollHeader分成兩部分:topViewbottomView

  • topView 即為壓縮后的布局
  • bottomView 即為展開后的布局
    我這里采取將topView固定在ScrollHeader的頂部,覆蓋在bottomView上方,根據滑動對其淡入淡出。

另一種效果是把topViewbottomView上下連接在一起,也就是沒有覆蓋關系,然后當bottomView向上滑時topView從屏幕外滑入屏幕內。這個讀者可以嘗試着實現一下。

實現方法

首先按照前面的設計將界面布局好,之后的重點是為ScrollHeader增加滑動效果。
由於我的ScrollHeader繼承的是UIView,所以為了處理滑動,為其設置一個UIScrollView

@property (nonatomic, strong) UIScrollView *headerScrollView;

這個屬性的作用就是獲得UITableView對應的scrollView,因為UITableView本身是繼承UIScrollView的,所以在初始化ScrollHeader的時候可以這么寫:

MyScrollHeader header = [[MyScrollHeader alloc] init];
header.headerScrollView = _tableView;

這樣,在ScrollHeader中就可以通過headerScrollView來判斷滑動狀態了。

剩下的工作就是捕捉滑動狀態,並且對滑動距離進行計算,移動topViewbottomView了。對於計算也不做過多說明了,因為沒有幾張草圖也說不清。直接貼上代碼,跟着代碼算一下就知道怎么回事了。

#pragma mark - scroll state
-(void)willMoveToSuperview:(UIView *)newSuperview{
    [self.headerScrollView addObserver:self forKeyPath:@"contentOffset" options:(NSKeyValueObservingOptionNew) context:Nil];
    self.headerScrollView.contentInset = UIEdgeInsetsMake(_bottomHeight, 0, 0, 0); // tableview 偏移
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ //監視滑動
    CGPoint newOffset = [change[@"new"] CGPointValue]; 
    [self updateSubViewsWithScrollOffset:newOffset];
}

-(void)updateSubViewsWithScrollOffset:(CGPoint)newOffset {
    
    //    NSLog(@"scrollview inset top:%f", self.headerScrollView.contentInset.top);
    //    NSLog(@"new offset before:%f", newOffset.y);
    //    NSLog(@"newOffset : %f", newOffset.y);
    
    float startChangeOffset = - self.headerScrollView.contentInset.top;
    
    newOffset = CGPointMake(newOffset.x, newOffset.y < startChangeOffset ? startChangeOffset : (newOffset.y > _destinaOffset ? _destinaOffset : newOffset.y));
    //    NSLog(@"new offset after:%f", newOffset.y);
    
    float newY = - newOffset.y - _bottomHeight;
    float d = _destinaOffset - startChangeOffset;
    float alpha = 1 - (newOffset.y - startChangeOffset) / d;

    self.frame = CGRectMake(0, newY, self.frame.size.width, self.frame.size.height);
    topView.frame = CGRectMake(0, -newY, self.frame.size.width, self.frame.size.height);
    
    topView.alpha = 1 - alpha;
    bottomView.alpha = alpha;
    
    _currentOffset = newOffset.y;
    NSLog(@"current offset: %f", _currentOffset);
}

最后放上源碼

github

粗略寫的,代碼是用自動布局寫的。往后抽空會優化一下代碼,把ScrollHeader封裝成控件方便使用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM