UITableView滑动到不同分区时联动头部菜单栏

效果图

详解

页面构成

1、菜单栏 , 使用的组件是 YNPageScrollMenuView
2、UITableView

功能效果

1、首先菜单栏是隐藏的,当tableview滑动到第一个基本信息分区头时显示出来,之后滑动到具体的分区头时,菜单栏定位到具体的对应位置 2、点击菜单栏上的按钮 ,tableview滑动到指定分区位置。

分析

1、UITableView 指定滑动的2种方法
// 滚动到指定 NSIndexPath 处 
1、- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated; 
// 滚动到指定的偏移位置处
2、- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;
复制代码
2、实现思路:

获取到每个 分区头 的位置,通过 setContentOffset 设置偏移量来让tableView滚动到具体位置。

那么我们如何获得 每个 分区头 的位置呢 ?

3、具体实现:

1、 首先我们通过网络请求获取到数据,构造数据源分区数组。(此时我们知道了我们有几个分区,每个分区有几个Rows。)

2、核心:获取到每个分区的Rect:遍历分区数据源分区数组,滚动到每个分区的第一行,通过系统提供的 rectForSection 获取到每个分区的Rect,将y值保存到一个数组里。

    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; 
        }
    }];

复制代码

3、 设置表尾,如果不设置表尾,我们是无法将最后一个分区或者几个分区滚动到屏幕最上方。所以我们需要设置一个表尾。

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

4、到目前为止,我们获取到每个分区的具体的位置信息了,那么点击菜单栏按钮,滚动到对应分区就很好实现了。在菜单栏按钮的点击的代理方法中:

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

5、然后就是滚动UITableView时获取偏移量,滑动到具体分区Rect的y值时,设置菜单栏滚动到指定按钮处。核心就是 UIScrollView 的几个代理方法的处理。

#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;
}

复制代码

6、处理偏移量,定位到具体哪个菜单栏按钮

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

结束

转载于:https://juejin.im/post/5c9c709bf265da60cd2b6d00

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值