效果如下图:
- 整体是一个tableview,里面的列表内容还需要一个tableview
- 上面的广告图需要随着滑动隐藏,而课程条目和下面的内容头(“全部课程”)需要停留在界面不被隐藏
- 子tableview支持上下拉刷新
另外还有一些小功能,比如子tableview可以左右滑动切换不同科目的tableview,不在此次讨论范围。
先定义两个tableview:
MainTableView,作为外层的tableview
ContentTableView,作为内容tableview
对于需要同时响应手势的视图,需要为底层的MainTableView视图添加一个代理方法:
//遵守手势协议
@interface MainTableView : UITableView<UIGestureRecognizerDelegate>
//.m中实现代理方法
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
这样当手势触发滑动的时候,两个tableview的scrollViewDidScroll就会同时触发
主要的思路就是判断几个场景:
1.上拉时主tableview存在上拉空间(还有内容可被隐藏),则子tableview不动,让主tableview自由滑动
2.上拉时主tableview不存在上拉空间(需要隐藏的内容已经全部被隐藏),则主tableview开始固定不动,子tableview自由发挥
3.下拉时子tableview存在下拉空间(还没拉到顶),则主tableview不动,让子tableview自由滑动
4.下拉时子tableview不存在下拉空间(已经拉到顶),主tableview也已经拉到顶,则让主tableview的contentoffset固定到(0,0)
对于判断是上拉还是下拉,可以对tableview设置一个lastOffsetY来记录上次的contentOffset.y,再根据判断lastOffsetY与当前的contentOffset.y大小来判断。另外因为内容视图可能会不止一个ContentTableView,所以为了方便获取,为其添加一个block属性,传递自身作为参数
#import <UIKit/UIKit.h>
@interface ContentTableView : UITableView<UITableViewDelegate,UITableViewDataSource,UIScrollViewDelegate>
@property (nonatomic,strong) void (^contentTableViewDidScroll)(ContentTableView *);
@property (nonatomic, assign) CGFloat lastCurrentOffsetY;
@end
.m中的初始化方法先设置lastCurrentOffsetY为0
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
self = [super initWithFrame:frame style:style];
if (self) {
self.delegate = self;
self.dataSource = self;
self.lastCurrentOffsetY = 0;
}
return self;
}
//在子tableview中的代理方法只是将block执行,在主控制器中实现block
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (self.contentTableViewDidScroll) {
self.contentTableViewDidScroll(self);
}
self.lastCurrentOffsetY = scrollView.contentOffset.y;
NSLog(@"lastCurrentOffsetY:%.2f",self.lastCurrentOffsetY);
}
在控制器中实现滑动的逻辑
创建子tableview及子tableview的block实现代码,可在viewdidload中执行
- (ContentTableView *)setupContentTableView{
ContentTableView *contentTableView = [[ContentTableView alloc] initWithFrame:CGRectMake(0 , 0, SCREEN_WIDTH, SCREEN_HEIGHT -45) style:UITableViewStylePlain];
//模拟上下拉
contentTableView.mj_header= [MJRefreshNormalHeader headerWithRefreshingBlock:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[contentTableView.mj_header endRefreshing];
});
}];
contentTableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[contentTableView.mj_footer endRefreshing];
});
}];
contentTableView.contentTableViewDidScroll = ^(ContentTableView *currentTableView) {
NSLog(@"currentTableViewoffsetY:%.2f",currentTableView.contentOffset.y);
NSInteger changeOffset = [_tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]].origin.y;
self.currentTableView = currentTableView;
if (currentTableView.contentOffset.y>currentTableView.lastCurrentOffsetY){
NSLog(@"c上拉");
if (self.tableView.contentOffset.y<changeOffset && currentTableView.lastCurrentOffsetY>=0) {
NSLog(@"c上拉,主tableview存在上拉空间,子tableview处于非MJRefresh刷新顶点");
//此时,子tableview保持contentoffset不变
currentTableView.contentOffset = CGPointMake(0, currentTableView.lastCurrentOffsetY);
}
}
if (currentTableView.contentOffset.y<currentTableView.lastCurrentOffsetY){
NSLog(@"c下拉");
if (currentTableView.contentOffset.y<=0 && self.tableView.contentOffset.y>0) {
NSLog(@"c下拉,子tableview已经下拉到最顶部,主tableview还存在下拉空间");
//此时,子tableview保持contentoffset为0,0
currentTableView.contentOffset = CGPointMake(0, 0);
}
}
};
return currentTableView;
}
以及控制器中主tableview的代理方法:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSLog(@"offsetY:%.2f",scrollView.contentOffset.y);
NSInteger changeOffset = [_tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]].origin.y;
if (scrollView.contentOffset.y>self.lastOffsetY) {
NSLog(@"m上拉");
if (scrollView.contentOffset.y>=changeOffset) {
NSLog(@"m上拉,主tableview隐藏完毕");
//此时保持主tableview的offset为隐藏零界点
scrollView.contentOffset = CGPointMake(0, changeOffset);
}
}
if (scrollView.contentOffset.y<self.lastOffsetY){
NSLog(@"m下拉");
if (self.currentTableView.contentOffset.y>0) {
NSLog(@"m下拉,子tableview存在下拉空间");
//此时主tableview保持不动
scrollView.contentOffset = CGPointMake(0, self.lastOffsetY);
}else{
if (scrollView.contentOffset.y<=0) {
NSLog(@"m下拉,子tableview已经下拉到最顶部,m主tableview也已经拉倒最顶部");
//此时保持主tableview不动,这样子tableview还可以响应下拉控件
scrollView.contentOffset = CGPointMake(0, 0);
}
}
}
self.lastOffsetY = scrollView.contentOffset.y;
NSLog(@"lastOffsetY:%.2f",self.lastOffsetY);
}
这样就实现了想要的滑动,写的有点乱,主要就是contentTableView的block和mainTableView的scrollViewDidScroll代理方法