某一天接到一个需求,要求做一个类似微博发现页的页面,需要支持上下滑动的同时可以左右滑动展示更多的内容,就像下图这样
WX20190617-135231.png蓝框下方的部分可以左右滑动,蓝框以及蓝框上边的部分不可以左右滑,点击蓝框中的title蓝框下方会切换不同的页面,左右滑蓝框下方的页面蓝框里的title也会实时变化为高亮,往上滑的时候蓝框内的部分会吸顶
大致的实现方法是用一个大的TableView实现,这个页面大概可以分为三部分
WX20190617-135136.png
- 第一部分是固定的,其实就是tableView的HeadView
- 第二部分(暂且叫做categoryView) 其实是tableView的第一个section的headerView,内部有一个点击切换的效果
- 第三部分是tableView的一个Cell,Cell内部是一个UICollectionView,可以左右滑动,滑动时与categoryView联动,collectionView的每一个cell是一个TableView,在categoryView吸顶的时候可以自由滑动
难题一:当视图滑动一部分之后,手指在第三部分滑动的时候,会触发第三部分视图内的某个TableView的滑动,这时候外部的大的tableView就会失去滑动效果,想要滑动只能是滑动第三部分上方的视图。
解决方案:将最外部的tableView变成新建的一个tableView类,(继承自UITableView)然后将它的- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
方法重写
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]];
}
这样可以保证你在滑动第三部分里的小的tableView时,外部的大的tableView依然可以滑动,但这时候这两种滑动会重叠,也就是说你滑动第三部分的tableView时,大的tableView也在滑动,我们要的效果是无论何时只有一个tableView在滑动,所以我们要实时监测这两个tableView的偏移量,当一个tableView滑动的时候让另一个tableView的contentOffset始终为0,造成一种不动的错觉,分两种情况:
- 第二部分没有吸顶的时候,保持外部的大tableView滑动效果,小tableView虽然也可以滑动,但是要造成它不动的效果
- 第二部分吸顶的时候,只让小的tableView滑动,外部的大tableView要造成不动的效果
同时检测这两个tableView的代理方法- (void)scrollViewDidScroll:(UIScrollView *)scrollView
,针对大的TableView:
if (self.currentScrollingListView != nil && self.currentScrollingListView.contentOffset.y > 0) {
//mainTableView的header已经滚动不见,开始滚动某一个listView,那么固定mainTableView的contentOffset,让其不动
self.mainTableView.contentOffset = CGPointMake(0, [self.delegate tableHeaderViewHeightInPagerView:self]);
}
if (scrollView.contentOffset.y < [self.delegate tableHeaderViewHeightInPagerView:self]) {
//mainTableView已经显示了header,listView的contentOffset需要重置
NSArray *listViews = [self.delegate listViewsInPagerView:self];
for (UIView <JXPagerViewListViewDelegate>* listView in listViews) {
[listView listScrollView].contentOffset = CGPointZero;
}
}
针对小的tableView
if (self.mainTableView.contentOffset.y < [self.delegate tableHeaderViewHeightInPagerView:self]) {
//mainTableView的header还没有消失,让listScrollView一直为0
scrollView.contentOffset = CGPointZero;
scrollView.showsVerticalScrollIndicator = NO;
}else {
//mainTableView的header刚好消失,固定mainTableView的位置,显示listScrollView的滚动条
self.mainTableView.contentOffset = CGPointMake(0, [self.delegate tableHeaderViewHeightInPagerView:self]);
scrollView.showsVerticalScrollIndicator = YES;
}
这样就可以实现一种自动吸顶同时又比较顺滑的滑动效果
难题二: categoryView与第三部分多个View的联动效果
categoryView的实现方式是用一个collectionView实现的,下方第三部分也是一个collectionView, 在封装第二部分视图的时候,里边有一个UIScrollview类型的属性,将第三部分的collectionView赋给这个属性,然后通过KVO监听第三部分collectionView的滑动,就可以将第二部分的collectionView的cell的点击事件和第三个collectionView的滑动关联起来
[contentScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
if ([keyPath isEqualToString:@"contentOffset"] && (self.contentScrollView.isTracking || self.contentScrollView.isDecelerating)) {
//用户滚动引起的contentOffset变化,才处理。
CGPoint contentOffset = [change[NSKeyValueChangeNewKey] CGPointValue];
[self contentOffsetOfContentScrollViewDidChanged:contentOffset];
}
}
这里只是记录了大体的思路,来理解这个效果是怎样实现的,其实还有很多需要注意的细节,源代码是两个第三方JXCategoryView和 JXPagingView
网友评论