zoukankan      html  css  js  c++  java
  • 自定义UICollectinviewFlowLayout,即实现瀑布流

    如图所示,通过实现不规则的网格分布,来显示出不同的效果。因为集合视图必须要指定布局还可以显示,所以自定义布局就可以实现瀑布流的效果。

    //创建布局对象
        WaterFlowLayout *flowLayout = [[WaterFlowLayout alloc] init];
        
        flowLayout.delegate = self;
        flowLayout.numberOfColumn = 3;
        
        //创建集合视图
        UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:flowLayout];

    因为系统自带的布局有四个方法,分别实现了item大小,分区间隔,最小行间距,item之间的间隙大小

    @protocol UICollectionViewDelegateFlowLayout <UICollectionViewDelegate>
    @optional
    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;

    所以,自定义FlowLayout,并定义协议,以便定义这些方法。

    @protocol WaterFlowLayoutDelegate <NSObject>
    
    //关键方法,此方法的作用是返回每一个item的size大小
    //数据中原始图片大小
    - (CGSize)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
    //分区间隔
    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
    //得到 item之间的间隙大小
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
    //最小行间距
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(WaterFlowLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
    
    @end
    
    @interface WaterFlowLayout : UICollectionViewLayout
    
    //瀑布流一共多少列
    @property (nonatomic, assign) NSUInteger numberOfColumn;
    
    @property (nonatomic, assign) id<WaterFlowLayoutDelegate>delegate;

    看图可知,最小高的的item,将作为下一列item的起点。

    @interface WaterFlowLayout ()
    
    //存放每一列的高度
    @property (nonatomic, retain) NSMutableArray *columnHeightsArray;
    
    //存放 每一个item的 属性 包含 frame以及下标
    @property (nonatomic, retain) NSMutableArray *attributesArray;
    
    @end
    
    @implementation WaterFlowLayout
    
    //获取最小高度的方法
    - (CGFloat)minHeight
    {
        CGFloat min = 100000;
        for (NSNumber *height in _columnHeightsArray) {
            CGFloat h = [height floatValue];
            if (min > h) {
                min = h;
            }
        }
        return min;
    }
    
    //获取最大值
    - (CGFloat)maxHeight
    {
        CGFloat max = 0;
        for (NSNumber *height in _columnHeightsArray) {
            CGFloat h = [height floatValue];
            if (max < h) {
                max = h;
            }
        }
        return max;
    }
    
    //找到最小高的下标
    - (NSUInteger)indexOfMinHeight
    {
        NSUInteger index = 0;
        for (int i = 0; i < [_columnHeightsArray count]; i ++) {
            CGFloat height = [_columnHeightsArray[i] floatValue];
            if (height == [self minHeight]) {
                index = i;
                return index;
            }
        }
        return index;
    }
    
    //重写父类的布局方法
    - (void)prepareLayout
    {
        [super prepareLayout];
        
        _attributesArray = [[NSMutableArray alloc] init];
        
        _columnHeightsArray = [[NSMutableArray alloc] initWithCapacity:self.numberOfColumn];
        
        //给列高数组里面的对象赋初值
        for (int i = 0; i < self.numberOfColumn; i ++) {
            [_columnHeightsArray addObject:@0.0];
        }
        
        CGFloat totalWidth = self.collectionView.frame.size.width;
        
        //创建 每个item frame中的x、y
        CGFloat x = 0;
        CGFloat y = 0;
        
        NSUInteger itemCount = [self.collectionView numberOfItemsInSection:0];
        
        for (int i = 0; i < itemCount; i ++) {
            //得到集合视图中 列间隙的个数
            NSUInteger numberOfSpace = self.numberOfColumn - 1;
            
            //代理对象执行代理方法,得到 item之间的间隙大小
            CGFloat spaceWidth = [_delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:0];
            
            //求每列的宽度,也就是每个item的width
            CGFloat width = (totalWidth - spaceWidth * numberOfSpace) / self.numberOfColumn;
            
            
            //获取每一个itemSize的大小
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
            
            //数据中原始图片大小
            CGSize imageSize = [_delegate collectionView:self.collectionView layout:self sizeForItemAtIndexPath:indexPath];
        
            //通过 约分公式得到固定宽之后的高度是多少
            CGFloat height = width * imageSize.height / imageSize.width;
            
            
            UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
            
            //记录每一个item的大小和位置
            attribute.frame = CGRectMake(x, y, width, height);
            
            //数组保存每个item的位置信息
            [_attributesArray addObject:attribute];
            
            NSLog(@"item = %d",i);
            NSLog(@"x = %.2f y = %.2f width = %.2f height = %.2f",x,y,width,height);
            
            //求列高最小的那一列的下标
            NSUInteger minHeightIndex = [self indexOfMinHeight];
            
            //求出最小列的高度
            CGFloat minHeight = [_columnHeightsArray[minHeightIndex] floatValue];
            
            //求出行高
            CGFloat lineHeight = [_delegate collectionView:self.collectionView layout:self minimumLineSpacingForSectionAtIndex:0];
            
            //上一次总的列高 加上 行高 加上新加上的item的height,才是现在这一列的总高度
            //minHeight为最小列现在的高度
            //lineHeight为行间距
            //height为新加的item的高
            _columnHeightsArray[minHeightIndex] = [NSNumber numberWithFloat:minHeight + lineHeight + height];
            
            //重新算最小列高的下标
            minHeightIndex = [self indexOfMinHeight];
            
            //算下一次新加的item的x和y值
            x = (spaceWidth + width) * minHeightIndex;
            
            y = [self minHeight];
        }
    }
    
    //重写这个方法,可以返回集合视图的总高度
    - (CGSize)collectionViewContentSize
    {
        return CGSizeMake(self.collectionView.frame.size.width, [self maxHeight]);
    }
    
    
    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
    {
        return _attributesArray;
    }

    注意,最后一个方法的实现,即- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect,如果这个方法不写,集合视图是显示不出来的,这个方法是次保存的每个item的信息重新告诉集合视图,进行显示。

  • 相关阅读:
    Windows Azure Storage (17) Azure Storage读取访问地域冗余(Read Access – Geo Redundant Storage, RA-GRS)
    SQL Azure (15) SQL Azure 新的规格
    Azure China (5) 管理Azure China Powershell
    Azure China (4) 管理Azure China Storage Account
    Azure China (3) 使用Visual Studio 2013证书发布Cloud Service至Azure China
    Azure China (2) Azure China管理界面初探
    Azure China (1) Azure公有云落地中国
    SQL Azure (14) 将云端SQL Azure中的数据库备份到本地SQL Server
    [New Portal]Windows Azure Virtual Machine (23) 使用Storage Space,提高Virtual Machine磁盘的IOPS
    Android数据库升级、降级、创建(onCreate() onUpgrade() onDowngrade())的注意点
  • 原文地址:https://www.cnblogs.com/xueyao/p/5188321.html
Copyright © 2011-2022 走看看