美文网首页
多级UIScrollView嵌套-升级版本

多级UIScrollView嵌套-升级版本

作者: 守护地中海的花 | 来源:发表于2019-07-12 17:59 被阅读0次

    多级UIScrollView嵌套

    升级版本 解决三大问题:1.多控制器时候 切换导致秩序问题 2.通知命名重复冲突问题3.优化代码


    QQ20200601-143648-HD.gif

    结构

    结构.png
    • HomePageVC 的子控制器homeParentVC、homeParentVC管理多个子控制器HomeSonVC
    • HomePageVC的scrollView必须允许多手势
    • HomeSonVC的scrollView可以不允许多手势、HomeSonVC继承BaseScrollViewVC即可

    BaseScrollViewVC(直接继承即可)

    .h

    /**
     1.解决多个VC通知发送影响问题
        1.1 接受 baseScrollNotiName + 通知名 + baseScrollIndex HomePageListsItemVC+kNSGoTop+1 接受都是子控制器 一对一 (以前是一对多 父类控制器发送 很多子类接受)
        1.1 发送通知  发送是可以用同一个名字 HomePageListsItemVC+KNSLeaveTop+all (多对一 所以父类控制器不怕发送多)
     */
    
    #import "BaseVC.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface BaseScrollViewVC : BaseVC
    @property(nonatomic,strong)NSString *baseScrollNotiName;//多VC下滑动标记发通知
    @property(nonatomic,assign)NSInteger baseScrollIndex;//多VC下滑动标记发通知
    @property(nonatomic,assign)BOOL canScroll;//是否可以滚动 默认是不可滚动的 必须等父类控制器的通知
    @property(strong,nonatomic)UIScrollView *currentScrollView;//通过scroll代理绑定当前滑动视图
    @property(nonatomic,copy)void (^callBackScrollBlcok)(UIScrollView *scrollView);
    @end
    
    NS_ASSUME_NONNULL_END
    

    .m

    
    
    #import "BaseScrollViewVC.h"
    
    @interface BaseScrollViewVC ()<UIGestureRecognizerDelegate,UIScrollViewDelegate>
    @end
    
    @implementation BaseScrollViewVC
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        //子控制器视图到达顶部的通知
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(acceptMsg:) name:[NSString stringWithFormat:@"%@+%@+%ld",self.baseScrollNotiName,kNestingScrollGoTopNoti,self.baseScrollIndex] object:nil];
        //这个千万不能加 要在主控制器先判断主控制器(如果HomePageVC)滑动状态
        //self.canScroll = NO;
    }
    //接收信息,处理通知
    - (void)acceptMsg:(NSNotification *)notification
    {
        //NSLog(@"子控制器接受goTop通知,%@",notification.name);
        NSString *notificationName = notification.name;
        if ([notificationName isEqualToString:[NSString stringWithFormat:@"%@+%@+%ld",self.baseScrollNotiName,kNestingScrollGoTopNoti,self.baseScrollIndex]])
        {
            self.canScroll = YES;
            self.currentScrollView.showsVerticalScrollIndicator = YES;
        }
    }
    #pragma mark - ScrollView代理
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        if (!self.canScroll)
        {
            //不可以滑动 设置 CGPointZero 都很准在0.0000 没有因为上下滑动
            [scrollView setContentOffset:CGPointZero];
        }
        CGFloat offsetY = scrollView.contentOffset.y;
        if (offsetY < 0)
        {
            //这里发送离开通知...
            //NSLog(@"子控制器发送leaveTop通知,%@",[NSString stringWithFormat:@"%@+%@+all",self.baseScrollNotiName,kNestingScrollLeaveTopNoti]);
            [[NSNotificationCenter defaultCenter]postNotificationName:[NSString stringWithFormat:@"%@+%@+all",self.baseScrollNotiName,kNestingScrollLeaveTopNoti] object:nil];
            [scrollView setContentOffset:CGPointZero];
            self.canScroll = NO;
            scrollView.showsVerticalScrollIndicator = NO;
        }
        _currentScrollView = scrollView;
        
        //回调偏移量
        !self.callBackScrollBlcok ?: self.callBackScrollBlcok (scrollView);
    }
    //处理左滑右滑,防止左滑返回上一界面
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
        // 首先判断otherGestureRecognizer是不是系统pop手势
        if ([otherGestureRecognizer.view isKindOfClass:NSClassFromString(@"UILayoutContainerView")])
        {
            // 再判断系统手势的state是began还是fail,同时判断scrollView的位置是不是正好在最左边
            if (otherGestureRecognizer.state == UIGestureRecognizerStateBegan && self.currentScrollView.contentOffset.x == 0)
            {
                return YES;
            }
        }
        return NO;
    }
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    @end
    
    
    • 为了解决通知名字不重复,所以需要.h传入进来参数
    • 临界点控制setContentOffset偏移量
    • 一个block用于回调偏移量防止子类重写scrollViewDidScroll
    • 子类继承控制器必须有UIScrollView子类并且设置代理不然.m里面scrollViewDidScroll不执行了
    • 子类endRefresh记得提醒主控制器

    HomePageVC

    .h

    #import "BaseVC.h"
    NS_ASSUME_NONNULL_BEGIN
    
    @interface HomePageVC : BaseVC
    @property(nonatomic,assign,readonly)BOOL canScroll;
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    .m

    #import "HomePageVC.h"
    #import "HomeParentVC.h"
    @interface HomePageVC ()<UITableViewDelegate,UITableViewDataSource>
    @property(nonatomic,strong)BaseTableView *mainView;
    @property(nonatomic,strong)HomeParentVC *managerVC;
    //滑动属性
    @property(nonatomic,assign)BOOL canScroll;
    @property(nonatomic,assign)BOOL canMove;//临界点(大于等于临界点不能滑动NO,小于临界点可以滑动)
    @property(nonatomic,assign)BOOL canMovePrevious;
    @end
    
    @implementation HomePageVC
    #pragma mark - life生命区
    - (void)viewDidLoad {
        [super viewDidLoad];
    
    }
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        [[Factory backAPPTabBarVC]changeSelectedIndex:0];
    }
    - (void)createProperty
    {
        self.canScroll = YES;
        self.canMove = YES;
        self.canMovePrevious = YES;
        //监听
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(acceptMsg:) name:[NSString stringWithFormat:@"%@+%@+all",@"HomeSonVC",kNestingScrollLeaveTopNoti] object:nil];
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(endRefresh) name:kNestingScrollEndRefreshNoti object:nil];
    }
    - (void)createUI
    {
        //临时头部
        UIImageView *tableHeaderView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, 206*ADAPTER_WIDTH)];
        tableHeaderView.backgroundColor = RedColor;
        [tableHeaderView sd_setImageWithURL:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1590987737591&di=e0e922dae7fd800750e17753efe9bc05&imgtype=0&src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fimages%2F20190720%2F188eef713776424c933eaa2de61f9dbf.jpeg".wppURL];
        tableHeaderView.clipsToBounds = YES;
        self.mainView.tableHeaderView = tableHeaderView;
        
        //添加刷新
        WK(weakSelf)
        self.mainView.mj_header = [BossHeaderView headerWithRefreshingBlock:^{
            [weakSelf.managerVC refreshCurrentSonVC];
        }];
    }
    - (void)endRefresh
    {
        [self.mainView.mj_header endRefreshing];
    }
    #pragma mark - UITableView代理方法
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return 1;
    }
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
        cell.backgroundColor = kColorBGTinyBlue;
        [cell.contentView addSubview:self.managerVC.view];
        return cell;
    }
    #pragma mark - 滑动代码
    //接收信息,处理通知
    - (void)acceptMsg:(NSNotification *)notification
    {
        //NSLog(@"%@",notification.name);
        if ([notification.name isEqualToString:[NSString stringWithFormat:@"%@+%@+all",@"HomeSonVC",kNestingScrollLeaveTopNoti]]) {
            //NSLog(@"主控制器接受:leaveTop通知");
            self.canScroll = YES;
            
            //这里处理已经创建的view 所有contentOffsetY 会0
            [self.managerVC.childViewControllers enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                //HomePageListsItemVC
                if ([obj isKindOfClass:[BaseScrollViewVC class]]) {
                    if (obj.viewLoaded) {
                        BaseScrollViewVC *scrollVC = (BaseScrollViewVC *)obj;
                        [scrollVC.currentScrollView setContentOffset:CGPointZero];
                        scrollVC.canScroll = NO;
                    }
                }
            }];
        }
    }
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        //NSLog(@"%f,%f",scrollView.contentOffset.y,ceil(scrollView.contentOffset.y));
        //最重要的就是 子视图滑动会影响主控制器视图 比如:子视图向下滑动 手势传递 自然contentOffsetY 会变大 子视图向上滑动 手势传递 自然contentOffsetY 会变小 从而影响临界点问题
        CGFloat offset = scrollView.contentOffset.y + scrollView.contentInset.top;
        CGFloat tableHeaderShowHeight = kStatusBarHeight + 30*ADAPTER_WIDTH;
        CGFloat criticalPoint = self.mainView.tableHeaderView.height - tableHeaderShowHeight;
        if (self.canScroll) {
            self.canMovePrevious = self.canMove;
            if (ceil(offset) >= ceil(criticalPoint)) {
                [scrollView setContentOffset:CGPointMake(0, criticalPoint)];
                self.canMove = NO;
            } else {
                self.canMove = YES;
                //NSLog(@"向上移动");
            }
            if (self.canMove != self.canMovePrevious) {
                if (self.canMove == NO && self.canMovePrevious == YES) {
                    //NSLog(@"不可以滑动 大于或等于临界点");
                    //发送通知 必须针对 当前的VC 不能一样
                    [[NSNotificationCenter defaultCenter]postNotificationName:[NSString stringWithFormat:@"%@+%@+%ld",@"HomeSonVC",kNestingScrollGoTopNoti,self.managerVC.currentIndex] object:nil];
                    _canScroll = NO;
                }
                if (self.canMove == YES && self.canMovePrevious == NO) {
                    //NSLog(@"可以滑动 小于临界点");
                    //[scrollView setContentOffset:CGPointMake(0, criticalPoint)];
                }
            }
        } else {
            [scrollView setContentOffset:CGPointMake(0, criticalPoint)];
        }
    }
    #pragma mark - lazy懒加载
    - (BaseTableView *)mainView
    {
        if (!_mainView) {
            BaseTableView *tableView = [[BaseTableView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, HEIGHT - kCustomTabBarHeight) style:UITableViewStylePlain];
            [self.view addSubview:tableView];
            tableView.delegate = self;
            tableView.dataSource = self;
            tableView.tableFooterView = [UIView new];
            [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
            tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
            tableView.backgroundColor = kColorBGTinyBlue;
            if (@available(iOS 11.0, *))
            {
                tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
                tableView.estimatedRowHeight = 0;
                tableView.estimatedSectionFooterHeight = 0;
                tableView.estimatedSectionHeaderHeight = 0;
            }
            else
            {
                self.automaticallyAdjustsScrollViewInsets = NO;
            }
            tableView.rowHeight = HEIGHT - kCustomTabBarHeight;
            tableView.showsVerticalScrollIndicator = NO;
            _mainView = tableView;
        }
        return _mainView;
    }
    - (HomeParentVC *)managerVC
    {
        if (!_managerVC) {
            HomeParentVC *vc = [[HomeParentVC alloc]init];
            CGFloat tableHeaderShowHeight = kStatusBarHeight + 30*ADAPTER_WIDTH;
            vc.viewHeight = HEIGHT - kCustomTabBarHeight - tableHeaderShowHeight;
            [self addChildViewController:vc];
            _managerVC = vc;
        }
        return _managerVC;
    }
    - (UIStatusBarStyle)preferredStatusBarStyle
    {
        return UIStatusBarStyleLightContent;
    }
    @end
    
    
    
    • 核心方法acceptMsg、scrollViewDidScroll
    • 属性canScroll、canMove、canMovePrevious
      1)默认canScroll、canMove、canMovePrevious都是YES可以滑动
      2)将canMove赋值给canMovePrevious

      3) image.png

    HomeParentVC

    .h

    #import "BaseVC.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface HomeParentVC : BaseVC
    @property(nonatomic,assign)CGFloat viewHeight;
    @property(nonatomic,assign)NSInteger jumIndex;
    @property(nonatomic,assign,readonly)NSInteger currentIndex;//提供给发通知的
    /**
     刷新当前子控制器
     */
    - (void)refreshCurrentSonVC;
    @end
    
    NS_ASSUME_NONNULL_END
    

    .m

    #import "HomeParentVC.h"
    #import "HomeSonVC.h"
    #import "BSTBitmapView.h"
    #import "HomePageVC.h"
    @interface HomeParentVC ()<UIScrollViewDelegate>
    @property(nonatomic,strong)UIScrollView *titleBG;
    @property(nonatomic,strong)UIScrollView *contentBG;
    @property(nonatomic,strong)NSMutableArray<UILabel *> *titleBtnArrs;
    @property(nonatomic,strong)UILabel *preLab;
    @property(nonatomic,strong)UIView *selectedItemBottomLine;
    //记录当前子控制器index
    @property(nonatomic,assign)NSInteger currentIndex;
    @end
    
    @implementation HomeParentVC
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
    }
    - (void)createProperty
    {
        
    }
    - (void)createUI
    {
        [self createContentBG];
        [self createTitleBG];
    }
    #pragma mark - UI
    - (void)createTitleBG
    {
        //准备滑动标题
        NSMutableArray<NSString *> *nameArrs = [NSMutableArray array];
        [nameArrs addObject:@"精选"];
        [nameArrs addObject:@"排行榜"];
        [nameArrs addObject:@"会逛超市"];
        [nameArrs addObject:@"鸡蛋禽类"];
        [nameArrs addObject:@"水果蔬菜"];
        //titleBG
        UIScrollView *titleBG = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, WIDTH, 42)];
        [self.view addSubview:titleBG];
        titleBG.backgroundColor = [UIColor whiteColor];
        titleBG.showsVerticalScrollIndicator = NO;
        titleBG.showsHorizontalScrollIndicator = NO;
        //titleBG.delegate = self;
        //titleBG.bounces = NO;
        self.titleBG = titleBG;
        //滑动小按钮下面的横线 提前创建 比小按钮早添加到titleBG
        UIView *selectedItemBottomLine = [[UIView alloc]init];
        [titleBG addSubview:selectedItemBottomLine];
        selectedItemBottomLine.backgroundColor = RGB(252, 181, 111, 1);
        self.selectedItemBottomLine = selectedItemBottomLine;
        
        UITapGestureRecognizer *jumIndexTap;
        CGFloat orinX = 13*ADAPTER_WIDTH,itemMargin = 25*ADAPTER_WIDTH;
        //for循环布局
        for (NSInteger index = 0; index < nameArrs.count; index ++) {
            NSString *content = nameArrs[index];
            CGFloat itemWidth = [content sizeWithFont:[UIFont systemFontOfSize:16*ADAPTER_WIDTH weight:UIFontWeightMedium]].width;
            
            UILabel *lab = [[UILabel alloc]init];
            [titleBG addSubview:lab];
            lab.text = content;
            lab.font = [UIFont systemFontOfSize:16*ADAPTER_WIDTH weight:UIFontWeightMedium];
            lab.textColor = kColor48;
            lab.frame = CGRectMake(orinX, 14, itemWidth, 17*ADAPTER_WIDTH);
            orinX = index == (nameArrs.count - 1) ? (lab.right + 13*ADAPTER_WIDTH) : (lab.right + itemMargin);
            //添加点击事件
            lab.tag = index;
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(clickItem:)];
            lab.userInteractionEnabled = YES;
            [lab addGestureRecognizer:tap];
            //加入数组中
            [self.titleBtnArrs addObject:lab];
            if (index == self.jumIndex)
            {
                self.preLab = lab;
                selectedItemBottomLine.frame = CGRectMake(lab.left+2, lab.bottom-4, lab.width-4, 4);
                jumIndexTap = tap;//绑定要跳转的
            }
        }
        titleBG.contentSize = CGSizeMake(orinX, 0);
        //为什么在循环之外写呢 因为 头部没有创建完整 进行跳转 头部如果过长 会出现 标题不能居中问题
        [self clickItem:jumIndexTap];
    }
    - (void)createContentBG
    {
        UIScrollView *contentBG = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 42, WIDTH, self.viewHeight - 42)];
        [self.view addSubview:contentBG];
        contentBG.backgroundColor = [UIColor whiteColor];
        contentBG.showsVerticalScrollIndicator = NO;
        contentBG.showsHorizontalScrollIndicator = NO;
        contentBG.pagingEnabled = YES;
        contentBG.bounces = NO;
        contentBG.delegate = self;
        self.contentBG = contentBG;
        //添加控制器
        HomeSonVC *vc = [[HomeSonVC alloc]init];
        [self addChildViewController:vc];
        HomeSonVC *vc1 = [[HomeSonVC alloc]init];
        [self addChildViewController:vc1];
        HomeSonVC *vc2 = [[HomeSonVC alloc]init];
        [self addChildViewController:vc2];
        HomeSonVC *vc3 = [[HomeSonVC alloc]init];
        [self addChildViewController:vc3];
        HomeSonVC *vc4 = [[HomeSonVC alloc]init];
        [self addChildViewController:vc4];
        //这里可以添加VC的回调block之类的
        //扩大滑动范围
        contentBG.contentSize = CGSizeMake(WIDTH * self.childViewControllers.count, 0);
        for (NSInteger index = 0; index < self.childViewControllers.count; index ++)
        {
            BSTBitmapView *bitmapView = [[BSTBitmapView alloc]initWithFrame:CGRectMake(index*WIDTH, 0, WIDTH, contentBG.height)];
            [self.contentBG addSubview:bitmapView];
            bitmapView.type = BitmapViewTypeLoading;
            bitmapView.tag = 100+index;
        }
    }
    #pragma mark - 点击事件
    - (void)clickItem:(UITapGestureRecognizer *)sender
    {
        //如果animated 是YES 会走scrollView 多次 2个界面会好看些
        [self.contentBG setContentOffset:CGPointMake(sender.view.tag * WIDTH, 0) animated:NO];
        //处理控制器
        [self setUpSubController:sender.view.tag];
        //处理头部title
        [self refreshTitleLab:self.titleBtnArrs[sender.view.tag]];
    }
    #pragma mark - SCrollView代理方法
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        NSInteger index = scrollView.contentOffset.x / WIDTH;
        //处理控制器
        [self setUpSubController:index];
        //处理头部title
        [self refreshTitleLab:self.titleBtnArrs[index]];
    }
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        NSInteger index = scrollView.contentOffset.x / WIDTH;
        CGFloat tmpOffsetX = scrollView.contentOffset.x / WIDTH;
        CGFloat scaleRight = tmpOffsetX - index;
        CGFloat scaleLeft = 1 - scaleRight;
        if (index + 1 <= self.titleBtnArrs.count - 1)
        {
            //NSLog(@"%ld,%f,%f",index,scaleRight,scaleLeft);
            //self.selectedItemBottomLine.frame = CGRectMake(currentLab.left+2, self.selectedItemBottomLine.top, self.selectedItemBottomLine.width, self.selectedItemBottomLine.height);
            UILabel *leftLab = self.titleBtnArrs[index];
            UILabel *rightLab = self.titleBtnArrs[index+1];
            if (scaleRight == 0)
            {
                //NSLog(@"到达临界值...");
                return;
            }
            if (self.currentIndex == index)
            {
                //NSLog(@"向右");
                self.selectedItemBottomLine.frame = CGRectMake(leftLab.left+2, self.selectedItemBottomLine.top, leftLab.width-4+(scaleRight*(rightLab.left-leftLab.right)), self.selectedItemBottomLine.height);
            }
            else
            {
                //NSLog(@"向左");
                self.selectedItemBottomLine.frame = CGRectMake(rightLab.left+2-(scaleLeft*(rightLab.left-leftLab.right)), self.selectedItemBottomLine.top, leftLab.width-4+(scaleLeft*(rightLab.left-leftLab.right)), self.selectedItemBottomLine.height);
            }
        }
    }
    #pragma mark - 添加子控制器视图
    - (void)setUpSubController:(NSInteger)index
    {
        self.currentIndex = index;
        HomeSonVC *vc = self.childViewControllers[index];
        //做一些 vc的初始化
        vc.viewHeight = self.contentBG.height;
        vc.baseScrollIndex = index;
        vc.baseScrollNotiName = @"HomeSonVC";
        //判断父类控制器 是否可以滑动
        if ([self.parentViewController isMemberOfClass:[HomePageVC class]]) {
            HomePageVC *parentVC = (HomePageVC *)self.parentViewController;
            vc.canScroll = !parentVC.canScroll;
        }
        if (vc.view.superview)
        {
            return;
        }
        vc.view.frame = CGRectMake(index *WIDTH, 0, WIDTH, self.contentBG.height);
        [self.contentBG addSubview:vc.view];
        
        //清除占位图
        BSTBitmapView *bitMapView = [self.contentBG viewWithTag:100+index];
        if (bitMapView != nil)
        {
            [bitMapView removeFromSuperview];
        }
    }
    #pragma mark - 矫正滑动标题的居中问题
    - (void)refreshTitleLab:(UILabel *)currentLab
    {
        self.preLab.textColor = kColor48;
        self.preLab = currentLab;
        
        [UIView animateWithDuration:0.2 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            self.selectedItemBottomLine.frame = CGRectMake(currentLab.left+2, self.selectedItemBottomLine.top, currentLab.width-4, self.selectedItemBottomLine.height);
        } completion:^(BOOL finished) {
            
        }];
        
        //标题居中
        //NSLog(@"设置居中...");
        CGFloat offsetX = currentLab.center.x - (self.titleBG.width *0.5);
        if (offsetX < 0)
        {
            offsetX = 0;
        }
        //如果针对 标签数量小 滑动范围小则出现问题 所有判断一下
        if (self.titleBG.contentSize.width > self.titleBG.width) {
            CGFloat maxOffsetX = self.titleBG.contentSize.width - self.titleBG.width;
            if (offsetX > maxOffsetX)
            {
                offsetX = maxOffsetX;
            }
        }
        //NSLog(@"offsetX:%f",offsetX);
        [self.titleBG setContentOffset:CGPointMake(offsetX, 0) animated:YES];
    }
    #pragma mark - 公开方法
    - (void)refreshCurrentSonVC
    {
        if (self.childViewControllers.count == 0) {
            [[NSNotificationCenter defaultCenter]postNotificationName:kNestingScrollEndRefreshNoti object:nil];
            return;
        }
        HomeSonVC *vc = self.childViewControllers[self.currentIndex];
        [vc loadNewData];
    }
    #pragma mark - Lazy懒加载区域
    - (NSMutableArray<UILabel *> *)titleBtnArrs
    {
        if (!_titleBtnArrs)
        {
            _titleBtnArrs = [NSMutableArray array];
        }
        return _titleBtnArrs;
    }
    @end
    
    
    • @property(nonatomic,assign,readonly)NSInteger currentIndex;//提供给发通知的
    • setUpSubController方法中 给VC传入通知所需属性
    #pragma mark - 添加子控制器视图
    - (void)setUpSubController:(NSInteger)index
    {
        self.currentIndex = index;
        HomeSonVC *vc = self.childViewControllers[index];
        //做一些 vc的初始化
        vc.viewHeight = self.contentBG.height;
        vc.baseScrollIndex = index;
        vc.baseScrollNotiName = @"HomeSonVC";
        //判断父类控制器 是否可以滑动
        if ([self.parentViewController isMemberOfClass:[HomePageVC class]]) {
            HomePageVC *parentVC = (HomePageVC *)self.parentViewController;
            vc.canScroll = !parentVC.canScroll;
        }
        if (vc.view.superview)
        {
            return;
        }
        vc.view.frame = CGRectMake(index *WIDTH, 0, WIDTH, self.contentBG.height);
        [self.contentBG addSubview:vc.view];
        
        //清除占位图
        BSTBitmapView *bitMapView = [self.contentBG viewWithTag:100+index];
        if (bitMapView != nil)
        {
            [bitMapView removeFromSuperview];
        }
    }
    

    如果还有一个头部伸缩在criticalPoint 进行修改

    QQ20200601-173204-HD.gif
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        //NSLog(@"%f,%f",scrollView.contentOffset.y,ceil(scrollView.contentOffset.y));
        //最重要的就是 子视图滑动会影响主控制器视图 比如:子视图向下滑动 手势传递 自然contentOffsetY 会变大 子视图向上滑动 手势传递 自然contentOffsetY 会变小 从而影响临界点问题
        CGFloat offset = scrollView.contentOffset.y + scrollView.contentInset.top;
        //伸缩
        if (offset <= 0) {
            self.flexView.top = 0;
            self.flexView.height = fabs(offset) + kContentInsetTop;
            CGFloat percent = self.flexView.height / kContentInsetTop;
            //宽度整体扩大
            self.flexView.width = WIDTH * percent;
            self.flexView.left = - (WIDTH * (percent - 1) * 0.5);
        } else {
            CGFloat flexShowHeight = kStatusBarHeight + 30*ADAPTER_WIDTH;
            CGFloat flexCriticalPoint = kContentInsetTop - flexShowHeight;
            CGFloat minOffset = MIN(offset, flexCriticalPoint);
            self.flexView.top = -minOffset;
            self.flexView.height = kContentInsetTop;
            //宽度整体恢复
            self.flexView.width = WIDTH;
            self.flexView.left = 0;
        }
        //头部
        CGFloat tableHeaderShowHeight = kStatusBarHeight + 50*ADAPTER_WIDTH;
        CGFloat criticalPoint = self.mainView.tableHeaderView.height - tableHeaderShowHeight + kContentInsetTop;
        if (self.canScroll) {
            self.canMovePrevious = self.canMove;
            if (ceil(offset) >= ceil(criticalPoint)) {
                [scrollView setContentOffset:CGPointMake(0, criticalPoint - kContentInsetTop)];
                self.canMove = NO;
            } else {
                self.canMove = YES;
                //NSLog(@"向上移动");
            }
            if (self.canMove != self.canMovePrevious) {
                if (self.canMove == NO && self.canMovePrevious == YES) {
                    //NSLog(@"不可以滑动 大于或等于临界点");
                    //发送通知 必须针对 当前的VC 不能一样
                    [[NSNotificationCenter defaultCenter]postNotificationName:[NSString stringWithFormat:@"%@+%@+%ld",@"HomeSonVC",kNestingScrollGoTopNoti,self.managerVC.currentIndex] object:nil];
                    _canScroll = NO;
                }
                if (self.canMove == YES && self.canMovePrevious == NO) {
                    //NSLog(@"可以滑动 小于临界点");
                    //[scrollView setContentOffset:CGPointMake(0, criticalPoint)];
                }
            }
        } else {
            [scrollView setContentOffset:CGPointMake(0, criticalPoint - kContentInsetTop)];
        }
    }
    

    bug汇总

    如果悬停和managerVC的子控制器都是靠着网络请求获取 需要cell代理方法中添加判断是否网络请求成功 还有就是rowHeight在懒加载是0 需要重新赋值


    崩溃闪退-滑动问题.png
    • 防止一直转


      防止一直转.png

    相关文章

      网友评论

          本文标题:多级UIScrollView嵌套-升级版本

          本文链接:https://www.haomeiwen.com/subject/cgvykctx.html