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;
    }
}
复制代码

结束

相关文章
相关标签/搜索