美文网首页
collectionview的高级应用

collectionview的高级应用

作者: 目前运行时 | 来源:发表于2018-04-08 17:51 被阅读0次

1.collectionview的用法很强大,我们平时用到的地方很多,但是大多数都是简单的使用,下面我来说一下复杂的用法,我也是从别的地方找来的 但是我弄明白了所以分享给大家 如果有什么不对的地方请给予指正 我将不胜感激!

  • collectionView的创建

    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 100, self.view.bounds.size.width, 200) collectionViewLayout:[[UICollectionViewFlowLayout alloc]init]];
    collectionView.delegate = self;
    collectionView.dataSource = self;
    [collectionView registerNib:[UINib nibWithNibName:@"LDGItemCell" bundle:nil] forCellWithReuseIdentifier:ID];
    [self.view addSubview:collectionView];
    // 创建数据
    for (NSInteger index = 1; index <= 20; index ++) {
        NSString *str = [NSString stringWithFormat:@"%zd",index];
        [self.dataArray addObject:str];
    }
    self.collectionView.backgroundColor = [UIColor redColor];
    self.collectionView = collectionView;
    [self.collectionView reloadData];
  • 其实我个人觉得tableview 有时候和collectionview差不多 ,唯一最大的区别就是UICollectionViewLayout,就是这个UICollectionViewLayout 我们可以干很多的事情,下面我们来做一个这样的效果


    1.png

    这样的效果在优酷等等的视频软件上都会用到下面是具体的代码

#import "LDGlineFlowLayout.h"

@implementation LDGlineFlowLayout

-(void)prepareLayout {
    [super prepareLayout];
    
    self.itemSize = CGSizeMake(100, 100);
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.minimumLineSpacing = 100;
    self.sectionInset = UIEdgeInsetsMake(0, (self.collectionView.frame.size.width - self.itemSize.width)*0.5, 0, (self.collectionView.frame.size.width - self.itemSize.width)*0.5);
}

/**
 这个方法是用来判断每次属性变化的时候都会返回

 @param newBounds 新的位置变化
 @return 是否都会会回调用
 */
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
/**
 最终停留的地方

 @param proposedContentOffset 系统自动计算停留的地方
 @param velocity 速度
 @return 要停留的地方
 */
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{
    
    // 只需要计算当前与屏幕相交的即可 没有必要放大所有的
    CGRect currentRect;
    currentRect.size = self.collectionView.frame.size;
    currentRect.origin = self.collectionView.contentOffset;
    NSArray *array = [self layoutAttributesForElementsInRect:currentRect];
     // 屏幕的中心点
    CGFloat screenCenterX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;
    // 计算最小值
    CGFloat minF = MAXFLOAT;
    CGFloat adjustF = 0;
    for (UICollectionViewLayoutAttributes *attrs in array) {
        if (ABS(attrs.center.x - screenCenterX) <= minF) {
            adjustF = attrs.center.x - screenCenterX;
        }
    }
    return CGPointMake(proposedContentOffset.x + adjustF, proposedContentOffset.y);
}
/**
 这个方法是间距变化返回每一个attr的数组

 @param rect rect
 @return 属性的数组
 */
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    
    NSArray *array = [super layoutAttributesForElementsInRect:rect];
    // 只需要计算当前与屏幕相交的即可 没有必要放大所有的
    CGRect currentRect;
    currentRect.size = self.collectionView.frame.size;
    currentRect.origin = self.collectionView.contentOffset;
    // 拿到屏幕的中心点
    CGFloat screenCenterX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;
    for (UICollectionViewLayoutAttributes *attrs in array) {
        if (!CGRectIntersectsRect(currentRect, attrs.frame)) {
            continue;
        }
        // 计算差值
        CGFloat differX = ABS(attrs.center.x - screenCenterX);
        // 计算需要放大的倍数
        CGFloat scale = 1 + 0.7*(1- differX/self.collectionView.frame.size.width);
        if (scale <= 1.3) {
            scale = 1;
        }
        attrs.transform = CGAffineTransformMakeScale(scale, scale);
    }
    return array;
}
@end

下面是具体的解释 当这个方法:-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}返回yes的时候 系统滑动或者边距发生改变的时候就会调用- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect 调用非常的频发 我们拿到里边的UICollectionViewLayoutAttributes 来改变item的大小 但是这样的话很耗性能 我们只需要改变当前我们看到的地方的UICollectionViewLayoutAttributes就可以了 所以我们判断屏幕UICollectionViewLayoutAttributes的frame是否相交。
我们有一个功能是:停在最后的位置 但是系统最后的位置是任意的 所以我们重写这个方法 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity 经过我们找到距离屏幕中心点距离最小的位置的UICollectionViewLayoutAttributes 最后偏移到那个位置去就OK了。

  • 第二个效果是做一个 这样的效果


    1.png
  • 具体实现的代码:
//
//  LDGCircleLayout.m
//  uicollectionView的联系
//
//  Created by 刘殿阁 on 2018/4/7.
//  Copyright © 2018年 刘殿阁. All rights reserved.
//

#import "LDGCircleLayout.h"

@implementation LDGCircleLayout

/**
 这个方法是用来判断每次属性变化的时候都会返回
 
 @param newBounds 新的位置变化
 @return 是否都会会回调用
 */
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
/**
 返回的是每一个属性
 
 @param indexPath indexPath
 @return 属性
 */
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    // 计算角度
    
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attrs.center = CGPointMake(150 - 100*cos(indexPath.item*(M_PI*2/count)),100 - 100*sin(indexPath.item*(M_PI*2/count)));
    attrs.size = CGSizeMake(50, 50);
    return attrs;
}
/**
 这个方法是间距变化返回每一个attr的数组
 
 @param rect rect
 @return 属性的数组
 */
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    
    NSMutableArray *dataArray = [NSMutableArray array];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (NSInteger index = 0; index < count; index++) {
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
        [dataArray addObject:attrs];
    }
    return dataArray;
}
@end
  • 具体的解释:
    这个LDGCircleLayout是继承的UICollectionViewLayout 而不是UICollectionViewFlowLayout因为 这个我们明显一看就不是流水布局,所以我们继承UICollectionViewLayout,这个就不具体解释了 很简单 需要实现这个- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath否则会报错澳
  • 下面是卡片的动画效果


    1.png

具体的代码实现如下:

#import "LDGStaticLayout.h"

@implementation LDGStaticLayout

/**
 这个方法是用来判断每次属性变化的时候都会返回
 
 @param newBounds 新的位置变化
 @return 是否都会会回调用
 */
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return YES;
}
/**
 返回的是每一个属性
 
 @param indexPath indexPath
 @return 属性
 */
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    NSArray *angleArray = @[@(0),@(-0.2),@(-0.8),@(0.2),@(0.8)];
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attrs.center = CGPointMake(150, 100);
    attrs.size = CGSizeMake(100, 100);
    if (indexPath.item >= 5) {
        attrs.hidden = YES;
    }else{
        attrs.hidden = NO;
        attrs.transform = CGAffineTransformMakeRotation([angleArray[indexPath.item] floatValue]);
    }
    attrs.zIndex = count - indexPath.item;
    return attrs;
}
/**
 这个方法是间距变化返回每一个attr的数组
 
 @param rect rect
 @return 属性的数组
 */
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    
    NSMutableArray *dataArray = [NSMutableArray array];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (NSInteger index = 0; index < count; index++) {
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
        [dataArray addObject:attrs];
    }
    return dataArray;
}
@end
  • 这个和圆环的差不多 实现原理 也是继承UICollectionViewLayout 因为流水布局办不到
  • 下面实现一个瀑布流,大致的样子如下


    1.png
  • 具体的实现代码:
LDGPullViewLayout.h文件

#import <UIKit/UIKit.h>
@protocol LDGPullViewLayoutDelegate <NSObject>
@optional
/**
 通过体格宽度来计算宽高比
 
 @param indexPath indexPath
 @return 返回宽高比
 */
- (CGFloat)receicewidthAndHeightScale:(NSIndexPath *)indexPath;
@end

@interface LDGPullViewLayout : UICollectionViewLayout
// 定义协议
@property (weak, nonatomic, nullable) id <LDGPullViewLayoutDelegate> layoutDelegate;
// 每一行之间的间距
@property (nonatomic, assign) CGFloat rowMargon;
// 每一列之间之间的间距
@property (nonatomic, assign) CGFloat columnMargon;
// 设置边距离
@property (nonatomic, assign) UIEdgeInsets layoutInset;
// 显示多少列
@property (nonatomic, assign) int columnCounts;
@end

LDGPullViewLayout.m文件实现

#import "LDGPullViewLayout.h"
@interface LDGPullViewLayout ()

@property (nonatomic, strong) NSMutableArray <UICollectionViewLayoutAttributes *>*attrArray;
@property (strong, nonatomic) NSMutableDictionary *minDic;

@end

@implementation LDGPullViewLayout
- (NSMutableArray *)attrArray {
    if (!_attrArray) {
        _attrArray = [NSMutableArray array];
    }
    return _attrArray;
}
-(NSMutableDictionary *)minDic {
    if (!_minDic) {
        _minDic = [[NSMutableDictionary alloc] init];
    }
    return _minDic;
}
-(instancetype)init {
    if (self = [super init]) {
        // 填写默认的行、列间距 默认的列数
        self.rowMargon = 10;
        self.columnMargon = 10;
        self.columnCounts = 2;
        self.layoutInset = UIEdgeInsetsMake(10, 10, 10, 10);
    }
    return self;
}
/**
 初始化的相关的操作 这里最准确
 */
- (void)prepareLayout {
    [super prepareLayout];
    // 给最小值的字典设置值
    for (NSInteger index = 0; index < self.columnCounts; index ++) {
        self.minDic[@(index)] = @(self.layoutInset.top);
    }
    // 开始的时候初始化
    [self.attrArray removeAllObjects];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (NSInteger index = 0; index < count; index++) {
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
        [self.attrArray addObject:attrs];
    }
    
}

/**
 这个方法用来计算ContentSize的大小 最终能够滑动的

 @return size
 */
-(CGSize)collectionViewContentSize{
    
    __block NSNumber *maxNumber = [NSNumber numberWithInt:0];
    [self.minDic enumerateKeysAndObjectsUsingBlock:^(NSNumber*  _Nonnull key, NSNumber*  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([self.minDic[maxNumber] floatValue] < [obj floatValue]) {
            maxNumber = key;
        }
    }];
    return CGSizeMake(0, [self.minDic[maxNumber] floatValue] + self.layoutInset.bottom);
}
/**
 间距发生变化的时候改变

 @param newBounds newBounds
 @return 对象本身
 */
//-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
//    return YES;
//}
/**
 返回每一个的item的属性

 @param indexPath indexPath
 @return item的属性
 */
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    // 宽度和高度
    CGFloat attrW = (self.collectionView.frame.size.width - (self.layoutInset.left + self.layoutInset.right) - (self.columnCounts-1)*self.columnMargon)/self.columnCounts;
    CGFloat scale = 0;
    if ([self.layoutDelegate respondsToSelector:@selector(receicewidthAndHeightScale:)]) {
        scale = [self.layoutDelegate receicewidthAndHeightScale:indexPath];
    }
    CGFloat attrH = attrW * scale;
    
    // 假设最小值的那列是第0列,假设最小的列是第0列
    __block NSNumber *minColumn = [NSNumber numberWithInt:0];
    [self.minDic enumerateKeysAndObjectsUsingBlock:^(NSNumber *  _Nonnull key, NSNumber *  _Nonnull obj, BOOL * _Nonnull stop) {
        if ([self.minDic[minColumn] floatValue] > [obj floatValue]) {
            minColumn = key;
        }
    }];
    CGFloat attrX = [minColumn integerValue] * (attrW + self.columnMargon) + self.layoutInset.left;
    CGFloat attrY = [self.minDic[minColumn] floatValue] + self.rowMargon;
    attrs.frame = CGRectMake(attrX, attrY, attrW, attrH);
    self.minDic[minColumn] = @(CGRectGetMaxY(attrs.frame));
    return attrs;
}

/**
 间距发生变化就调用这个方法

 @param rect rect
 @return 数组
 */
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    return self.attrArray;
}
@end
  • 特别说明:

1.我们计算的UICollectionViewLayoutAttributes的宽度和高度不难,因为宽度是由我们的列数确定的 ,后台会告诉我们宽度和高度 我们计算宽和高的比列就能计算出宽和高。

  1. 难的是计算x、y值 说实话 我开始想了半天没有想出来一个好的办法,后来经过查询 我看人家是用一个字典来计算的 ,也就是有多少列字典中有多少个key ,每个key存储的是每列的最大y值,我们经过字典便利来计算最小y值的那一列 从而确定x 和 y值

3.这个layout不用重写这个方法-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds,因为我们在prepare方法里全部初始化了。那就没有必要重复调用- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect这个方法了

4.值得注意的是collectionview不能用initwithframe 来进行创建 因为他要求layout必须不能为空。

  1. 关于瀑布流这里有一片不错的文章 瀑布流
  • collectionview 的头部试图显示
    大致分为几步:
    1.创建基于UICollectionReusableView的cell
    2.返回大小
    3.实现UICollectionViewDelegateFlowLayout和UICollectionViewDelegate中的方法
    具体的代码为:
 JHHeaderFlowLayout *flowLayout = [[JHHeaderFlowLayout alloc] init];
    flowLayout.itemSize = CGSizeMake((SCREEN_WIDTH -45)/2.0 , (SCREEN_WIDTH -45)/2.0 + 50);
    flowLayout.minimumLineSpacing = 15;
    flowLayout.minimumInteritemSpacing = 10;
    self.collectionView.contentInset = UIEdgeInsetsMake(0, 15, 0, 15);
    [self.collectionView registerNib:[UINib nibWithNibName:@"MRMSongListCell" bundle:nil] forCellWithReuseIdentifier:reuseSongListCellIndentifier];
    [self.collectionView registerNib:[UINib nibWithNibName:@"MRMHeaderCollectionCell" bundle:nil] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerCell_ID];
    [self.collectionView setCollectionViewLayout:flowLayout];
#pragma mark - uicollectionFlowDelegate
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section{
    return CGSizeMake(SCREEN_WIDTH, 200);
}
#pragma mark - uicollectionview 的 delegate
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
    UICollectionReusableView *view = nil;
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
        MRMHeaderCollectionCell *headerCell = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerCell_ID forIndexPath:indexPath];
        return headerCell;
    }
    return view;
}

代码在(https://github.com/liudiange/practicePublic)中的

image.png
注意点为:
1.需要继承协议
image.png
2.我们在创建基于UICollectionReusableView的cell的时候 其中xcode有一个bug 就是你创建出来的cell (在有xib的前提下)不会主动继承你创建出来的类
image.png
需要你手动填写你自己的类,否则注册的时候说你的cell 不是你创建的那个类,我找了半天才找出来 可把我坑惨了 ,不信的话可以试试创建UITableViewCell勾选xib默认都是帮你继承你创建出来的类。
  1. 默认的情况下 collectionview的headerview是不会悬浮的,不想tableivew那样六,怎样办到悬浮 我在网上找了一个类


    image.png

    只要你的流水布局的layout继承这个类自动就会悬浮。
    他的具体代码为:

JHHeaderFlowLayout.h文件
@interface JHHeaderFlowLayout : UICollectionViewFlowLayout
//默认为64.0, default is 64.0
@property (nonatomic, assign) CGFloat naviHeight;
@end
JHHeaderFlowLayout.m文件
#import "JHHeaderFlowLayout.h"

@implementation JHHeaderFlowLayout
-(instancetype)init
{
    self = [super init];
    if (self)
    {
        _naviHeight = 0.0;
    }
    return self;
}
/*
 
 // 作用:返回指定区域的cell布局对象
 // 什么时候调用:指定新的区域的时候调用
 - (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect

 */
- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect
{
    //UICollectionViewLayoutAttributes:我称它为collectionView中的item(包括cell和header、footer这些)的《结构信息》
    //截取到父类所返回的数组(里面放的是当前屏幕所能展示的item的结构信息),并转化成不可变数组
    NSMutableArray *superArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
    
    //创建存索引的数组,无符号(正整数),无序(不能通过下标取值),不可重复(重复的话会自动过滤)
    NSMutableIndexSet *noneHeaderSections = [NSMutableIndexSet indexSet];
    //遍历superArray,得到一个当前屏幕中所有的section数组
    for (UICollectionViewLayoutAttributes *attributes in superArray)
    {
        //如果当前的元素分类是一个cell,将cell所在的分区section加入数组,重复的话会自动过滤
        if (attributes.representedElementCategory == UICollectionElementCategoryCell)
        {
            [noneHeaderSections addIndex:attributes.indexPath.section];
        }
    }
    
    //遍历superArray,将当前屏幕中拥有的header的section从数组中移除,得到一个当前屏幕中没有header的section数组
    //正常情况下,随着手指往上移,header脱离屏幕会被系统回收而cell尚在,也会触发该方法
    for (UICollectionViewLayoutAttributes *attributes in superArray)
    {
        //如果当前的元素是一个header,将header所在的section从数组中移除
        if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])
        {
            [noneHeaderSections removeIndex:attributes.indexPath.section];
        }
    }
    
    //遍历当前屏幕中没有header的section数组
    [noneHeaderSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop){
        
        //取到当前section中第一个item的indexPath
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
        //获取当前section在正常情况下已经离开屏幕的header结构信息
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
        
        //如果当前分区确实有因为离开屏幕而被系统回收的header
        if (attributes)
        {
            //将该header结构信息重新加入到superArray中去
            [superArray addObject:attributes];
        }
    }];
    
    //遍历superArray,改变header结构信息中的参数,使它可以在当前section还没完全离开屏幕的时候一直显示
    for (UICollectionViewLayoutAttributes *attributes in superArray) {
        
        //如果当前item是header
        if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader])
        {
            //得到当前header所在分区的cell的数量
            NSInteger numberOfItemsInSection = [self.collectionView numberOfItemsInSection:attributes.indexPath.section];
            //得到第一个item的indexPath
            NSIndexPath *firstItemIndexPath = [NSIndexPath indexPathForItem:0 inSection:attributes.indexPath.section];
            //得到最后一个item的indexPath
            NSIndexPath *lastItemIndexPath = [NSIndexPath indexPathForItem:MAX(0, numberOfItemsInSection-1) inSection:attributes.indexPath.section];
            //得到第一个item和最后一个item的结构信息
            UICollectionViewLayoutAttributes *firstItemAttributes, *lastItemAttributes;
            if (numberOfItemsInSection>0)
            {
                //cell有值,则获取第一个cell和最后一个cell的结构信息
                firstItemAttributes = [self layoutAttributesForItemAtIndexPath:firstItemIndexPath];
                lastItemAttributes = [self layoutAttributesForItemAtIndexPath:lastItemIndexPath];
            }else
            {
                //cell没值,就新建一个UICollectionViewLayoutAttributes
                firstItemAttributes = [UICollectionViewLayoutAttributes new];
                //然后模拟出在当前分区中的唯一一个cell,cell在header的下面,高度为0,还与header隔着可能存在的sectionInset的top
                CGFloat y = CGRectGetMaxY(attributes.frame)+self.sectionInset.top;
                firstItemAttributes.frame = CGRectMake(0, y, 0, 0);
                //因为只有一个cell,所以最后一个cell等于第一个cell
                lastItemAttributes = firstItemAttributes;
            }
            
            //获取当前header的frame
            CGRect rect = attributes.frame;
            
            //当前的滑动距离 + 因为导航栏产生的偏移量,默认为64(如果app需求不同,需自己设置)
            CGFloat offset = self.collectionView.contentOffset.y + _naviHeight;
            //第一个cell的y值 - 当前header的高度 - 可能存在的sectionInset的top
            CGFloat headerY = firstItemAttributes.frame.origin.y - rect.size.height - self.sectionInset.top;
            
            //哪个大取哪个,保证header悬停
            //针对当前header基本上都是offset更加大,针对下一个header则会是headerY大,各自处理
            CGFloat maxY = MAX(offset,headerY);
            
            //最后一个cell的y值 + 最后一个cell的高度 + 可能存在的sectionInset的bottom - 当前header的高度
            //当当前section的footer或者下一个section的header接触到当前header的底部,计算出的headerMissingY即为有效值
            CGFloat headerMissingY = CGRectGetMaxY(lastItemAttributes.frame) + self.sectionInset.bottom - rect.size.height;
            
            //给rect的y赋新值,因为在最后消失的临界点要跟谁消失,所以取小
            rect.origin.y = MIN(maxY,headerMissingY);
            //给header的结构信息的frame重新赋值
            attributes.frame = rect;
            
            //如果按照正常情况下,header离开屏幕被系统回收,而header的层次关系又与cell相等,如果不去理会,会出现cell在header上面的情况
            //通过打印可以知道cell的层次关系zIndex数值为0,我们可以将header的zIndex设置成1,如果不放心,也可以将它设置成非常大,这里随便填了个7
            attributes.zIndex = 7;
        }
    }
    
    //转换回不可变数组,并返回
    return [superArray copy];
    
}

//return YES;表示一旦滑动就实时调用上面这个layoutAttributesForElementsInRect:方法
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound
{
    return YES;
}
@end
  • collectionview的footer 和header方式是一模一样的 。

相关文章

网友评论

      本文标题:collectionview的高级应用

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