多級UIScrollView嵌套-升級版本

多級UIScrollView嵌套

升級版本 解決三大問題:1.多控制器時候 切換導(dǎo)致秩序問題 2.通知命名重復(fù)沖突問題3.優(yōu)化代碼


QQ20200601-143648-HD.gif

結(jié)構(gòu)

結(jié)構(gòu).png
  • HomePageVC 的子控制器homeParentVC、homeParentVC管理多個子控制器HomeSonVC
  • HomePageVC的scrollView必須允許多手勢
  • HomeSonVC的scrollView可以不允許多手勢、HomeSonVC繼承BaseScrollViewVC即可

BaseScrollViewVC(直接繼承即可)

.h

/**
 1.解決多個VC通知發(fā)送影響問題
    1.1 接受 baseScrollNotiName + 通知名 + baseScrollIndex HomePageListsItemVC+kNSGoTop+1 接受都是子控制器 一對一 (以前是一對多 父類控制器發(fā)送 很多子類接受)
    1.1 發(fā)送通知  發(fā)送是可以用同一個名字 HomePageListsItemVC+KNSLeaveTop+all (多對一 所以父類控制器不怕發(fā)送多)
 */

#import "BaseVC.h"

NS_ASSUME_NONNULL_BEGIN

@interface BaseScrollViewVC : BaseVC
@property(nonatomic,strong)NSString *baseScrollNotiName;//多VC下滑動標(biāo)記發(fā)通知
@property(nonatomic,assign)NSInteger baseScrollIndex;//多VC下滑動標(biāo)記發(fā)通知
@property(nonatomic,assign)BOOL canScroll;//是否可以滾動 默認(rèn)是不可滾動的 必須等父類控制器的通知
@property(strong,nonatomic)UIScrollView *currentScrollView;//通過scroll代理綁定當(dāng)前滑動視圖
@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];
    //子控制器視圖到達(dá)頂部的通知
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(acceptMsg:) name:[NSString stringWithFormat:@"%@+%@+%ld",self.baseScrollNotiName,kNestingScrollGoTopNoti,self.baseScrollIndex] object:nil];
    //這個千萬不能加 要在主控制器先判斷主控制器(如果HomePageVC)滑動狀態(tài)
    //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)
    {
        //不可以滑動 設(shè)置 CGPointZero 都很準(zhǔn)在0.0000 沒有因?yàn)樯舷禄瑒?        [scrollView setContentOffset:CGPointZero];
    }
    CGFloat offsetY = scrollView.contentOffset.y;
    if (offsetY < 0)
    {
        //這里發(fā)送離開通知...
        //NSLog(@"子控制器發(fā)送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;
    
    //回調(diào)偏移量
    !self.callBackScrollBlcok ?: self.callBackScrollBlcok (scrollView);
}
//處理左滑右滑,防止左滑返回上一界面
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    // 首先判斷otherGestureRecognizer是不是系統(tǒng)pop手勢
    if ([otherGestureRecognizer.view isKindOfClass:NSClassFromString(@"UILayoutContainerView")])
    {
        // 再判斷系統(tǒng)手勢的state是began還是fail恢总,同時判斷scrollView的位置是不是正好在最左邊
        if (otherGestureRecognizer.state == UIGestureRecognizerStateBegan && self.currentScrollView.contentOffset.x == 0)
        {
            return YES;
        }
    }
    return NO;
}
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end

  • 為了解決通知名字不重復(fù)蚤蔓,所以需要.h傳入進(jìn)來參數(shù)
  • 臨界點(diǎn)控制setContentOffset偏移量
  • 一個block用于回調(diào)偏移量防止子類重寫scrollViewDidScroll
  • 子類繼承控制器必須有UIScrollView子類并且設(shè)置代理不然.m里面scrollViewDidScroll不執(zhí)行了
  • 子類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;//臨界點(diǎn)(大于等于臨界點(diǎn)不能滑動NO,小于臨界點(diǎn)可以滑動)
@property(nonatomic,assign)BOOL canMovePrevious;
@end

@implementation HomePageVC
#pragma mark - life生命區(qū)
- (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;
    //監(jiān)聽
    [[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;
        
        //這里處理已經(jīng)創(chuàng)建的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 會變小 從而影響臨界點(diǎn)問題
    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(@"不可以滑動 大于或等于臨界點(diǎn)");
                //發(fā)送通知 必須針對 當(dāng)前的VC 不能一樣
                [[NSNotificationCenter defaultCenter]postNotificationName:[NSString stringWithFormat:@"%@+%@+%ld",@"HomeSonVC",kNestingScrollGoTopNoti,self.managerVC.currentIndex] object:nil];
                _canScroll = NO;
            }
            if (self.canMove == YES && self.canMovePrevious == NO) {
                //NSLog(@"可以滑動 小于臨界點(diǎn)");
                //[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)默認(rèn)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;//提供給發(fā)通知的
/**
 刷新當(dāng)前子控制器
 */
- (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;
//記錄當(dāng)前子控制器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
{
    //準(zhǔn)備滑動標(biāo)題
    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;
    //滑動小按鈕下面的橫線 提前創(chuàng)建 比小按鈕早添加到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循環(huán)布局
    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);
        //添加點(diǎn)擊事件
        lab.tag = index;
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(clickItem:)];
        lab.userInteractionEnabled = YES;
        [lab addGestureRecognizer:tap];
        //加入數(shù)組中
        [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;//綁定要跳轉(zhuǎn)的
        }
    }
    titleBG.contentSize = CGSizeMake(orinX, 0);
    //為什么在循環(huán)之外寫呢 因?yàn)?頭部沒有創(chuàng)建完整 進(jìn)行跳轉(zhuǎn) 頭部如果過長 會出現(xiàn) 標(biāo)題不能居中問題
    [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的回調(diào)block之類的
    //擴(kuò)大滑動范圍
    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 - 點(diǎn)擊事件
- (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(@"到達(dá)臨界值...");
            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 - 矯正滑動標(biāo)題的居中問題
- (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) {
        
    }];
    
    //標(biāo)題居中
    //NSLog(@"設(shè)置居中...");
    CGFloat offsetX = currentLab.center.x - (self.titleBG.width *0.5);
    if (offsetX < 0)
    {
        offsetX = 0;
    }
    //如果針對 標(biāo)簽數(shù)量小 滑動范圍小則出現(xiàn)問題 所有判斷一下
    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懶加載區(qū)域
- (NSMutableArray<UILabel *> *)titleBtnArrs
{
    if (!_titleBtnArrs)
    {
        _titleBtnArrs = [NSMutableArray array];
    }
    return _titleBtnArrs;
}
@end

  • @property(nonatomic,assign,readonly)NSInteger currentIndex;//提供給發(fā)通知的
  • 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 進(jìn)行修改

QQ20200601-173204-HD.gif
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    //NSLog(@"%f,%f",scrollView.contentOffset.y,ceil(scrollView.contentOffset.y));
    //最重要的就是 子視圖滑動會影響主控制器視圖 比如:子視圖向下滑動 手勢傳遞 自然contentOffsetY 會變大 子視圖向上滑動 手勢傳遞 自然contentOffsetY 會變小 從而影響臨界點(diǎn)問題
    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;
        //寬度整體擴(kuò)大
        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;
        //寬度整體恢復(fù)
        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(@"不可以滑動 大于或等于臨界點(diǎn)");
                //發(fā)送通知 必須針對 當(dāng)前的VC 不能一樣
                [[NSNotificationCenter defaultCenter]postNotificationName:[NSString stringWithFormat:@"%@+%@+%ld",@"HomeSonVC",kNestingScrollGoTopNoti,self.managerVC.currentIndex] object:nil];
                _canScroll = NO;
            }
            if (self.canMove == YES && self.canMovePrevious == NO) {
                //NSLog(@"可以滑動 小于臨界點(diǎn)");
                //[scrollView setContentOffset:CGPointMake(0, criticalPoint)];
            }
        }
    } else {
        [scrollView setContentOffset:CGPointMake(0, criticalPoint - kContentInsetTop)];
    }
}

bug匯總

如果懸停和managerVC的子控制器都是靠著網(wǎng)絡(luò)請求獲取 需要cell代理方法中添加判斷是否網(wǎng)絡(luò)請求成功 還有就是rowHeight在懶加載是0 需要重新賦值


崩潰閃退-滑動問題.png
  • 防止一直轉(zhuǎn)


    防止一直轉(zhuǎn).png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市酒奶,隨后出現(xiàn)的幾起案子蚁孔,更是在濱河造成了極大的恐慌,老刑警劉巖惋嚎,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杠氢,死亡現(xiàn)場離奇詭異,居然都是意外死亡另伍,警方通過查閱死者的電腦和手機(jī)鼻百,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門绞旅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人温艇,你說我怎么就攤上這事因悲。” “怎么了勺爱?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵晃琳,是天一觀的道長。 經(jīng)常有香客問我琐鲁,道長卫旱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任围段,我火速辦了婚禮顾翼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒜撮。我一直安慰自己暴构,他們只是感情好跪呈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布段磨。 她就那樣靜靜地躺著,像睡著了一般耗绿。 火紅的嫁衣襯著肌膚如雪苹支。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天误阻,我揣著相機(jī)與錄音债蜜,去河邊找鬼。 笑死究反,一個胖子當(dāng)著我的面吹牛寻定,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播精耐,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼狼速,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卦停?” 一聲冷哼從身側(cè)響起向胡,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎惊完,沒想到半個月后僵芹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡小槐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年拇派,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡件豌,死狀恐怖桐腌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情苟径,我是刑警寧澤案站,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站棘街,受9級特大地震影響蟆盐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜遭殉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一石挂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧险污,春花似錦痹愚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蚁飒,卻和暖如春动壤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淮逻。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工琼懊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人爬早。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓哼丈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親筛严。 傳聞我的和親對象是個殘疾皇子醉旦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353