UITableView滑動到不一樣分區時聯動頭部菜單欄

效果圖

詳解

頁面構成

一、菜單欄 , 使用的組件是 YNPageScrollMenuView
二、UITableView數組

功能效果

一、首先菜單欄是隱藏的,當tableview滑動到第一個基本信息分區頭時顯示出來,以後滑動到具體的分區頭時,菜單欄定位到具體的對應位置 二、點擊菜單欄上的按鈕 ,tableview滑動到指定分區位置。bash

分析

一、UITableView 指定滑動的2種方法

// 滾動到指定 NSIndexPath 處 
一、- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated; 
// 滾動到指定的偏移位置處
二、- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;
複製代碼

二、實現思路:

獲取到每一個 分區頭 的位置,經過 setContentOffset 設置偏移量來讓tableView滾動到具體位置。網絡

那麼咱們如何得到 每一個 分區頭 的位置呢 ?post

三、具體實現:

一、 首先咱們經過網絡請求獲取到數據,構造數據源分區數組。(此時咱們知道了咱們有幾個分區,每一個分區有幾個Rows。)動畫

二、核心:獲取到每一個分區的Rect:遍歷分區數據源分區數組,滾動到每一個分區的第一行,經過系統提供的 rectForSection 獲取到每一個分區的Rect,將y值保存到一個數組裏。ui

self.scrollYArr = [NSMutableArray array];
    __block CGRect lastRect;
    NSInteger sectioncounts = self.titleNameArr.count; 
    [self.titleNameArr enumerateObjectsUsingBlock:^(NSArray *sectionArr, NSUInteger index, BOOL * _Nonnull stop) {
        
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:index];
        [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:(UITableViewScrollPositionBottom) animated:NO];
        CGRect section_all_Rect = [self.tableView rectForSection:index];
        HTLog(@"section_all_Rect:%@",NSStringFromCGRect(section_all_Rect));
        NSInteger currentPostion = (NSInteger)section_all_Rect.origin.y;
        // 注意: 還需 減去 被 MenuView 遮擋的 Height 高度
        currentPostion -= self.menuView.height;
        [self.scrollYArr addObject:[NSNumber numberWithInteger:currentPostion]];
        
        if (index == sectioncounts - 1) {
            // 記錄最後一個分區的位置
            lastRect = section_all_Rect; 
        }
    }];

複製代碼

三、 設置表尾,若是不設置表尾,咱們是沒法將最後一個分區或者幾個分區滾動到屏幕最上方。因此咱們須要設置一個表尾。spa

CGFloat delt = self.tableView.height - lastRect.size.height - self.menuView.height;
    UIView *newFootView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, HT_SCREEN_WIDTH, delt)];
    self.tableView.tableFooterView = newFootView;
    [self.tableView setContentOffset:CGPointMake(0, 0) animated:NO];
    
複製代碼

四、到目前爲止,咱們獲取到每一個分區的具體的位置信息了,那麼點擊菜單欄按鈕,滾動到對應分區就很好實現了。在菜單欄按鈕的點擊的代理方法中:代理

- (void)pagescrollMenuViewItemOnClick:(UIButton *)label index:(NSInteger)index{
    if (index > self.titleNameArr.count - 1) {
        return;
    }
    NSInteger currentPostion = [self.scrollYArr[index] integerValue];
    [self.tableView setContentOffset:CGPointMake(0, currentPostion) animated:YES];
}
複製代碼

五、而後就是滾動UITableView時獲取偏移量,滑動到具體分區Rect的y值時,設置菜單欄滾動到指定按鈕處。核心就是 UIScrollView 的幾個代理方法的處理。code

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    // 只要contentOffset變化,都會被調用(包括用戶拖動,減速過程,直接經過代碼設置等
    if (scrollView.isTracking) {
        HTLog(@"---手指拖動中-------- calculateScrollView");
        [self calculateScrollView:scrollView]; 
    }else{
        if(scrollView.isDecelerating){
            HTLog(@"---無手指減速滾動中--------calculateScrollView");
            [self calculateScrollView:scrollView];
        }
    }
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    // 用戶開始拖動 scroll view 的時候被調用 可能須要一些時間和距離移動以後纔會觸發。
    HTLog(@"1.將要開始拖動--------")
    self.menuView.userInteractionEnabled = NO;
    // 這邊關閉菜單欄交互,是避免用戶在拖拽後手指離開屏幕,此時TableView滾動中,用戶再去點擊菜單欄。這樣是會影響tableview滑動以及菜單欄定位的。
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
    
    /*
     在 didEndDragging 前被調用,當 willEndDragging 方法中 velocity 爲 CGPointZero
     (結束拖動時兩個方向都沒有速度)時,didEndDragging 中的 decelerate 爲 NO,即沒有減速過程,
     */
    HTLog(@"2.將要結束拖動--------");
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    
    // 判斷是否有減速過程
    if (decelerate) {
        HTLog(@"3.已經結束拖動--------有減速過程,無需處理,交給減速的代理方法處理");
    }else{
        HTLog(@"3.已經結束拖動--------沒有有減速過程 calculateScrollView");
        [self calculateScrollView:scrollView];
        self.menuView.userInteractionEnabled = YES;
    }
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
    // 減速動畫開始前被調用。
    HTLog(@"4.將要開始減速--------");
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    
    HTLog(@"5.已經結束減速--------calculateScrollView");
    [self calculateScrollView:scrollView];
    self.menuView.userInteractionEnabled = YES;
}

複製代碼

六、處理偏移量,定位到具體哪一個菜單欄按鈕cdn

- (void)calculateScrollView:(UIScrollView *)scrollView{

    self.menuView.userInteractionEnabled = NO;
    
    NSInteger currentPostion = (NSInteger)scrollView.contentOffset.y;
    
    NSInteger count = self.scrollYArr.count;
    __block NSInteger index = self.menuView.currentIndex;
    [self.scrollYArr enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSInteger scrollY = [obj integerValue];
        
        if (currentPostion < scrollY) {
            index = idx - 1;
            *stop = YES;
        }else{
            if (idx == count - 1) {
                index = count - 1;
                *stop = YES;
            }
        }
    }];
    
    index = MAX(0, index);
    [self.menuView selectedItemIndex:index animated:YES];
    // 處理隱藏效果
    if(currentPostion >= [[self.scrollYArr firstObject] integerValue]){
        if(self.menuView.alpha == 0){
            [UIView animateWithDuration:0.1 animations:^{
                self.menuView.alpha = 1;
            } completion:nil];
        }
    }else{
        self.menuView.alpha = 0;
    }
}
複製代碼

結束

相關文章
相關標籤/搜索