• 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

    演示结果如下:

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

     

     

  • 相关阅读:
    单分发器
    Python 虚拟环境
    $modal
    memoization
    directive例子2
    UI-Router
    angular-translate
    directive例子1
    FileInputStream/FileOutputStream的应用
    自定义readLine
  • 原文地址:https://www.cnblogs.com/XYQ-208910/p/4985527.html
Copyright © 2020-2023  润新知