iOS开发 - WaterflowLayout 瀑布流布局

Model Data

@interface Shop : NSObject
@property (nonatomic, assign) CGFloat w;
@property (nonatomic, assign) CGFloat h;
@property (nonatomic, copy) NSString *img;
@property (nonatomic, copy) NSString *price;
@end

define cell

@class Shop;
@interface ShopCell : UICollectionViewCell
@property (nonatomic, strong) Shop *shop;
@end
#import "ShopCell.h"
#import "Shop.h"
#import "UIImageView+WebCache.h"

@interface ShopCell()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *priceLabel;
@end

@implementation ShopCell

- (void)setShop:(Shop *)shop
{
    _shop = shop;

    // 1.图片
    [self.imageView sd_setImageWithURL:[NSURL URLWithString:shop.img] placeholderImage:[UIImage imageNamed:@"loading"]];

    // 2.价格
    self.priceLabel.text = shop.price;
}
@end

xib

WaterflowLayout 瀑布流布局

@class WaterflowLayout;

@protocol WaterflowLayoutDelegate <NSObject>
- (CGFloat)waterflowLayout:(WaterflowLayout *)waterflowLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath;
@end

@interface WaterflowLayout : UICollectionViewLayout
@property (nonatomic, assign) UIEdgeInsets sectionInset;
/** 每一列之间的间距 */
@property (nonatomic, assign) CGFloat columnMargin;
/** 每一行之间的间距 */
@property (nonatomic, assign) CGFloat rowMargin;
/** 显示多少列 */
@property (nonatomic, assign) int columnsCount;

@property (nonatomic, weak) id<WaterflowLayoutDelegate> delegate;

@end

@interface WaterflowLayout();
/** 这个字典用来存储每一列最大的Y值(每一列的高度) */
@property (nonatomic, strong) NSMutableDictionary *maxYDict;

/** 存放所有的布局属性 */
@property (nonatomic, strong) NSMutableArray *attrsArray;
@end

@implementation WaterflowLayout

- (NSMutableDictionary *)maxYDict
{
    if (!_maxYDict) {
        self.maxYDict = [[NSMutableDictionary alloc] init];
    }
    return _maxYDict;
}

- (NSMutableArray *)attrsArray
{
    if (!_attrsArray) {
        self.attrsArray = [[NSMutableArray alloc] init];
    }
    return _attrsArray;
}

- (instancetype)init
{
    if (self = [super init]) {
        self.columnMargin = 10;
        self.rowMargin = 10;
        self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
        self.columnsCount = 3;
    }
    return self;
}

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

/**
 *  每次布局之前的准备
 */
- (void)prepareLayout
{
    [super prepareLayout];

    // 1.清空最大的Y值
    for (int i = 0; i<self.columnsCount; i++) {
        NSString *column = [NSString stringWithFormat:@"%d", i];
        self.maxYDict[column] = @(self.sectionInset.top);
    }

    // 2.计算所有cell的属性
    [self.attrsArray removeAllObjects];
    NSInteger count = [self.collectionView numberOfItemsInSection:0];
    for (int i = 0; i<count; i++) {
        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        [self.attrsArray addObject:attrs];
    }
}

/**
 *  返回所有的尺寸
 */
- (CGSize)collectionViewContentSize
{
    __block NSString *maxColumn = @"0";
    [self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
        if ([maxY floatValue] > [self.maxYDict[maxColumn] floatValue]) {
            maxColumn = column;
        }
    }];
    return CGSizeMake(0, [self.maxYDict[maxColumn] floatValue] + self.sectionInset.bottom);
}

/**
 *  返回indexPath这个位置Item的布局属性
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    // 假设最短的那一列的第0列
    __block NSString *minColumn = @"0";
    // 找出最短的那一列
    [self.maxYDict enumerateKeysAndObjectsUsingBlock:^(NSString *column, NSNumber *maxY, BOOL *stop) {
        if ([maxY floatValue] < [self.maxYDict[minColumn] floatValue]) {
            minColumn = column;
        }
    }];

    // 计算尺寸
    CGFloat width = (self.collectionView.frame.size.width - self.sectionInset.left - self.sectionInset.right - (self.columnsCount - 1) * self.columnMargin)/self.columnsCount;
    CGFloat height = [self.delegate waterflowLayout:self heightForWidth:width atIndexPath:indexPath];

    // 计算位置
    CGFloat x = self.sectionInset.left + (width + self.columnMargin) * [minColumn intValue];
    CGFloat y = [self.maxYDict[minColumn] floatValue] + self.rowMargin;

    // 更新这一列的最大Y值
    self.maxYDict[minColumn] = @(y + height);

    // 创建属性
    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attrs.frame = CGRectMake(x, y, width, height);
    return attrs;
}

/**
 *  返回rect范围内的布局属性
 */
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    return self.attrsArray;
}

@end

控制器 ViewController

#import "ViewController.h"
#import "WaterflowLayout.h"
#import "MJExtension.h"
#import "Shop.h"
#import "ShopCell.h"
#import "MJRefresh.h"

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

@implementation ViewController

- (NSMutableArray *)shops
{
    if (_shops == nil) {
        self.shops = [NSMutableArray array];
    }
    return _shops;
}

static NSString *const ID = @"shop";

- (void)viewDidLoad {
    [super viewDidLoad];

    // 1.初始化数据
    NSArray *shopArray = [Shop objectArrayWithFilename:@"1.plist"];
    [self.shops addObjectsFromArray:shopArray];

    WaterflowLayout *layout = [[WaterflowLayout alloc] init];
    layout.delegate = self;
//    layout.sectionInset = UIEdgeInsetsMake(100, 20, 40, 30);
//    layout.columnMargin = 20;
//    layout.rowMargin = 30;
//    layout.columnsCount = 4;

    // 2.创建UICollectionView
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
    collectionView.backgroundColor = [UIColor whiteColor];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    [collectionView registerNib:[UINib nibWithNibName:@"ShopCell" bundle:nil] forCellWithReuseIdentifier:ID];
    [self.view addSubview:collectionView];
    self.collectionView = collectionView;

    // 3.增加刷新控件
    [self.collectionView addFooterWithTarget:self action:@selector(loadMoreShops)];
}

- (void)loadMoreShops
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSArray *shopArray = [Shop objectArrayWithFilename:@"1.plist"];
        [self.shops addObjectsFromArray:shopArray];
        [self.collectionView reloadData];
        [self.collectionView footerEndRefreshing];
    });
}

#pragma mark - <WaterflowLayoutDelegate>
- (CGFloat)waterflowLayout:(WaterflowLayout *)waterflowLayout heightForWidth:(CGFloat)width atIndexPath:(NSIndexPath *)indexPath
{
    Shop *shop = self.shops[indexPath.item];
    return shop.h / shop.w * width;
}

#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.shops.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    ShopCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
    cell.shop = self.shops[indexPath.item];
    return cell;
}

@end

瀑布流效果图

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 22:30:02

iOS开发 - WaterflowLayout 瀑布流布局的相关文章

iOS开发之窥探UICollectionViewController(四) --一款功能强大的自定义瀑布流

在上一篇博客中<iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流>,自定义瀑布流的列数,Cell的外边距,Cell的最大以及最小高度是在我们的布局文件中是写死的,换句话说也就是不可配置的.为了循序渐进,由浅入深呢,上篇博客暂且那么写.不过那样写太过死板,本来使用起来比较灵活的自定义布局,如果把其配置参数给写死了,就相当于在笼中的猛兽,再厉害不也白扯蛮. 在今天这篇博客中我们要接着上篇博客中的Demo,使其自定义布局

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作)

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作) 一.简单说明 使用数据刷新框架: 该框架提供了两种刷新的方法,一个是使用block回调(存在循环引用问题,_ _weak),一个是使用调用. 问题:在进行下拉刷新之前,应该要清空之前的所有数据(在刷新数据这个方法中). 移除正在显示的cell: (1)把字典中的所有的值,都从屏幕上移除 (2)清除字典中的所有元素 (3)清除cell的frame,每个位置的cell的frame都要重新计算 (4)清除可复用的缓存池. 该部分的代码如下: 1

iOS开发之窥探UICollectionViewController(三) --使用UICollectionView自定义瀑布流

上篇博客的实例是自带的UICollectionViewDelegateFlowLayout布局基础上来做的Demo, 详情请看<iOS开发之窥探UICollectionViewController(二) --详解CollectionView各种回调>.UICollectionView之所以强大,是因为其具有自定义功能,这一自定义就不得了啦,自由度非常大,定制的高,所以功能也是灰常强大的.本篇博客就不使用自带的流式布局了,我们要自定义一个瀑布流.自定义的瀑布流可以配置其参数: 每个Cell的边距

iOS开发UI篇—自定义瀑布流控件(基本实现)

iOS开发UI篇—自定义瀑布流控件(基本实现) 一.基本实现 说明:在View加载的时候,刷新数据. 1.实现代码 YYViewController.m文件 1 // 2 // YYViewController.m 3 // 06-瀑布流 4 // 5 // Created by apple on 14-7-28. 6 // Copyright (c) 2014年 wendingding. All rights reserved. 7 // 8 9 #import "YYViewControll

iOS流布局UICollectionView系列四——自定义FlowLayout进行瀑布流布局

iOS流布局UICollectionView系列四--自定义FlowLayout进行瀑布流布局 一.引言 前几篇博客从UICollectionView的基础应用到设置UICollectionViewFlowLayout更加灵活的进行布局,但都限制在系统为我们准备好的布局框架中,还是有一些局限性,例如,如果我要进行瀑布流似的不定高布局,前面的方法就很难满足我们的需求了,如下: 这种布局无疑在app的应用中更加广泛,商品的展示,书架书目的展示,都会倾向于采用这样的布局方式,当然,通过自定义FlowL

iOS:UICollectionView纯自定义的布局:瀑布流布局

创建瀑布流有三种方式: 第一种方式:在一个ScrollView里面放入三个单元格高度一样的tableView,禁止tableView滚动,只需让tableView随着ScrollView滚动即可.这种方式太奇葩,不太推荐使用... 第二种方式:在一个ScrollView里面从左到右依次放入三个UIView,当ScrollView滚动时,如果之前的三个view消失就将它们存入自定义的缓冲池,即数组中,下拉时再从数组中取出这三个view放到之前三个view位置的下面.但是,切记,每次要依次计算补全最

iOS开发UI篇—自定义瀑布流控件(蘑菇街实现)

iOS开发UI篇—自定义瀑布流控件(蘑菇街瀑布流) 一.简单说明 关于瀑布流 1.是使用UIScrollView实现的 2.刷新数据(reloadData)方法里面做哪些事情 3.layoutSubviews方法里面做哪些事情 4.模仿UItableView进行设计 完善: 瀑布流控件第一次显示到屏幕上的时候自动的向数据源索要数据,而不需要手动调用.这需要监听View的显示,View的显示有一个方法,叫做willMoveToSuperview:在该方法中直接刷新一次数据即可. 二.把自定义的瀑布

iOS开发UI篇—自定义瀑布流控件(接口设计)

iOS开发UI篇—自定义瀑布流控件(接口设计) 一.简单说明 1.关于瀑布流 电商应用要展示商品信息通常是通过瀑布流的方式,因为每个商品的展示图片,长度和商都都不太一样. 如果不用瀑布流的话,展示这样的格子数据,还有一种办法是使用九宫格. 但利用九宫格有一个缺点,那就是每个格子的宽高是一样的,如果一定要使用九宫格来展示,那么展示的商品图片可能会变形. 为了保证商品图片能够按照原来的宽高比进行展示,一般采用的是瀑布流的方式. 2.瀑布流的特点: 由很多的格子组成,但是每个格子的宽度和高速都是不确定

【iOS开发每日小笔记(二)】gitHub上的开源“瀑布流”使用心得

这篇文章是我的[iOS开发每日小笔记]系列中的一片,记录的是今天在开发工作中遇到的,可以用很短的文章或很小的demo演示解释出来的小心得小技巧.该分类的文章,内容涉及的知识点可能是很简单的.或是用很短代码片段就能实现的,但在我看来它们可能会给用户体验.代码效率得到一些提升,或是之前自己没有接触过的技术,很开心的学到了,放在这里得瑟一下(^_^).其实,90%的作用是帮助自己回顾.记忆.复习.如果看官觉得太easy,太碎片,则可以有两个选择:1,移步[iOS探究]分类,对那里的文章进行斧正:2,在