9 - 瀑布流 - 这才是实现"瀑布流"效果最行之有效的办法

首先, 对于瀑布流的实现大体分为tableView和collectionView实现两种, 以collectionView实现最为简单. 本文对流行的实现方式进行改进, 减少依赖,增加更多代理方法,增加扩展性

 1 //
 2 //  AYWaterFlowLayout.h
 3 //  AY瀑布流
 4 //
 5 //  Created by Jasper on 16/1/25.
 6 //  Copyright © 2016年 Jasper. All rights reserved.
 7 //
 8
 9 #import <UIKit/UIKit.h>
10
11 @class AYWaterFlowLayout;
12
13 @protocol AYWaterFlowLayoutDelegate <NSObject>
14 @required
15 - (CGFloat)waterflowLayout:(AYWaterFlowLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth;
16
17 @optional
18 - (CGFloat)columnCountInWaterflowLayout:(AYWaterFlowLayout *)waterflowLayout;
19 - (CGFloat)columnMarginInWaterflowLayout:(AYWaterFlowLayout *)waterflowLayout;
20 - (CGFloat)rowMarginInWaterflowLayout:(AYWaterFlowLayout *)waterflowLayout;
21 - (UIEdgeInsets)edgeInsetsInWaterflowLayout:(AYWaterFlowLayout *)waterflowLayout;
22 @end
23
24
25 @interface AYWaterFlowLayout : UICollectionViewLayout
26 /** 代理 */
27 @property (nonatomic, weak) id<AYWaterFlowLayoutDelegate> delegate;
28 @end
  1 //
  2 //  AYWaterFlowLayout.m
  3 //  AY瀑布流
  4 //
  5 //  Created by Jasper on 16/1/25.
  6 //  Copyright © 2016年 Jasper. All rights reserved.
  7 //
  8
  9 #import "AYWaterFlowLayout.h"
 10
 11
 12 /** 默认的列数 */
 13 static const NSInteger AYDefaultColumnCount = 3;
 14 /** 每一列之间的间距 */
 15 static const CGFloat AYDefaultColumnMargin = 10;
 16 /** 每一行之间的间距 */
 17 static const CGFloat AYDefaultRowMargin = 10;
 18 /** 边缘间距 */
 19 static const UIEdgeInsets AYDefaultEdgeInsets = {10, 10, 10, 10};
 20
 21
 22
 23
 24
 25 @interface AYWaterFlowLayout ()
 26
 27 /** 存放所有cell的布局属性 */
 28 @property (nonatomic, strong) NSMutableArray *attrsArray;
 29 /** 存放所有列的当前高度 */
 30 @property (nonatomic, strong) NSMutableArray *columnHeights;
 31 /** 内容的高度 */
 32 @property (nonatomic, assign) CGFloat contentHeight;
 33
 34
 35 - (CGFloat)rowMargin;
 36 - (CGFloat)columnMargin;
 37 - (NSInteger)columnCount;
 38 - (UIEdgeInsets)edgeInsets;
 39
 40 @end
 41
 42
 43 @implementation AYWaterFlowLayout
 44
 45 #pragma mark - 常见数据处理
 46 - (CGFloat)rowMargin
 47 {
 48     if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) {
 49         return [self.delegate rowMarginInWaterflowLayout:self];
 50     } else {
 51         return AYDefaultRowMargin;
 52     }
 53 }
 54
 55 - (CGFloat)columnMargin
 56 {
 57     if ([self.delegate respondsToSelector:@selector(columnMarginInWaterflowLayout:)]) {
 58         return [self.delegate columnMarginInWaterflowLayout:self];
 59     } else {
 60         return AYDefaultColumnMargin;
 61     }
 62 }
 63
 64 - (NSInteger)columnCount
 65 {
 66     if ([self.delegate respondsToSelector:@selector(columnCountInWaterflowLayout:)]) {
 67         return [self.delegate columnCountInWaterflowLayout:self];
 68     } else {
 69         return AYDefaultColumnCount;
 70     }
 71 }
 72
 73 - (UIEdgeInsets)edgeInsets
 74 {
 75     if ([self.delegate respondsToSelector:@selector(edgeInsetsInWaterflowLayout:)]) {
 76         return [self.delegate edgeInsetsInWaterflowLayout:self];
 77     } else {
 78         return AYDefaultEdgeInsets;
 79     }
 80 }
 81
 82 #pragma mark - 懒加载
 83 - (NSMutableArray *)columnHeights
 84 {
 85     if (!_columnHeights) {
 86         _columnHeights = [NSMutableArray array];
 87     }
 88     return _columnHeights;
 89 }
 90
 91 - (NSMutableArray *)attrsArray
 92 {
 93     if (!_attrsArray) {
 94         _attrsArray = [NSMutableArray array];
 95     }
 96     return _attrsArray;
 97 }
 98
 99 /**
100  * 初始化
101  */
102 - (void)prepareLayout
103 {
104     [super prepareLayout];
105
106     self.contentHeight = 0;
107
108     // 清除以前计算的所有高度
109     [self.columnHeights removeAllObjects];
110     for (NSInteger i = 0; i < self.columnCount; i++) {
111         [self.columnHeights addObject:@(self.edgeInsets.top)];
112     }
113
114     // 清除之前所有的布局属性
115     [self.attrsArray removeAllObjects];
116     // 开始创建每一个cell对应的布局属性
117     NSInteger count = [self.collectionView numberOfItemsInSection:0];
118     for (NSInteger i = 0; i < count; i++) {
119         // 创建位置
120         NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
121         // 获取indexPath位置cell对应的布局属性
122         UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
123         [self.attrsArray addObject:attrs];
124     }
125 }
126
127 /**
128  * 决定cell的排布
129  */
130 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
131 {
132     return self.attrsArray;
133 }
134
135 /**
136  * 返回indexPath位置cell对应的布局属性
137  */
138 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
139 {
140     // 创建布局属性
141     UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
142
143     // collectionView的宽度
144     CGFloat collectionViewW = self.collectionView.frame.size.width;
145
146     // 设置布局属性的frame
147     CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columnMargin) / self.columnCount;
148     CGFloat h = [self.delegate waterflowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w];
149
150     // 找出高度最短的那一列
151     NSInteger destColumn = 0;
152     CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
153     for (NSInteger i = 1; i < self.columnCount; i++) {
154         // 取得第i列的高度
155         CGFloat columnHeight = [self.columnHeights[i] doubleValue];
156
157         if (minColumnHeight > columnHeight) {
158             minColumnHeight = columnHeight;
159             destColumn = i;
160         }
161     }
162
163     CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);
164     CGFloat y = minColumnHeight;
165     if (y != self.edgeInsets.top) {
166         y += self.rowMargin;
167     }
168     attrs.frame = CGRectMake(x, y, w, h);
169
170     // 更新最短那列的高度
171     self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
172
173     // 记录内容的高度
174     CGFloat columnHeight = [self.columnHeights[destColumn] doubleValue];
175     if (self.contentHeight < columnHeight) {
176         self.contentHeight = columnHeight;
177     }
178     return attrs;
179 }
180
181 - (CGSize)collectionViewContentSize
182 {
183     //    CGFloat maxColumnHeight = [self.columnHeights[0] doubleValue];
184     //    for (NSInteger i = 1; i < self.columnCount; i++) {
185     //        // 取得第i列的高度
186     //        CGFloat columnHeight = [self.columnHeights[i] doubleValue];
187     //
188     //        if (maxColumnHeight < columnHeight) {
189     //            maxColumnHeight = columnHeight;
190     //        }
191     //    }
192     return CGSizeMake(0, self.contentHeight + self.edgeInsets.bottom);
193 }
194
195 @end

设计的思想源于tableView, 每个item的高度不该由控件本身决定,而是应该由数据决定, 通过代理告诉我每个item返回多高,边界距离, 如果没有实现代理方法,那么返回默认宽高,边界距离, 不依赖于任何数据模型

时间: 2024-11-05 11:41:22

9 - 瀑布流 - 这才是实现"瀑布流"效果最行之有效的办法的相关文章

IO流(四):其他流

一.操作基本数据类型的流 (一)构造方法 1.数据输入流:DataInputStream(InputStream in) 2.数据输出流:DataOutputStream(OutputStream out) (二)方法 1.DataOutputStream: writeByte(10); writeShort(100); writeInt(1000); writeLong(10000); writeFloat(12.34F); writeDouble(12.56); writeChar('a')

黑马程序员——Java基础--IO流(一)---File类以及其他流对象

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.File类 File类是将文件系统中的文件和文件夹封装成了对象.提供了更多的属性和行为可以对这些文件和文件夹进行操作.这些是流对象办不到的,因为流只操作数据.File对象可以作为参数传递给流对象的构造函数.File 类的实例是不可变的:也就是说,一旦创建,File 对象表示的抽象路径名将永不改变. 1.File类中的方法 (1).File类的构造方法以及字段 1 new File(Stri

面试题:JavaIO流分类详解与常用流用法实例

Java流概念: Java把所有的有序数据都抽象成流模型,简化了输入输出,理解了流模型就理解了Java IO.可以把流想象成水流,里面的水滴有序的朝某一方向流动.水滴就是数据,且代表着最小的数据流动单位,在字节流中,水滴就是一字节(byte),在字符流中,水滴就是一字符(char). Java流的分类方法大致分为以下几种: 1.按流向划分,分为输入流.输出流 请注意,这里的流向是以程序的运行时内存为参照的. 输入流类名中包含关键字InputStream或Reader,输出流类名中包含关键字Out

Java基础知识强化之IO流笔记63:随机访问流RandomAccessFile

1. 随机访问流RandomAccessFile RandomAccessFile类不属于流,是Object类的子类.但它融合了InputStream和OutputStream的功能.支持对随机访问文件的读取和写入. RandomAccessFile的构造方法: 构造方法摘要 RandomAccessFile(File file, String mode)           创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定. RandomAccessFile(St

IO包中的其他类 打印流,序列流,操作对象,管道流,RandomAccessFile,操作基本数据类型,操作字节数组

打印流,序列流,操作对象,管道流,RandomAccessFile,操作基本数据类型,操作字节数组 一.打印流: 该流提供了打印方法,可以将各种数据类型的数据都原样打印. 字节打印流PrintStream构造函数可以接收的参数类型1.File对象 File2.字符串路径 String3.字节输出流 OutputStream 字符打印流PrintWriter(更常用)1.File对象 File2.字符串路径 String3.字节输出流 OutputStream4.字符输出流 Writer publ

黑马程序员——Java基础——IO流(三)—对象的序列化(持久化),管道流,操作基本数据类型的流对象

第一讲 对象序列化(持久化) 概述: 将堆内存中的对象存入硬盘,以包括对象的数据,称为持久化或序列化 使用的类:ObjectInputStream和ObjectOutputStream 特有方法: ObjectInputStream Object readObject():读取 ObjectOutputStream void writeObject(obj):写入 1 /* 2 * 需求:将一个对象序列化,并从硬盘中读取序列化的对象 3 * 4 */ 5 import java.io.*; 6

一、javaSE (二十二)登录注册IO版本案例、数据操作流、内存操作流、打印流、标准输入输出流、随机访问流、合并流、序列化流、Properties、NIO

1:登录注册Io版本案例(掌握) 要求,对着写一遍 cn.itcast.pojo User cn.itcast.dao UserDao cn.itcast.dao.impl UserDaoImp1(实现我不管) cn.itcast.game GuessNumber cn.itcast.test UserTest 2:数据操作流(操作基本类型数据的流)(理解) (1)可以操作基本类型的数据 (2)流对象名称 DataInputStream DataOutputStream 3:内存操作流(理解)

字符缓冲流,properties类,序列化流与反序列化流,打印流

1.字符缓冲流的写法与字节缓冲流类似,也是用字符缓冲对象嵌套字符读写对象.格式为: BufferedReader br=new BufferedReader(new FileReader(数据源)): BufferedWriter bw=new BufferedWriter(new FileWriter(目的地)): 2.BufferedReader有一个优势是可以读取一个文本行,它的方法是readLine(),这与FileReader相区别. 3.在IO操作时选用哪个对象,要看输入还是输出,文

java 节点流(字符流,字节流)和包装流(缓冲流,转换流)

结点流:直接对File类进行操作的文件流 package stream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import org.junit.jupiter.api.Test; /* * 流的体系结构: 抽象基类 节点流(或文件流) 缓冲流(处理流的一