static const CGFloat VerticalListCategoryViewHeight = 60
static const NSUInteger VerticalListPinSectionIndex = 1
@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]
_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.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
[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
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)
}
}
- (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]
}
}
}
- (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