欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

嵌套tableview的滑动问题

程序员文章站 2024-01-28 18:23:04
...

效果如下图:
嵌套tableview的滑动问题

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