多级UIScrollView嵌套-升级版本

多级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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352