求职iOS开发iOS进阶

iOS开发实战 - 完美解决UIScrollView嵌套滑动手势

2018-05-03  本文已影响3051人  ArchLL

1.本文介绍如何通过改变内外层scrollView的contentOffset来达到子列表页吸顶等自定义悬浮;本文看起来有点长,但是相对其他方法确实是比较简单的,如果恰巧要做这个功能,希望你认真看一看,或许真的可以帮到你;

2.最近我抽出一些时间整理了一个 demo,与下方展示的代码不同的是:该Demo中的分页部分的子视图使用的是controller,适合更复杂的分页,耦合度更低,如有疑问或者建议可在下方评论;

3.之前也写过类似 简书个人主页效果的demo ,如果想看关于头部放大、分页吸顶效果可移止本文末尾;

4.已更新两次,完美解决各种冲突、卡顿问题,可在下方查看更新内容;

先来看一下效果吧
实战演示
最新更新(2018/05/16):

真正完美解决当前页子视图(scrollView/collectionView)左右滑动和外层tableView的上下滑动不能互斥的问题, 包括除了分页部分的其他子视图,虽然上一次更新的办法也可以移植当页的其他可左右滚动的视图,但是因为一旦你的手指一直往下滑动屏幕,一旦到达能左右滑动的区域,这个时候你的手指滑动方向一般都不是直上直下的,就会触发子视图左右滑动,从而导致外层的tableView停止滑动,如果这个页面可以左右滑动的子视图比较多,就会造成一卡一卡的感觉,体验非常不好;

这次的更新不仅能解决左右滑动和上下滑动互斥问题,同时也优化了滑动体验,通过更改外层tableView的基类滑动手势的透穿方法,根据区域改变是否允许手势的透穿,该方法不仅能解决左右滑动和上下滑动互斥问题,同时也优化了滑动体验;

解决当前页面除了分页之外的其他左右滚动视图左右滑动与外层tableView上下滑动不能互斥的问题,比如品牌列表
本次更新代码比较少,但是很关键,可直接去看下方 1.1 BaseTableView 部分代码,有问题可评论或私信我;
上一次更新(2018/05/11):

解决下方分页部分左右滑动和外层tableView的上下滑动不能互斥的问题,因为一旦你的手指一直往下滑动屏幕,到达能左右滑动的区域,这个时候你的手指滑动方向一般都不是直上直下的,就会触发子视图左右滑动,从而让外层的tableView停止滑动,如果这个页面可以左右滑动的子视图比较多,就会造成一卡一卡的感觉,该方法可以参考一下,不推荐使用,因为没从根本上解决问题,当然如果主页就下方分页这部分可以左右滑动,倒也无所谓;

解决分页部分未吸顶时,分页部分左右滑动不能与外层tableView上下滑动互斥的问题
2018/05/11 更新主要代码

//内层可左右滑动的子视图处理代码
//增加分页视图左右滑动和外界tableView上下滑动互斥处理
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [[NSNotificationCenter defaultCenter] postNotificationName:IsScrollHomeVC object:nil userInfo:@{@"canScroll":@"0"}];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    [[NSNotificationCenter defaultCenter] postNotificationName:IsScrollHomeVC object:nil userInfo:@{@"canScroll":@"1"}];
}

//--------------------------------------------------------

//外层tableView代码处理
//注册允许首页外层tableView滚动通知-解决子视图左右滑动和外层tableView上下滑动的冲突问题
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptMsgOfSubView:) 
name:IsScrollHomeVC object:nil];

- (void)acceptMsgOfSubView:(NSNotification *)notification{
    NSDictionary *userInfo = notification.userInfo;
    NSString *canScroll = userInfo[@"canScroll"];
    if ([canScroll isEqualToString:@"1"]) {
        _mainTableView.scrollEnabled = YES;
    }else if([canScroll isEqualToString:@"0"]) {
        _mainTableView.scrollEnabled = NO;
    }
}


案例分析:

1.外层tableView+中间层scrollView+内层collectionView;
2.存在滑动冲突的是外层的tableView和内层的collectionView;
3.首页子scrollView的左右滑动和外层tableView上下滑动的互斥处理;
4.⚠️我这里的footerView是猜你喜欢以下部分,包含猜你喜欢(猜你喜欢不是通过viewForHeader获得的);

核心代码:

1.先看外层的tableView处理

1.1 BaseTableView

#import "XXHomeBaseTableView.h"
#define pagingHeaderHeight 60

@implementation XXHomeBaseTableView
//注意:下方的tableView是继承自XXHomeBaseTableView,至关重要,目的是判断是否让外层tableView的手势透传到子视图
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    //分页列表高度
    CGFloat naviBarHeight = NaviBarHeight;
    CGFloat tabBarHeight = TabBarHeight;
    CGFloat listHeight = SCREEN_HEIGHT - naviBarHeight - tabBarHeight - pagingHeaderHeight;
    CGPoint currentPoint = [gestureRecognizer locationInView:self];
    if (CGRectContainsPoint(CGRectMake(0, self.contentSize.height - listHeight, SCREEN_WIDTH, listHeight), currentPoint) ) {
        return YES;
    }
    return NO;
}

@end

1.2 主控制器主要代码:

#define sectionHeaderHeight 80.0
#define pagingHeaderHeight 60.0

@interface XXHomeViewController ()<UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) XXHomeBaseTableView *mainTableView;
@property (nonatomic, strong) XXHomeFooterView *footerView;
@property (nonatomic, assign) BOOL canScroll;
@property (nonatomic, assign) BOOL isTopIsCanNotMoveTabView;//到达顶部不能移动mainTableView
@property (nonatomic, assign) BOOL isTopIsCanNotMoveTabViewPre;//到达顶部不能移动子控制器的tableView

@end

@implementation XXHomeViewController
{
    CGFloat  _footerViewHeight; //底部猜你喜欢高度
    CGFloat  _listHeight;             //底部分页列表高度
}

- (void)viewDidLoad {
    [super viewDidLoad];
    //底部分页列表高度
    _listHeight = SCREEN_HEIGHT - self.naviBarHeight - self.tabBarHeight - pagingHeaderHeight;
    //底部猜你喜欢高度
    _footerViewHeight = sectionHeaderHeight + ReSize_UIHeight(300.0) + pagingHeaderHeight + _listHeight;
    //注册允许首页外层tableView滚动通知-解决和footerView的上下滑动冲突问题
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptMsgOfFooterView:) name:@"leaveTop" object:nil];
    //注册允许首页外层tableView滚动通知-解决子视图左右滑动和外层tableView上下滑动的冲突问题
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptMsgOfSubView:) name:IsScrollHomeVC object:nil];
}

//接收通知
- (void)acceptMsgOfFooterView:(NSNotification *)notification{
    NSDictionary *userInfo = notification.userInfo;
    NSString *canScroll = userInfo[@"canScroll"];
    if ([canScroll isEqualToString:@"1"]) {
        _canScroll = YES;
    }
}

//解决分页部分未达到临界点时,左右滑动和外层tableView上下滑动不能互斥的问题
- (void)acceptMsgOfSubView:(NSNotification *)notification{
    NSDictionary *userInfo = notification.userInfo;
    NSString *canScroll = userInfo[@"canScroll"];
    if ([canScroll isEqualToString:@"1"]) {
        _mainTableView.scrollEnabled = YES;
    }else if([canScroll isEqualToString:@"0"]) {
        _mainTableView.scrollEnabled = NO;
    }
}

#pragma mark 处理联动
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView == _mainTableView) {
        //当前偏移量
        CGFloat yOffset  = scrollView.contentOffset.y;
        //临界点偏移量
        CGFloat tabyOffset = scrollView.contentSize.height  - _listHeight  - pagingHeaderHeight - self.naviBarHeight;
        
        //更改状态栏的字体颜色
        if (yOffset >= tableHeaderViewHeight) {
            if (yOffset <= (scrollView.contentSize.height  - _footerViewHeight + pagingHeaderHeight)) {
                [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
            }else{
                [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
            }
        }else {
            [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
        }
        
        //滑动到一定高度后,更改底部按钮的显示
        if (yOffset > (SCREEN_HEIGHT - self.tabBarHeight)*3) {
            _isTop = NO;
            //超过三屏 首页按钮显示为@"返回顶部"
            [[NSNotificationCenter defaultCenter] postNotificationName:UpdateTabBarItem object:nil userInfo:@{@"status":@"1"}];
        }else {
            if (!_isTop) {
                _isTop = YES;
                //否则显示@"首页"
                [[NSNotificationCenter defaultCenter] postNotificationName:UpdateTabBarItem object:nil userInfo:@{@"status":@"0"}];
            }
        }
        
        //解决scrollView嵌套手势冲突问题
        if (yOffset >= tabyOffset) {
            //当分页视图滑动至导航栏时,禁止外层tableView滑动
            _mainTableView.contentOffset = CGPointMake(0, tabyOffset);
            _isTopIsCanNotMoveTabView = YES;
        }else{
            //当分页视图和顶部导航栏分离时,允许外层tableView滑动
            _isTopIsCanNotMoveTabView = NO;
        }
        
        //取反
        _isTopIsCanNotMoveTabViewPre = !_isTopIsCanNotMoveTabView;
        
        if (!_isTopIsCanNotMoveTabViewPre) {
            NSLog(@"滑动到顶端");
            _canScroll = NO;
            [[NSNotificationCenter defaultCenter] postNotificationName:@"goTop" object:nil userInfo:@{@"canScroll":@"1"}];
        }else {
            NSLog(@"滑动到底部后开始下拉页面");
            if (!_canScroll) {
                NSLog(@"让分页部分保持吸顶状态");
                _mainTableView.contentOffset = CGPointMake(0, tabyOffset);
            }
        }
    }
}

#pragma mark 懒加载
- (UITableView *)mainTableView {
    if (!_mainTableView) {
        //初始化最好在tableView创建之前设置(️:如果在tableView创建之后,设置了tableView的contentInset,比如你要头部headerView的放大效果,就会出问题,因为contentInset的设置会调用scrollViewDidScroll这个方法)
        _canScroll = YES;
        _isTopIsCanNotMoveTabView = NO;

        //创建tableView等代码省略...
    }
    return _mainTableView;
}

- (XXHomeFooterView *)footerView {
    if (!_footerView) {
        CGFloat listHeight = SCREEN_HEIGHT - self.naviBarHeight - self.tabBarHeight - pagingHeaderHeight;
        CGFloat height = sectionHeaderHeight + ReSize_UIHeight(300.0) + pagingHeaderHeight + listHeight; //标题区域的高度 + 轮播图高度 + 按钮区域高度 + 列表高度
        _footerView = [[XXHomeFooterView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, height)];
        _footerView.superController = self;
    }
    return _footerView;
}

2.中间层 - footerView,包含中层scrollView(collectionView的容器)和collectionView的创建
为了你们更清晰的看到footerView的具体构建,这里贴出footerView的全部代码,关注内层collectionView和外层tableView滑动手势冲突的小伙伴可移至第三部分

footerView
scrollView禁用上下滑动

相关代码:

#import "XXHomeFooterView.h"
#import "YSCommodityDetailsVC.h"
#import "XXHomeGuessLikeListView.h"
#import "XXHomeBaseModel.h"
#import "XXHomeViewController.h"

#define btnWidth 48
#define btnHeight 40
#define pagingHeaderHeight 60
#define carouselViewHeight ReSize_UIHeight(300.0)

@interface XXHomeFooterView () <SDCycleScrollViewDelegate, UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIView *carouselBgView;          //轮播图背景
@property (weak, nonatomic) IBOutlet UIView *btnBgView;                  //按钮背景
@property (weak, nonatomic) IBOutlet UIScrollView *listBgScrollView; //商品列表背景

@property (nonatomic, strong) SDCycleScrollView *carouselView; //轮播图
@property (nonatomic, strong) NSArray *btnTitleArr; //按钮title
@property (nonatomic, strong) UIView *line; //btn下滑线
@property (nonatomic, strong) UIButton *lastSelectBtn; //上次选中的btn

//数据
@property (nonatomic,   copy) NSArray *carouselDataArr; //轮播图数据

@end


@implementation XXHomeFooterView
{
    CGFloat  _listHeight;
    CGFloat  _naviBarHeight;
    CGFloat  _tabBarHeight;
}

- (instancetype)initWithFrame:(CGRect)frame superVC:(XXHomeViewController *)superVC {
    if (self = [super initWithFrame:frame]) {
       self = [[[NSBundle mainBundle] loadNibNamed:@"XXHomeFooterView" owner:self options:nil] lastObject];
       self.frame = frame;
        _naviBarHeight = NaviBarHeight;
        _tabBarHeight = TabBarHeight;
        _listHeight = SCREEN_HEIGHT - _naviBarHeight - _tabBarHeight - pagingHeaderHeight;
       self.superController = superVC;
       //注册通知-用于标签栏通知其返回顶部
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backTop) name:BackTopHomeFooterView object:nil];
       [self createUI];
       self.listBgScrollView.delegate = self;
    }
    return self;
}

- (NSArray *)btnTitleArr {
    if (!_btnTitleArr) {
        _btnTitleArr = @[@"包袋", @"礼服", @"旅行"];
    }
    return _btnTitleArr;
}

- (void)createUI {
    //轮播图
    self.carouselView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 0, self.width, ReSize_UIHeight(self.carouselBgView.height)) delegate:self placeholderImage:[UIImage imageNamed:@"XXHome_swiper_botttom"]];
    [self.carouselBgView addSubview:self.carouselView];
    
    //中间按钮区域
    for (NSInteger i = 0; i < self.btnTitleArr.count; i++) {
        //创建按钮
        UIButton * btn = [UIButton buttonWithType:(UIButtonTypeCustom)];
        btn.backgroundColor = [UIColor clearColor];
        [btn setTitle:_btnTitleArr[i] forState:(UIControlStateNormal)];
        btn.titleLabel.font = NFont(13);
        [btn setTitleColor:[UIColor blackColor] forState:(UIControlStateSelected)];
        [btn setTitleColor:[UIColor colorWithHexString:@"#9C9C9C"] forState:(UIControlStateNormal)];
        [btn addTarget:self action:@selector(btnClickAction:) forControlEvents:(UIControlEventTouchUpInside)];
        btn.tag = 100 + i;
        [self.btnBgView addSubview:btn];
        
        if (i == 0) {
            [btn mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.offset(5);
                make.top.offset(10);
                make.width.offset(btnWidth);
                make.height.offset(btnHeight);
            }];
            //初始选中
            btn.selected = YES;
            _lastSelectBtn = btn;
        }else if (i == 1) {
            [btn mas_makeConstraints:^(MASConstraintMaker *make) {
                make.centerX.offset(0);
                make.top.offset(10);
                make.width.offset(btnWidth);
                make.height.offset(btnHeight);
            }];
        }else {
            [btn mas_makeConstraints:^(MASConstraintMaker *make) {
                make.right.offset(-5);
                make.top.offset(10);
                make.width.offset(btnWidth);
                make.height.offset(btnHeight);
            }];
        }
    }
    //下划线
    _line = [UIView new];
    _line.backgroundColor = [UIColor blackColor];
    [self.btnBgView addSubview:_line];
    [_line mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.offset(15);
        make.top.offset(btnHeight + 5);
        make.width.offset(btnWidth - 20);
        make.height.offset(2);
    }];
    
    //商品列表页
    _listBgScrollView.contentSize = CGSizeMake(self.btnTitleArr.count * self.width, 0);
    for (NSInteger i = 0; i < self.btnTitleArr.count ; i++) {
        XXHomeGuessLikeListView *pageView = [[XXHomeGuessLikeListView alloc] init];
        pageView.tag = 200+i;
        pageView.currentIndex = i;
        pageView.superController = self.superController;
        [_listBgScrollView addSubview:pageView];
        [pageView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.offset(i*self.width);
            make.top.offset(0);
            make.width.offset(self.width);
            make.height.offset(_listHeight);
        }];

        //item点击的回调
        __weak typeof(self) weakSelf = self;
        pageView.didSelectItemBlock = ^(IOShopListModel *model) {
            //商品详情
            YSCommodityDetailsVC *shopDetail = [[YSCommodityDetailsVC alloc] init];
            shopDetail.shopDetailID = [NSString stringWithFormat:@"%li",model.shopId];
            shopDetail.ppName = @"商品详情";
            [weakSelf.superController.navigationController pushViewController:shopDetail animated:YES];
        };
    }
}


#pragma mark 更新数据
- (void)updateData:(NSDictionary *)dataDic {
    //轮播图
    _carouselDataArr = [dataDic[@"swiper"] copy];
    NSMutableArray *carouselImageArr = [NSMutableArray array];
    for (XXHomeBaseModel  *model in _carouselDataArr) {
        NSArray *imgArr = model.imgs;
        if (imgArr.count > 0) {
            [carouselImageArr addObject:imgArr[0][@"url"]];
        }
    }
    _carouselView.imageURLStringsGroup = carouselImageArr;
    
    //包袋、礼服、旅行生活
    //包袋
    XXHomeGuessLikeListView *pageView1 = [self viewWithTag:200];
    [pageView1 updateDataWithIds:dataDic[@"handbags"]];
    //礼服
    XXHomeGuessLikeListView *pageView2 = [self viewWithTag:201];
    [pageView2 updateDataWithIds:dataDic[@"fulldress"]];
    //旅行生活
    XXHomeGuessLikeListView *pageView3 = [self viewWithTag:202];
    [pageView3 updateDataWithIds:dataDic[@"travellife"]];
}

#pragma mark 按钮的点击事件
- (void)btnClickAction:(UIButton *)sender {
    //更新按钮状态
    [self updateBtnSAndPageViewStatusWithIndex:sender.tag-100];
}

//更新按钮的状态
- (void)updateBtnSAndPageViewStatusWithIndex:(NSInteger)index {
    //更新按钮下标
    //获取当前btn
    UIButton *sender = [self viewWithTag:index+100];
    //先改变上一次选中button的字体大小和状态(颜色)
    _lastSelectBtn.selected = NO;
    //再改变当前选中button的字体大小和状态(颜色)
    sender.selected = YES;
    //移动下划线
    [UIView animateWithDuration:0.15 animations:^{
        CGPoint point = _line.center;
        point.x = sender.center.x;
        _line.center = point;
    }];
    //更新_lastSelectBtn
    _lastSelectBtn = sender;
    
    //更新列表页下标
    [_listBgScrollView setContentOffset:CGPointMake(index * _listBgScrollView.width, 0) animated:YES];
}

//增加分页视图左右滑动和外界tableView上下滑动互斥处理
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [[NSNotificationCenter defaultCenter] postNotificationName:IsScrollHomeVC object:nil userInfo:@{@"canScroll":@"0"}];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    [[NSNotificationCenter defaultCenter] postNotificationName:IsScrollHomeVC object:nil userInfo:@{@"canScroll":@"1"}];
}

// 列表页结束滑动
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    CGFloat offetX = scrollView.contentOffset.x;
    NSInteger index = (NSInteger)offetX/scrollView.width;
    [self updateBtnSAndPageViewStatusWithIndex:index];
}

#pragma mark SDCycleScrollViewDelegate
/** 点击图片回调 */
- (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didSelectItemAtIndex:(NSInteger)index {

}

@end

  1. 内层collectionView的手势处理
#import <UIKit/UIKit.h>
#import "IOShopListModel.h"

@interface XXHomeGuessLikeListView : UIView

@property (nonatomic, copy) NSArray *dataArray;
@property (nonatomic, copy) void (^didSelectItemBlock)(IOShopListModel *model); //item点击事件的回调

//更新数据
- (void)updateDataWithIds:(NSArray *)ids;

@end
#import "XXHomeGuessLikeListView.h"
#import "ShopCollectionViewCell.h"
#import "BanLiYearCardVC.h"

@interface XXHomeGuessLikeListView () <UIScrollViewDelegate>

@property (weak,  nonatomic) IBOutlet UICollectionView *collectionView;
@property (nonatomic,   copy) NSArray *idArray;
@property (nonatomic,   copy) NSArray *commodityListArr;
@property (nonatomic, strong) UIScrollView * scrollView;
@property (nonatomic, assign) BOOL canScroll;

@end


@implementation XXHomeGuessLikeListView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        self = [[[NSBundle mainBundle] loadNibNamed:@"XXHomeGuessLikeListView" owner:self options:nil] lastObject];
        self.frame = frame;
    }
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    [_collectionView registerClass:[ShopCollectionViewCell class] forCellWithReuseIdentifier:@"ShopCollectionViewCell"];
    //子控制器视图到达顶部的通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptMsg:) name:@"goTop" object:nil];
    //子控制器视图离开顶部的通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptMsg:) name:@"leaveTop" object:nil];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

//接收信息,处理通知
- (void)acceptMsg:(NSNotification *)notification {
    NSString *notificationName = notification.name;
    if ([notificationName isEqualToString:@"goTop"]) {
        NSDictionary *userInfo = notification.userInfo;
        NSString *canScroll = userInfo[@"canScroll"];
        if ([canScroll isEqualToString:@"1"]) {
            _canScroll = YES;
            _collectionView.showsVerticalScrollIndicator = YES;
        }
    }else if([notificationName isEqualToString:@"leaveTop"]){
        _canScroll = NO;
        _collectionView.contentOffset = CGPointZero;
        _collectionView.showsVerticalScrollIndicator = NO;
    }
}

//更新数据
- (void)updateDataWithIds:(NSArray *)ids {
    _dataArray = ids;
    //请求商品列表
    if (_dataArray.count > 0) {
        //将id数组转成字符串
        NSString *idStrs = [_dataArray componentsJoinedByString:@","];
        NSDictionary *dic = @{@"ids":idStrs};
        [YQHttpRequest getData:dic url:@"/commodity/guessLikeCommodityList" success:^(id responseDic) {
            if ([responseDic isKindOfClass:[NSArray class]]) {
                NSArray *dataArr = [NSArray modelArrayWithClass:IOShopListModel.class json:responseDic];
                if (dataArr.count > 0) {
                    _commodityListArr = dataArr;
                    [_collectionView reloadData];
                }
            }else{
                [MBProgressHUD showError:@"请求列表失败"];
            }
        } fail:^(NSError *error) {
            if (error) {
                [MBProgressHUD showError:@"请求列表失败"];
            }
        }];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView == _collectionView) {
        if (!self.canScroll) {
            [scrollView setContentOffset:CGPointZero];
        }
        CGFloat offsetY = scrollView.contentOffset.y;
        if (offsetY <= 0) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"leaveTop" object:nil userInfo:@{@"canScroll":@"1"}];
        }
    }
}


#pragma mark CollectionView Delegate
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.commodityListArr.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    ShopCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ShopCollectionViewCell" forIndexPath:indexPath];
    if (self.commodityListArr.count > 0) {
        cell.model = _commodityListArr[indexPath.row];
    }
    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    if (self.didSelectItemBlock) {
        IOShopListModel *model = _commodityListArr[indexPath.row];
        self.didSelectItemBlock(model);
    }
}

#pragma mark FlowLayoutDelegate
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return CGSizeMake((self.width-10)/2.0, 316.0);
}

@end

补充:
  1. 由于该项目下方分页的都是一样的类型collectionView,并且布局也一样,所以我这里使用了在scrollView上添加collectionView,并没有产出冗余的代码,如果你的业务逻辑稍复杂,可采用子控制器替换直接添加collectionView的方式,你也可以看我整理的 Demo,其中使用的就是添加子控制器的方式;

  2. 之前我也写过相似的demo,效果是头部放大,下面分页部分上滑吸顶,也是采用上面这种方式解决滑动冲突,不过还有不同的是里面的分页部分我采用的是子控制器的方式,里面做了很详细的注释;
    笔 记
    Demo

  3. 除了改变contentOffset这种方式,还有没有其他方式可以解决scrollView嵌套手势冲突问题呢?
    (1) 没故事的卓同学 - 嵌套UIScrollview的滑动冲突解决方案
    (2) 军_andy - iOS 嵌套UIScrollview的滑动冲突另一种解决方案
    上面第一种我试过,但是有瑕疵, 第二种加入了动画,但是集成稍显麻烦,下面这两种是飞羽田海的推荐,很不错,大家可以参考一下:
    (3) 腾讯开源框架-特斯拉组件" 体验比较好 内有OC和Swift版本,
    地址: https://github.com/xichen744/SPPage
    (4) 这个开源库很不错,目前star数1400多,一直在维护,
    地址: https://github.com/Roylee-ML/SwipeTableView

亲们喜欢的话,请给个 ❤️ 额,谢谢亲们的支持!!!
上一篇下一篇

猜你喜欢

热点阅读