PS:也是看别人写的。自己刚好有这个需求,看了代码,改吧改吧就用了。找不到那篇文章了。
效果:实现tableview的嵌套,页面可以左右滑动切换页面。头部轮播图隐藏的时候,选项卡实现头部悬停的效果。如下:
分析过程:
1.看到可以上下滑动,并带有头view,立马想到是最外层是UITableView
2.三个按钮的选项卡可以悬停到头部,立马想到是sectionHeader,tableview用plain样式。更加确定是最外层用tableview做的。
3.可以滑动,这种样式是scrollview或者collectionview上添加tableview
所以,层级关系是tableview-->collectionview-->tableview
开始也想着根据偏移量,打开或者关闭用户交互,设置偏移量啥的,试了试,发现效果太差,后来上网搜了,找了别人的方法,看着代码,自己试了试,能达到我需要的效果。自己看明白了,就挪到自己项目里了。
其实全篇看下来,主要是通过一个全局的BOOL值,设置tableview是否需要滑动,在scrollview的代理方法里,根据tableview的偏移量,再结合这个BOOL值,判断是否需要滑动,同时设置tableview的偏移量。
一、最外层tableview
1.上下滑动的tableview
由于屏幕上有多个tableview,需要同时相应操作,所以这个tableview需要接受多个手势。需要新建一个tableview,继承自UITableView,遵守手势代理,运行接受多个手势。暂时称为大tableview,有个canScroll属性。
h文件
#import <UIKit/UIKit.h>
@interface MainTableView : UITableView <UIGestureRecognizerDelegate>
@end
m文件
#import "MainTableView.h"
@implementation MainTableView
//允许接受多个手势
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
@end
还需要headView,sectionHeaderView等。
2.横向滑动的collectionview
由于最外层的tableview只起到上下滑动的作用,所以一个cell就够了。
cell的高度 = 屏幕高度 - 头view高度 - sectionHeader高度。
在这个cell上添加一个collectionview,横向滑动,collectionview大小和cell大小一致。
紧接着在这个collectionview上添加几个tableview,这几个tableview大小和collectionview大小一致,即和最外层唯一的大cell的大小一致。暂时称为小tableview.也有个canScroll属性。
添加小tableview的时候,用了addChildViewController方法,等于先添加子控制器,用了子控制器的tableview。
self.subVC1 = [[SubTableVC alloc] init];
[self addChildViewController:self.subVC1];
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"UICollectionViewCellID" forIndexPath:indexPath];
cell.backgroundColor = [UIColor yellowColor];
SubTableVC *subVC = self.childViewControllers[indexPath.row];
subVC.view.frame = CGRectMake(0, 0, SCREENW, SCREENH-64-49-40);
subVC.tableView.frame = CGRectMake(0, 0, SCREENW, SCREENH-64-49-40);
[cell.contentView addSubview:subVC.view];
return cell;
}
这样就完成了整体的结构。
三、监听tableview的偏移量
直接根据scollview的代理方法,- (void)scrollViewDidScroll:(UIScrollView *)scrollView;监听大tableview的滑动。
这里是重点。慢慢滑动页面,慢慢体会。
大tableview的滑动:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.tableView) {
CGFloat scrollY = [self.tableView rectForSection:0].origin.y;
if (scrollView.contentOffset.y >= scrollY) {
if (self.canScroll == YES) {
self.canScroll = NO;
self.subVC1.canScroll = YES;
self.subVC1.tableView.contentOffset = CGPointZero;
self.subVC2.canScroll = YES;
self.subVC2.tableView.contentOffset = CGPointZero;
self.subVC3.canScroll = YES;
self.subVC3.tableView.contentOffset = CGPointZero;
}
self.tableView.contentOffset = CGPointMake(0, scrollY);
}else{
//这句判断,是在小tableview往滑动的时候,需要固定大tableview的偏移量,原因参考第6点。
if (self.canScroll == NO) {
self.tableView.contentOffset = CGPointMake(0, scrollY);
}
}
}
}
过程分析:
1.根据实际需求,首次加载页面的时候,大tableview需要滑动,小tableview不需要滑动,这个时候是大tableview带着小tableview跑。所以大tableview的canScroll=YES,小tableview的canScroll=NO。两者的属性值一直是相反的。实际情况也只这样 ,滑动的时候,只需要一个滑动就够了。
2.等滑动头view消失的时候,大tableview就不需要滑动,小tableview滑动。
(scrollY是头tableview中第一个section的Y坐标。也就是头view需要消失的时候的偏移量)。
3.第一次向下滑动,从大tableview下滑动开始,直到头view消失之前,这个时间段这个代理方法不需要啥操作,这段时间是大tableview带着小tableview滑动。
4.等第一次滑动偏移量=scrollY的时候,,这个时候大tableview就不需要滑动了,该小tableview滑动了。所以这时候需要if (scrollView.contentOffset.y >= scrollY)这个判断开始起作用。
5.继续往下滑,都是小tableview在滑动了。看起来貌似没大tableview的事情了。接着就网上滑动了,需要回去了。
6.重点:虽然canScroll属性=NO,但不代表大小tableview不会滑动了,它每时每刻都在响应着scrollview的代理方法。它还会滑动,只是我们不让它滑动,不需要滑动的时候,我们就固定它的偏移量。(这点很重要)
小tableview滑动的监听
block是为了通知大tableview可以滑动了。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (self.canScroll == NO) {
scrollView.contentOffset = CGPointZero;
}
if (scrollView.contentOffset.y < 0 ) {
self.canScroll = NO;
scrollView.contentOffset = CGPointZero;
self.block();
}
}
7.页面初始化canScroll=NO,所以为了不让它滑动,就设置偏移量=0,等到可以滑动的时候,canScroll=YES。
8.该往上滑回到顶部了。第6点说过,tableview一直响应滑动的方法,所以一直网上滑,当scrollView.contentOffset.y < 0,说明小tableview到顶部了,就不需要它滑动了,设置canScroll=NO,通知大tableview可以滑动了。
9.在block通知中设置大tableview canScroll=YES,回到了初始状态,完成了整个过程。
demo