效果如下图:

ios嵌套表格 tableview嵌套_ios嵌套表格

  • 整体是一个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代理方法