cell滑动标题横向滑动效果

586 阅读2分钟
#import "ViewController.h"
#import "JXCategoryView.h"
#import "VerticalTableSectionCategoryHeaderView.h"
#import "VerticalTableSectionHeaderView.h"
#import "VerticalListTableView.h"

static const CGFloat VerticalListCategoryViewHeight = 60;   //悬浮categoryView的高度
static const NSUInteger VerticalListPinSectionIndex = 1;    //悬浮固定section的index

@interface ViewController () <UITableViewDataSource, UITableViewDelegate, JXCategoryViewDelegate>

@property (nonatomic, strong) VerticalListTableView *tableView;
@property (nonatomic, strong) UIActivityIndicatorView *loading;
@property (nonatomic, strong) NSArray <NSArray <NSString *>*> *dataSource;
@property (nonatomic, strong) NSArray <NSString *> *headerTitles;
@property (nonatomic, strong) JXCategoryTitleView *pinCategoryView;
@property (nonatomic, strong) UIView *sectionCategoryHeaderView;
@property (nonatomic, strong) NSArray <NSValue *> *sectionHeaderRectArray;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.tableView];
    [self.view addSubview:self.loading];
    [self loadData];
}

- (VerticalListTableView *)tableView {
    if (!_tableView) {
        _tableView = [[VerticalListTableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];//仅支持UITableViewStyleGrouped
        _tableView.backgroundColor = [UIColor colorWithRed:0.94 green:0.94 blue:0.94 alpha:1];
        _tableView.dataSource = self;
        _tableView.delegate = self;
        _tableView.estimatedRowHeight = 0.0;
        _tableView.sectionHeaderHeight = 0.0;
        _tableView.sectionFooterHeight = 0.0;
        _tableView.estimatedSectionHeaderHeight = 0.0;
        _tableView.estimatedSectionFooterHeight = 0.0;
        _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAlways;
        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:NSStringFromClass([UITableViewCell class])];
        [_tableView registerClass:[VerticalTableSectionCategoryHeaderView class] forHeaderFooterViewReuseIdentifier:NSStringFromClass([VerticalTableSectionCategoryHeaderView class])];
        [_tableView registerClass:[VerticalTableSectionHeaderView class] forHeaderFooterViewReuseIdentifier:NSStringFromClass([VerticalTableSectionHeaderView class])];
        __weak typeof(self)weakSelf = self;
        _tableView.layoutSubviewsCallback = ^{
            [weakSelf updateSectionHeaderAttributes];
        };
    }
    return _tableView;
}

- (JXCategoryTitleView *)pinCategoryView {
    if (!_pinCategoryView) {
        _pinCategoryView = [[JXCategoryTitleView alloc] init];//创建pinCategoryView,但是不要被addSubview
        _pinCategoryView.backgroundColor = [UIColor colorWithRed:0.94 green:0.94 blue:0.94 alpha:1];
        _pinCategoryView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, VerticalListCategoryViewHeight);
        _pinCategoryView.titles = @[@"超级大IP", @"热门HOT", @"周边衍生", @"影视综", @"游戏集锦", @"搞笑百事"];
        JXCategoryIndicatorLineView *lineView = [[JXCategoryIndicatorLineView alloc] init];
        lineView.verticalMargin = 15;
        _pinCategoryView.indicators = @[lineView];
        _pinCategoryView.delegate = self;
    }
    return _pinCategoryView;
}

- (UIActivityIndicatorView *)loading {
    if (!_loading) {
        _loading = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
        _loading.bounds = CGRectMake(0, 0, 100, 100);
        _loading.transform = CGAffineTransformMakeScale(3, 3);
        _loading.center = self.view.center;
        [_loading startAnimating];
    }
    return _loading;
}

- (void)loadData {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.75 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //模拟数据加载
        [self.loading stopAnimating];
        [self.loading removeFromSuperview];
        
        self.headerTitles = @[@"我的频道", @"超级大IP", @"热门HOT", @"周边衍生", @"影视综", @"游戏集锦", @"搞笑百事"];
        NSMutableArray *tempDataSource = [NSMutableArray array];
        for (NSString *headerTitle in self.headerTitles) {
            NSMutableArray *tempSectionTitles = [NSMutableArray array];
            for (NSInteger index = 0; index <= 10; index ++) {
                [tempSectionTitles addObject:[NSString stringWithFormat:@"%@:%ld", headerTitle, index]];
            }
            [tempDataSource addObject:tempSectionTitles];
        }
        self.dataSource = tempDataSource;
        [self.tableView reloadData];
    });
}

- (void)updateSectionHeaderAttributes {
    //获取到所有的sectionHeaderRect,用于后续的点击,滚动到指定contentOffset.y使用
    NSMutableArray *rects = [NSMutableArray array];
    CGRect lastHeaderRect = CGRectZero;
    for (int i = 0; i < self.headerTitles.count; i++) {
        CGRect rect = [self.tableView rectForHeaderInSection:i];
        [rects addObject:[NSValue valueWithCGRect:rect]];
        if (i == self.headerTitles.count - 1) {
            lastHeaderRect = rect;
        }
    }
    if (rects.count == 0) {
        return;
    }
    self.sectionHeaderRectArray = rects;
    
    //如果最后一个section条目太少了,会导致滚动最底部,但是却不能触发categoryView选中最后一个item。而且点击最后一个滚动的contentOffset.y也不好弄。所以添加contentInset,让最后一个section滚到最下面能显示完整个屏幕。
    CGRect lastCellRect = [self.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:self.dataSource[self.headerTitles.count - 1].count - 1 inSection:self.headerTitles.count - 1]];
    CGFloat lastSectionHeight = CGRectGetMaxY(lastCellRect) - CGRectGetMinY(lastHeaderRect);
    CGFloat value = (self.view.bounds.size.height - VerticalListCategoryViewHeight) - lastSectionHeight;
    if (value > 0) {
        self.tableView.contentInset = UIEdgeInsetsMake(0, 0, value, 0);
    }
}

#pragma mark - UITableViewDataSource, UITableViewDelegate

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.dataSource.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataSource[section].count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 50;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([UITableViewCell class]) forIndexPath:indexPath];
    cell.textLabel.text = self.dataSource[indexPath.section][indexPath.row];
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section == VerticalListPinSectionIndex) {
        return VerticalListCategoryViewHeight;
    }
    return 40;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    if (section == VerticalListPinSectionIndex) {
        VerticalTableSectionCategoryHeaderView *headerView = (VerticalTableSectionCategoryHeaderView *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:NSStringFromClass([VerticalTableSectionCategoryHeaderView class])];
        self.sectionCategoryHeaderView = headerView;
        if (self.pinCategoryView.superview == nil) {
            //首次使用VerticalSectionCategoryHeaderView的时候,把pinCategoryView添加到它上面。
            [headerView addSubview:self.pinCategoryView];
        }
        return headerView;
    }else {
        VerticalTableSectionHeaderView *headerView = (VerticalTableSectionHeaderView *)[tableView dequeueReusableHeaderFooterViewWithIdentifier:NSStringFromClass([VerticalTableSectionHeaderView class])];
        headerView.textLabel.text = self.headerTitles[section];
        return headerView;
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGRect sectionHeaderRect = self.sectionHeaderRectArray[VerticalListPinSectionIndex].CGRectValue;
    if (scrollView.contentOffset.y >= sectionHeaderRect.origin.y) {
        //当滚动的contentOffset.y大于了指定sectionHeader的y值,且还没有被添加到self.view上的时候,就需要切换superView
        if (self.pinCategoryView.superview != self.view) {
            [self.view addSubview:self.pinCategoryView];
        }
    }else if (self.pinCategoryView.superview != self.sectionCategoryHeaderView) {
        //当滚动的contentOffset.y小于了指定sectionHeader的y值,且还没有被添加到sectionCategoryHeaderView上的时候,就需要切换superView
        [self.sectionCategoryHeaderView addSubview:self.pinCategoryView];
    }
    if (self.pinCategoryView.selectedIndex != 0 && scrollView.contentOffset.y == 0) {
        //点击了状态栏滚动到顶部时的处理
        [self.pinCategoryView selectItemAtIndex:0];
    }
    if (!(scrollView.isTracking || scrollView.isDecelerating)) {
        //不是用户滚动的,比如setContentOffset等方法,引起的滚动不需要处理。
        return;
    }
    //用户滚动的才处理
    //获取categoryView下面一点的所有布局信息,用于知道,当前最上方是显示的哪个section
    NSArray <NSIndexPath *>*topIndexPaths = [self.tableView indexPathsForRowsInRect:CGRectMake(0, scrollView.contentOffset.y + VerticalListCategoryViewHeight + 1, self.tableView.bounds.size.width, 200)];
    NSIndexPath *topIndexPath = topIndexPaths.firstObject;
    NSUInteger topSection = topIndexPath.section;
    if (topIndexPath != nil && topSection >= VerticalListPinSectionIndex) {
        if (self.pinCategoryView.selectedIndex != topSection - VerticalListPinSectionIndex) {
            //不相同才切换
            [self.pinCategoryView selectItemAtIndex:topSection - VerticalListPinSectionIndex];
        }
    }
}

#pragma mark - JXCategoryViewDelegate

- (void)categoryView:(JXCategoryBaseView *)categoryView didClickSelectedItemAtIndex:(NSInteger)index {
    //这里关心点击选中的回调!!!
    CGRect targetHeaderRect = self.sectionHeaderRectArray[index + VerticalListPinSectionIndex].CGRectValue;
    if (index == 0) {
        //选中了第一个,特殊处理一下,滚动到sectionHeaer的最上面
        [self.tableView setContentOffset:CGPointMake(0, targetHeaderRect.origin.y) animated:YES];
    }else {
        //不是第一个,需要滚动到categoryView下面
        [self.tableView setContentOffset:CGPointMake(0, targetHeaderRect.origin.y - VerticalListCategoryViewHeight) animated:YES];
    }
}

@end