iOS:UICollectionView流式布局及其在该布局上的扩展的线式布局

简介:
UICollectionViewFlowLayout是苹果公司做好的一种单元格布局方式,它约束item的排列规则是:从左到右依次排列,如果右边不够放下,就换一行重复上面的方式排放,,,,,
 
常用的流式布局UICollectionViewFlowLayout的属性

@property (nonatomic) CGFloat minimumLineSpacing;       //每一个item之间最小的行间距

@property (nonatomic) CGFloat minimumInteritemSpacing;//每一个item之间最小的列间距

@property (nonatomic) CGSize itemSize;              //每一个item的大小

@property (nonatomic) CGSize estimatedItemSize; //每一个item的预测大小 

@property (nonatomic) UICollectionViewScrollDirection scrollDirection; // 集合视图的滚动方向,默认垂直

@property (nonatomic) CGSize headerReferenceSize; //表头视图大小

@property (nonatomic) CGSize footerReferenceSize; //表尾视图大小

@property (nonatomic) UIEdgeInsets sectionInset;    //和集合视图上下左右四边的间距

 

使用流式布局很简单,不需要我们做任何的操作,只需要创建它的实例,然后把它放入创建的集合视图中即可。然而,流式布局看起来比较的单一,没有很炫酷的效果。基于此,我们可以在流式布局的基础上进行一些布局的扩展,比如线式布局等。。。

 

 

 

 

下面就做一个流式布局和线式布局的切换,点击时,可以自由切换,使集合视图的item排列呈现出不同的效果,举例如下:

1、首先创建一个自定义的单元格类,并附带创建一个.xib文件,在.xib文件中设置一个UIImageView,将它IBOutLet到对应的类中

  

 

2、准备一些图片素材

3、在ImagesCell类中

.h

#import <UIKit/UIKit.h>

@interface ImagesCell : UICollectionViewCell
@property (strong,nonatomic)UIImage *image;
@end

.m

复制代码
#import "ImagesCell.h"

@interface ImagesCell() 
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end

@implementation ImagesCell


- (void)awakeFromNib {
    
    //设置图像视图图层的属性
    self.imageView.layer.borderWidth = 3;
    self.imageView.layer.borderColor = [[UIColor redColor]CGColor];
    self.imageView.layer.cornerRadius = 5;
    self.imageView.clipsToBounds = YES; //切割边角
    
}


-(void)setImage:(UIImage *)image
{
    _image = image;
    
    //显示图片
    [_imageView setImage:_image];
}
@end
复制代码

3.在控制器类ViewController类中

复制代码
#import "ViewController.h"
#import "ImagesCell.h"
#import "CustomLineLayout.h"

@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate>
@property (strong,nonatomic)UICollectionView *collectionView;
@property (strong,nonatomic)NSMutableArray *images;
@end

@implementation ViewController

static NSString *const reuseIndentifier = @"image";

//懒加载
-(NSMutableArray *)images
{
    if (!_images)
    {
        _images = [NSMutableArray array];
        for (int i=1; i<=25; i++)
        {
            [_images addObject:[NSString stringWithFormat:@"clothes%d",i]];
        }
    }
    return _images;
}

- (void)viewDidLoad {
    [super viewDidLoad];
   
    //创建集合视图
    CGFloat width = self.view.frame.size.width;
    CGRect rect = CGRectMake(0, 100, width, 200);
    self.collectionView = [[UICollectionView alloc]initWithFrame:rect collectionViewLayout:[[CustomLineLayout alloc]init]];

    
    //设置数据源和代理
    self.collectionView.dataSource  = self;
    self.collectionView.delegate  = self;
    
    //注册单元格
    [self.collectionView registerNib:[UINib nibWithNibName:@"ImagesCell" bundle:nil] forCellWithReuseIdentifier:reuseIndentifier];
    
    //添加视图
   [self.view addSubview:self.collectionView];
    
    
    //UICollectionViewLayout:最根本的布局,自定义布局时,完全需要自己重新去写需要的布局
    //UICollectionViewFlowLayout :流水布局,自定义布局时,有时可以在它的布局基础上再进行扩展布局
}


//切换布局方式
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if ([self.collectionView.collectionViewLayout isKindOfClass:[CustomLineLayout class]])
    {
        [self.collectionView setCollectionViewLayout:[[UICollectionViewFlowLayout alloc]init] animated:YES];
    }
    else
    {
        [self.collectionView setCollectionViewLayout:[[CustomLineLayout alloc]init] animated:YES];
    }
}

#pragma mark - <UICollectionDataSourceDelegate>
//返回组数
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}
//返回个数
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.images.count;
}
//显示conllectionView的单元格
-(ImagesCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    //设置重用单元格
    
    ImagesCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIndentifier forIndexPath:indexPath];

    //设置cell的内容
    cell.image = [UIImage imageNamed:[self.images objectAtIndex:indexPath.item]];
    return cell;
}

//选中item时删除它
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    //1.先删除掉对应的模型数据
    [self.images removeObjectAtIndex:indexPath.item];
    
    //2.删除item(刷新UI)
    [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
}
@end
复制代码

4、自定义线式布局,它继承于流式布局,即

在.m文件中设置每一个item的布局属性如下:

复制代码
#import "CustomLineLayout.h"

//设置item的固定的宽和高
static const CGFloat itemWH = 100;

//设置缩放时的有效距离
static const CGFloat activeDistance = 150;

//设置缩放因数,值越大,缩放效果越明显
static const CGFloat scaleFactor = 0.6;

@implementation CustomLineLayout


//UICollectionViewLayoutAttributes:很重要,布局属性设置
//每一个cell(item)都有自己的UICollectionViewLayoutAttributes
//每一个indexPath都有自己的UICollectionViewLayoutAttributes

-(instancetype)init{
    if (self = [super init]){
        
    }
    return self;
}

//每一次重新布局前,都会准备布局(苹果官方推荐使用该方法进行一些初始化)
-(void)prepareLayout
{
    [super prepareLayout];
    
    //初始化,设置默认的item属性
    self.itemSize = CGSizeMake(itemWH, itemWH);
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.minimumLineSpacing = itemWH * scaleFactor;
    
    //将第一个和最后一个item始终显示在中间,即分别将它们设置到组头和组尾的距离
    CGFloat inset = (self.collectionView.frame.size.width - itemWH) / 2;
    self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
}

//是否要重新刷新布局(只要显示的item边界发生改变就重新布局)
//只要每一次重新布局内部就会调用下面的layoutAttributesForElementsInRect:获取所有cell(item)的属性
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
    return YES;
}


//用来设置colectionView停止滚动时的那一刻位置,内部会自动调用
#pragma targetContentOffset : 原本colectionView停止滚动时的那一刻位置
#pragma velocity : 滚动的速率,根据正负可以判断滚动方向是向左还是向右

-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
    
    //1.计算colectionView最终停留的位置
    CGRect lastRect;
    lastRect.origin = proposedContentOffset;
    lastRect.size = self.collectionView.frame.size;
    
    //2.取出这个范围内的所有item的属性
    NSArray *array = [self layoutAttributesForElementsInRect:lastRect];
    
    
    //3.计算最终屏幕的中心x
    CGFloat centerX = proposedContentOffset.x+ self.collectionView.frame.size.width/2;
    
    
    //4.遍历所有的属性,通过计算item与最终屏幕中心的最小距离,然后将item移动屏幕的中心位置
    CGFloat adjustOffsetX = MAXFLOAT;
    for (UICollectionViewLayoutAttributes *attris in array)
    {
        if (ABS(attris.center.x - centerX) < ABS(adjustOffsetX)) {
            
            adjustOffsetX = attris.center.x - centerX;
        }
    }
 
    //5.返回要移动到中心的item的位置
    return CGPointMake(proposedContentOffset.x + adjustOffsetX , proposedContentOffset.y);
}

//返回需要重新布局的所有item属性
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    //0.计算可见的矩形框属性
    CGRect visiableRect;
    visiableRect.size = self.collectionView.frame.size;
    visiableRect.origin = self.collectionView.contentOffset;
    
    
    //1.取出默认的cell的UICollectionViewLayoutAttributes
    NSArray *array = [super layoutAttributesForElementsInRect:rect];
    
    
    //计算屏幕最中心的x(滚出去的所有的item的偏移 + collectionView宽度的一半)
    CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width/2;
    
    
    //2.遍历所有的布局属性
    for(UICollectionViewLayoutAttributes *attrs in array)
    {
        //如果遍历的item和可见的矩形框的frame不相交,即不e是可见的,就直接跳过,只对可见的item进行放缩
        if (!CGRectIntersectsRect(visiableRect, attrs.frame)) continue;
        
        //每一个item的中心x
        CGFloat itemCenterX =  attrs.center.x;
        
        
        //差距越大,缩放比例越小
        //计算每一个item的中心x和屏幕最中心x的绝对值距离,然后可以计算出缩放比例,即
        
        //<1>计算间距/屏幕一半时的比例,得出的数一定<1
        //CGFloat ratio = ABS(itemCenterX - centerX) / (self.collectionView.frame.size.width/2);
        //CGFloat ratio = ABS(itemCenterX - centerX) / 150;
        //<2>实现放大
        //CGFloat scale = 1 +  (1 - ratio);
        //attrs.transform3D = CATransform3DMakeScale(scale, scale, 1.0);
        //attrs.transform = CGAffineTransformMakeScale(scale, scale);
        
        
        //当item的中心x距离屏幕的中心x距离在有效距离150以内时,item才开始放大
        if (ABS(itemCenterX - centerX) <= activeDistance)
        {
            //CGFloat ratio = ABS(itemCenterX - centerX) / (self.collectionView.frame.size.width/2);
            CGFloat ratio = ABS(itemCenterX - centerX) / activeDistance;
            
            //<2>实现放大
            CGFloat scale = 1 +  scaleFactor*(1 - ratio);
            attrs.transform3D = CATransform3DMakeScale(scale, scale, 1.0);
            //attrs.transform = CGAffineTransformMakeScale(scale, scale);
        }
        else
        {
            attrs.transform = CGAffineTransformMakeScale(1, 1);
        }
    }
    return array;
}
@end
复制代码

演示结果如下:

 流式布局:                                                切换为线式布局:

 

 

 

程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式!
本文转自当天真遇到现实博客园博客,原文链接:http://www.cnblogs.com/XYQ-208910/p/4985527.html ,如需转载请自行联系原作者

相关文章
|
10月前
|
Swift iOS开发
iOS 用一个布局来解决嵌套问题—— UICollectionViewCompositionalLayout
iOS 用一个布局来解决嵌套问题—— UICollectionViewCompositionalLayout
iOS 用一个布局来解决嵌套问题—— UICollectionViewCompositionalLayout
|
8月前
|
iOS开发
iOS布局中的抗被拉伸、抗压缩优先级
iOS布局中的抗被拉伸、抗压缩优先级
460 0
|
Android开发 iOS开发
IOS开发之UIScrollView约束布局
IOS开发之UIScrollView约束布局
356 0
|
API iOS开发 开发者
iOS文本布局探讨之三——使用TextKit框架进行富文本布局
iOS文本布局探讨之三——使用TextKit框架进行富文本布局
333 0
iOS文本布局探讨之三——使用TextKit框架进行富文本布局
|
程序员 iOS开发 开发者
iOS开发:设置UICollectionView不同大小的item的方法
在iOS开发过程中,UICollectionView的使用作为iOS开发者来说都不陌生,但是要想完美的玩转UICollectionView的所有使用的技巧,还是需要了解很多的。本篇博文来分享一下关于UICollectionView设置不同大小item的方法,为的是迎合产品的需求,方便记录为了以后查看使用,分享给有需要的人。
550 0
iOS开发:设置UICollectionView不同大小的item的方法
|
iOS开发
iOS分享扩展支持自定义联系人
iOS可以通过添加Share Extension来扩展系统的分享组件,能够支持将网页,图片,音乐或任何接入了系统分享组件功能的应用分享到我们自己的App内。
291 0
|
搜索推荐 iOS开发
iOS小技能:消息推送扩展的使用
iOS小技能:消息推送扩展的使用
479 0
iOS小技能:消息推送扩展的使用
|
网络协议 Unix API
iOS进程间的实时通讯方案: local socket(解决扩展和容器应用的实时通讯问题)
iOS进程间的实时通讯方案: local socket(解决扩展和容器应用的实时通讯问题)
320 0
iOS进程间的实时通讯方案: local socket(解决扩展和容器应用的实时通讯问题)
|
iOS开发
iOS UICollectionView 从右向左对齐的实现
iOS UICollectionView 从右向左对齐的实现
513 0
iOS UICollectionView 从右向左对齐的实现