[iOS][Objective-C]:collectionView联动效果实现

这是一个demo:左右滑动与上部分类标签栏的联动效果,同时可以点击将分类标签栏打开明细进行选择。

效果:


collectionView联动.gif

首先在底层创建一个大的collectionView,嵌套于一个tabBar和navigationBar中。
然后创建对应的cell作为整个页面的容器。

DQCollectionViewController
DQCollectionViewCell

对应该collectionView的数据源方法,我们这边就先设置8个页面。也就是对应上方分类栏有8个标签。
DQCollectionViewController的设置:

  • (void)loadView {
    [super loadView];
    //设置高斯模糊:关闭
    self.navigationController.navigationBar.translucent = NO;
    self.tabBarController.tabBar.translucent = NO;
    //设置颜色
    self.collectionView.backgroundColor = [UIColor blueColor];
    self.navigationController.navigationBar.barTintColor = [UIColor cyanColor];
    self.tabBarController.tabBar.barTintColor = [UIColor cyanColor];
    //分页效果
    self.collectionView.pagingEnabled = YES;

    self.title = @"Demo";
    }

上部联动的分类的CollectionView和对应的cell设置


//
// CategroyBaseView.m
// CategroyCollectionView
//
// Created by admin on 16/8/29.
// Copyright © 2016年 Derrick_Qin. All rights reserved.
//

import "CategroyBaseView.h"

import "CategroyCell.h"

//下标高度

define kIndicatorH 3

//选项cell宽度

define kItemSizeW 55

@interface CategroyBaseView () UICollectionViewDelegate>

@property(nonatomic, assign) BOOL btnIsClicked;
@property(nonatomic, weak) UIView *indictorView;
@property(nonatomic, weak) UICollectionView *cateView;
@property(nonatomic, strong) NSIndexPath *selectedIndexPath;

@end

static NSString *cateCellReuseId = @"cateCell";

@implementation CategroyBaseView

  • (instancetype)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];

    if (self) {

    self.backgroundColor = [UIColor whiteColor];

    [self setupBaseView:frame];

    [self setupButton:frame];

    [self setupCollectionView:frame];

    [self setupIndictor:frame];
    }

    return self;
    }

/**

  • 添加底层View
  • @param frame
    */
  • (void)setupBaseView:(CGRect)frame {

    UIView *baseView = [[UIView alloc]
    initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];

    UILabel *textLabel = [[UILabel alloc]
    initWithFrame:CGRectMake(25, frame.size.height * 0.5 - 12.5, 120, 25)];

    textLabel.text = @"All Categroies";

    textLabel.font = [UIFont systemFontOfSize:13];

    textLabel.textColor = [UIColor darkGrayColor];

    [baseView addSubview:textLabel];

    baseView.backgroundColor =
    [UIColor colorWithRed:0.9444 green:0.9444 blue:0.9444 alpha:1.0];

    [self addSubview:baseView];
    }
    /**

  • 添加下拉按钮
  • @param frame
    */
  • (void)setupButton:(CGRect)frame {

    UIButton *changeBtn = [[UIButton alloc]
    initWithFrame:CGRectMake(frame.size.width - frame.size.height, 0,
    frame.size.height, frame.size.height)];

    UIImage *image = [UIImage imageNamed:@"explore_board_indicator"];

    UIImageView *btnImageView = [[UIImageView alloc] initWithImage:image];

    btnImageView.frame = CGRectMake(0, 0, changeBtn.bounds.size.width * 0.25,
    changeBtn.bounds.size.height * 0.25);

    btnImageView.center = CGPointMake(changeBtn.bounds.size.width * 0.5,
    changeBtn.bounds.size.height * 0.5);

    btnImageView.contentMode = UIViewContentModeScaleAspectFit;

    [changeBtn setBackgroundColor:[UIColor whiteColor]];

    _changeButton = changeBtn;

    [changeBtn addSubview:btnImageView];
    //添加点击触发事件
    [changeBtn addTarget:self
    action:@selector(changeBtnClick:)
    forControlEvents:UIControlEventTouchUpInside];

    [self addSubview:changeBtn];
    }

/**

  • 添加collectionView
  • @param frame
    */
  • (void)setupCollectionView:(CGRect)frame {

    UICollectionViewFlowLayout *flowLayout =
    [[UICollectionViewFlowLayout alloc] init];

    flowLayout.itemSize = CGSizeMake(kItemSizeW, frame.size.height - kIndicatorH);

    flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);

    flowLayout.minimumLineSpacing = 0;

    flowLayout.minimumInteritemSpacing = 0;

    flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;

    UICollectionView *cateView = [[UICollectionView alloc]
    initWithFrame:CGRectMake(0, 0,
    frame.size.width - frame.size.height,
    frame.size.height)
    collectionViewLayout:flowLayout];

    cateView.backgroundColor = [UIColor whiteColor];

    cateView.showsVerticalScrollIndicator = NO;

    cateView.showsHorizontalScrollIndicator = NO;

    cateView.bounces = NO;

    cateView.dataSource = self;

    cateView.delegate = self;

    _cateView = cateView;

    [self addSubview:cateView];

    [cateView registerClass:[CategroyCell class]
    forCellWithReuseIdentifier:cateCellReuseId];
    }

  • (void)setupIndictor:(CGRect)frame {

    UIView *indictorView = [[UIView alloc]
    initWithFrame:CGRectMake(0, frame.size.height - kIndicatorH, kItemSizeW,
    kIndicatorH)];

    indictorView.backgroundColor =
    [UIColor colorWithRed:1.0 green:0.0786 blue:0.2518 alpha:1.0];

    _indictorView = indictorView;

    [_cateView addSubview:indictorView];
    }

/**

  • 按钮触发事件
  • @param changeBtn
    */
  • (void)changeBtnClick:(UIButton *)changeBtn {
    //记录按钮状态
    _btnIsClicked = !_btnIsClicked;

    UIImageView *imageView = _changeButton.subviews.lastObject;
    //旋转
    imageView.transform = _btnIsClicked ? CGAffineTransformMakeRotation(M_PI)
    : CGAffineTransformMakeRotation(0);

    if (_btnIsClicked) {

    _cateView.hidden = YES;

    [_changeButton setBackgroundColor:[UIColor colorWithRed:0.9444
    green:0.9444
    blue:0.9444
    alpha:1.0]];

    } else {
    [_changeButton setBackgroundColor:[UIColor whiteColor]];

    _cateView.hidden = NO;
    }

    if (_changeBtnClick) {

    _changeBtnClick(_changeButton, _btnIsClicked, _selectedIndexPath);
    }
    }

  • (NSInteger)numberOfSectionsInCollectionView:
    (UICollectionView *)collectionView {

    return 1;
    }

  • (NSInteger)collectionView:(UICollectionView *)collectionView
    numberOfItemsInSection:(NSInteger)section {

    return self.categroyArray.count;
    }

  • (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
    cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    CategroyCell *cell =
    [collectionView dequeueReusableCellWithReuseIdentifier:cateCellReuseId
    forIndexPath:indexPath];

    NSString *title = self.categroyArray[indexPath.item];

    cell.title = title;

    if (indexPath.item == _selectedIndexPath.item) {

    cell.isSelected = YES;

    } else {
    cell.isSelected = NO;
    }

    return cell;
    }

  • (void)collectionView:(UICollectionView *)collectionView
    didSelectItemAtIndexPath:(NSIndexPath *)indexPath {

    CategroyCell *selectedCell = (CategroyCell *)[collectionView
    cellForItemAtIndexPath:_selectedIndexPath];

    selectedCell.isSelected = NO;

    CategroyCell *cell =
    (CategroyCell *)[collectionView cellForItemAtIndexPath:indexPath];

    cell.isSelected = YES;

    _selectedIndexPath = indexPath;

    [UIView animateWithDuration:0.2
    animations:^{

                   CGRect currentRect = _indictorView.frame;
    
                   currentRect.origin.x = kItemSizeW * indexPath.item;
    
                   _indictorView.frame = currentRect;
                 }];
    

    if (_indexChange) {

    _indexChange(indexPath.item);
    }

    [_cateView
    scrollToItemAtIndexPath:indexPath
    atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
    animated:YES];
    }

  • (void)selectAtIndex:(NSInteger)index {

    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];

    [self collectionView:_cateView didSelectItemAtIndexPath:indexPath];
    }

@end



//
// CategroyCell.m
// CategroyCollectionView
//
// Created by admin on 16/8/29.
// Copyright © 2016年 Derrick_Qin. All rights reserved.
//

import "CategroyCell.h"

@interface CategroyCell ()
@property(nonatomic, weak) UILabel *textLabel;
@end

@implementation CategroyCell

  • (instancetype)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];

    if (self) {

    UILabel *textLabel =
    [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 60, 25)];

    textLabel.textAlignment = NSTextAlignmentCenter;

    textLabel.center =
    CGPointMake(frame.size.width * 0.5, frame.size.height * 0.5);

    _textLabel = textLabel;

    [self.contentView addSubview:textLabel];
    }

    return self;
    }

  • (void)setTitle:(NSString *)title {

    _title = title;

    _textLabel.text = title;
    }

@end


实现上下联动的关键点:

  1. 上方分类栏点击,下方跟随滑动
    在collectionViewdidSelectItemAtIndexPath中实现点击方法:
    通过indexpath获取cell,底下红色条做动画,做block回调,然后使用scrollToItemAtIndexPath:atScrollPosition: 方法做分类栏的滑动跳转
  • (void)collectionView:(UICollectionView *)collectionView
    didSelectItemAtIndexPath:(NSIndexPath *)indexPath {

    CategroyCell *selectedCell = (CategroyCell *)[collectionView
    cellForItemAtIndexPath:_selectedIndexPath];

    selectedCell.isSelected = NO;

    CategroyCell *cell =
    (CategroyCell *)[collectionView cellForItemAtIndexPath:indexPath];

    cell.isSelected = YES;

    _selectedIndexPath = indexPath;

    [UIView animateWithDuration:0.2
    animations:^{

                   CGRect currentRect = _indictorView.frame;
    
                   currentRect.origin.x = kItemSizeW * indexPath.item;
    
                   _indictorView.frame = currentRect;
                 }];
    

    if (_indexChange) {

    _indexChange(indexPath.item);
    }

    [_cateView
    scrollToItemAtIndexPath:indexPath
    atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
    animated:YES];
    }

在DQCollectionViewController中添加block回调的实现,同样是scrollView的滑动方法:


cateView.indexChange = ^(NSInteger itemIndex) {

NSIndexPath *indexPath =
    [NSIndexPath indexPathForItem:itemIndex inSection:0];

//记得动画效果去掉
[self.collectionView
    scrollToItemAtIndexPath:indexPath
           atScrollPosition:UICollectionViewScrollPositionLeft
                   animated:NO];

};

  1. 下方collectionView滑动同时联动上方的分类栏联动:
    这个相对比较简单,正向调用。重点是collectionView中的
  • (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 方法的实现,
    在滑动松手结束至停止时,带动上方做联动:

  • (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

    //根据当前 contentoffset获得index

    CGFloat width = scrollView.contentSize.width / self.categroyArray.count;

    CGFloat offsetX = scrollView.contentOffset.x;

    NSInteger index = offsetX / width;
    //选择cateview的index
    [_cateView selectAtIndex:index];
    }

调用selectAtIndex:,该方法调用didSelectItemAtIndexPath:方法进行实现

  • (void)selectAtIndex:(NSInteger)index {

    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0];

    [self collectionView:_cateView didSelectItemAtIndexPath:indexPath];
    }

添加一个detailView:

//
// DetailCategroyBaseView.m
// CategroyCollectionView
//
// Created by admin on 16/8/29.
// Copyright © 2016年 Derrick_Qin. All rights reserved.
//

import "CategroyCell.h"

import "DetailCategroyBaseView.h"

import "DetailCell.h"

@interface DetailCategroyBaseView () UICollectionViewDataSource>
@property(nonatomic, weak) UICollectionView *detailCateView;
@property(nonatomic, strong) NSIndexPath *selectedIndexPath;
@end

static NSString *detailCateCellReuseId = @"detailCateCell";

@implementation DetailCategroyBaseView

  • (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {

    [self setupCollectionViewWithFrame:frame];
    }
    return self;
    }

  • (void)setupCollectionViewWithFrame:(CGRect)frame {
    UICollectionViewFlowLayout *flowLayout =
    [[UICollectionViewFlowLayout alloc] init];

    flowLayout.itemSize =
    CGSizeMake(frame.size.width / (8 * 0.5), frame.size.height / 2);

    flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);

    flowLayout.minimumLineSpacing = 0;

    flowLayout.minimumInteritemSpacing = 0;

    UICollectionView *detailCateView =
    [[UICollectionView alloc] initWithFrame:frame
    collectionViewLayout:flowLayout];

    detailCateView.backgroundColor = [UIColor whiteColor];

    detailCateView.showsVerticalScrollIndicator = NO;

    detailCateView.showsHorizontalScrollIndicator = NO;

    detailCateView.bounces = NO;

    detailCateView.dataSource = self;

    detailCateView.delegate = self;

    _detailCateView = detailCateView;

    [self addSubview:detailCateView];

    [detailCateView registerClass:[DetailCell class]
    forCellWithReuseIdentifier:detailCateCellReuseId];
    }

  • (void)selectAtIndex:(NSIndexPath *)indexPath {

    _selectedIndexPath = indexPath;
    }

pragma mark--数据源方法

  • (NSInteger)numberOfSectionsInCollectionView:
    (UICollectionView *)collectionView {
    return 1;
    }

  • (NSInteger)collectionView:(UICollectionView *)collectionView
    numberOfItemsInSection:(NSInteger)section {

    return self.categroyArray.count;
    }

  • (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
    cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    DetailCell *cell = [collectionView
    dequeueReusableCellWithReuseIdentifier:detailCateCellReuseId
    forIndexPath:indexPath];

    NSString *title = self.categroyArray[indexPath.item];

    cell.title = title;

    if (indexPath.item == _selectedIndexPath.item) {

    cell.isSelected = YES;

    } else {
    cell.isSelected = NO;
    }

    return cell;
    }

  • (void)collectionView:(UICollectionView *)collectionView
    didSelectItemAtIndexPath:(NSIndexPath *)indexPath {

    CategroyCell *selectedCell = (CategroyCell *)[collectionView
    cellForItemAtIndexPath:_selectedIndexPath];

    selectedCell.isSelected = NO;

    CategroyCell *cell =
    (CategroyCell *)[collectionView cellForItemAtIndexPath:indexPath];

    cell.isSelected = YES;

    _selectedIndexPath = indexPath;

    if ([self.delegate respondsToSelector:@selector(indexChange:andIndex:)]) {

    [self.delegate indexChange:self andIndex:indexPath.item];
    }
    }

@end

点击下拉按钮实现展现/隐藏明细分类栏:
弹出明细分类栏的时候需要给北京设置一个蒙板

cateView.changeBtnClick = ^(UIButton *changeBtn, BOOL btnIsSelected,
NSIndexPath *indexPath) {

if (btnIsSelected) {

  UIButton *coverView = [[UIButton alloc]
      initWithFrame:CGRectMake(
                        0, 40, [UIScreen mainScreen].bounds.size.width,
                        [UIScreen mainScreen].bounds.size.height - 40)];

  [coverView setBackgroundColor:[UIColor blackColor]];
  coverView.alpha = 0.45;
  [coverView addTarget:self
                action:@selector(cancelDetailView)
      forControlEvents:UIControlEventTouchUpInside];
  _coverView = coverView;
  [self.view addSubview:coverView];

  DetailCategroyBaseView *detailView = [[DetailCategroyBaseView alloc]
      initWithFrame:CGRectMake(
                        0, 0, [UIScreen mainScreen].bounds.size.width, 80)];

  detailView.categroyArray = self.categroyArray;
  detailView.delegate = self;

  [self.view addSubview:detailView];

  _detailView = detailView;

  [detailView selectAtIndex:indexPath];

  [self.view bringSubviewToFront:_cateView];

  [UIView
      animateWithDuration:0.2
               animations:^{
                 _detailView.frame = CGRectMake(
                     0, 40, [UIScreen mainScreen].bounds.size.width, 80);
               }];
} else {

  [_coverView removeFromSuperview];

  _coverView = nil;

  [self.view bringSubviewToFront:_cateView];

  [UIView animateWithDuration:0.2
      animations:^{
        _detailView.frame =
            CGRectMake(0, -40, [UIScreen mainScreen].bounds.size.width, 80);

      }
      completion:^(BOOL finished) {

        [_detailView removeFromSuperview];

        _detailView = nil;

      }];
}

};

[self.view addSubview:cateView];
}

点击明细分类栏的cell触发事件:
这边不适用block回调,使用代理的方式做到回传

  • (void)collectionView:(UICollectionView *)collectionView
    didSelectItemAtIndexPath:(NSIndexPath *)indexPath {

    CategroyCell *selectedCell = (CategroyCell *)[collectionView
    cellForItemAtIndexPath:_selectedIndexPath];

    selectedCell.isSelected = NO;

    CategroyCell *cell =
    (CategroyCell *)[collectionView cellForItemAtIndexPath:indexPath];

    cell.isSelected = YES;

    _selectedIndexPath = indexPath;

    if ([self.delegate respondsToSelector:@selector(indexChange:andIndex:)]) {

    [self.delegate indexChange:self andIndex:indexPath.item];
    }
    }

代理的协议方法的实现:

  • (void)indexChange:(DetailCategroyBaseView *)detailCategroyBaseView
    andIndex:(NSInteger)itemIndex {

    [self.view bringSubviewToFront:_cateView];

    [UIView animateWithDuration:0.2
    animations:^{

      _detailView.transform = CGAffineTransformMakeTranslation(0, 0);
    
    }
    completion:^(BOOL finished) {
    
      [_detailView removeFromSuperview];
    
    }];
    

    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:itemIndex inSection:0];

    //记得动画效果去掉
    [self.collectionView
    scrollToItemAtIndexPath:indexPath
    atScrollPosition:UICollectionViewScrollPositionLeft
    animated:NO];

    [_cateView selectAtIndex:itemIndex];

    [_cateView changeBtnClick:nil];
    }

具体的一些细节可以参考代码:
https://github.com/DerrickQin2853/CategoryCollectionViewDemo

感谢阅读

你可能感兴趣的:([iOS][Objective-C]:collectionView联动效果实现)