UICollectionView详解二:UICollectionViewFlowLayout

来源:互联网 发布:计算机一级用什么软件 编辑:程序博客网 时间:2024/06/06 14:01

UICollectionViewFlowLayout是一个具体的layout对象,用来把item布局在网格中,并且可选页眉和页脚。在collection view中的items,可以从一行或者一列flow至下一行或者下一列(行或者列取决于滚动的方向)。每行都会根据情况,包含尽可能多的Cells。Cells可以是相同的尺寸,也可以是不同的尺寸。

页眉页脚的属性如下图


当垂直的时候,需要设置Height,如下图


 当水平的时候,需要设置Width,如下图


Section Inset : 我们先通过两个图来看看Sections Insets是怎么回事


 


 从上面的两个图中,我们大概知道了,Section Inset就是某个section中cell的边界范围。

本节中得Demo,就是针对一行进行设计的。效果图如下:



一行显示很多的Icon,并且距中心点越近的Icon,尺寸越大;距离中心点越远的Icon,尺寸逐渐变小。而且在滚动过程中,也是这种效果。

1. 主界面的代码如下(如有不明白的,请参考我上一节讲解的内容,请点击这里):

@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate>@property (nonatomic,strong) NSMutableArray *images;@property (nonatomic,weak) UICollectionView *collectionView;@endstatic NSString *const identifer = @"ImageCell";@implementation ViewController-(NSMutableArray *)images {    if (!_images) {        _images = [NSMutableArray array];        for (int i=1;i<=20;i++) {            [_images addObject:[NSString stringWithFormat:@"%d.jpg",i]];        }    }    return _images;}- (void)viewDidLoad {    [super viewDidLoad];        CGRect rect = CGRectMake(0, 250, self.view.frame.size.width,200);    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:rect collectionViewLayout:[[LFFlowLayout alloc] init]];    collectionView.dataSource = self;    collectionView.delegate = self;        // 注册collectionView(因为是从xib中加载cell的,所以registerNib)    [collectionView registerNib:[UINib nibWithNibName:@"ImageCell" bundle:nil] forCellWithReuseIdentifier:identifer];        [self.view addSubview:collectionView];        self.collectionView = collectionView;}#pragma mark - 点击屏幕空白处,切换布局模式//- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {//    if ([self.collectionView.collectionViewLayout isKindOfClass:[LFFlowLayout class]]) {//        [self.collectionView setCollectionViewLayout:[[LFNormalLayout alloc] init] animated:YES];//    }//    else{//        [self.collectionView setCollectionViewLayout:[[LFFlowLayout alloc] init] animated:YES];//    }////}#pragma mark - delegate- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {    return  self.images.count;}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {    ImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifer forIndexPath:indexPath];        cell.iconName = self.images[indexPath.item];        return cell;}- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {    // 1. 删除模型数据    [self.images removeObjectAtIndex:indexPath.item];        // 2. 删除UI元素    [collectionView deleteItemsAtIndexPaths:@[indexPath]];}

2. 自定义一个类,继承自UICollectionViewFlowLayout

@interface LFFlowLayout : UICollectionViewFlowLayout@end

3. 控制布局的主要代码如下,有以下几点,我进行说明:

1) prepareLayout 方法可以对布局中得内容进行初始化工作。如果init方法中执行的代码不起作用,可以放在prepareLayout中。

- (void)prepareLayout {    [super prepareLayout];    ...}
2)  允许CollectionView Bounds发生变化时,重新进行布局, 要实现下面的方法

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {    return YES;}

3) 流式处理中最关键的方法如下,用来获取CollectionView的所有Item项的layout,进行相印的处理。

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {       ...}
4) 对于Item的细节计算,可以调用下面的方法,完成定位处理。

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {     ...}


完整的实现代码如下:

#define kItemWidth 100#import "LFFlowLayout.h"@implementation LFFlowLayout-(instancetype)init{    if(self=[super init]){    }    return self;}- (void)prepareLayout {    [super prepareLayout];    // 设置为水平滚动    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;    // 设置每个Item之间的距离    self.minimumLineSpacing = 100;    // 重新设置Item的尺寸,不然的话,有等比例缩小的可能    self.itemSize = CGSizeMake(kItemWidth, kItemWidth);}#pragma mark - 重写父类的方法- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {    return YES;}// 获取CollectionView的所有Item项,进行相印的处理(移动过程中,控制各个Item的缩放比例)-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {    NSArray *array = [super layoutAttributesForElementsInRect:rect];        CGFloat inset = (self.collectionView.frame.size.width - kItemWidth) * 0.5;    // 设置第一个和最后一个默认居中显示    self.collectionView.contentInset = UIEdgeInsetsMake(0, inset, 0, inset);        CGRect visibleRect;    visibleRect.origin =self.collectionView.contentOffset;    visibleRect.size = self.collectionView.frame.size;        CGFloat collectionViewCenterX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;        for (UICollectionViewLayoutAttributes *attrs in array) {        // 只处理正在界面上面显示的Item        if(!CGRectIntersectsRect(visibleRect, attrs.frame)) continue;                // 计算各个Item的缩放比例(距离中线越近,缩放比例就越大)        CGFloat scale;        // 防止突变的情况(当Item的中心与collectionView中心的距离大于等于collectionView宽度的一半时,Item不缩放,平稳过度)        if(ABS(attrs.center.x - collectionViewCenterX) >= self.collectionView.frame.size.width * 0.5){            scale = 1;        }        else{            scale = 1 + 0.8 * (1 - ABS(attrs.center.x - collectionViewCenterX) / (self.collectionView.frame.size.width * 0.5));        }        attrs.transform3D = CATransform3DMakeScale(scale, scale, 1);    }            return array;}// 当UICollectionView停止的那一刻ContentOffset的值(控制UICollectionView停止时,有一个Item一定居中显示)// 1.proposedContentOffset默认的ContentOffset- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {        //1. 获取UICollectionView停止的时候的可视范围    CGRect contentFrame;    contentFrame.size = self.collectionView.frame.size;    contentFrame.origin = proposedContentOffset;        NSArray *array = [self layoutAttributesForElementsInRect:contentFrame];        //2. 计算在可视范围的距离中心线最近的Item    CGFloat minCenterX = CGFLOAT_MAX;    CGFloat collectionViewCenterX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;    for (UICollectionViewLayoutAttributes *attrs in array) {        if(ABS(attrs.center.x - collectionViewCenterX) < ABS(minCenterX)){            minCenterX = attrs.center.x - collectionViewCenterX;        }    }        //3. 补回ContentOffset,则正好将Item居中显示    return CGPointMake(proposedContentOffset.x + minCenterX, proposedContentOffset.y);}@end


0 0
原创粉丝点击