详解IOS中如何实现瀑布流效果

2025-05-29 0 99

首先是效果演示

详解IOS中如何实现瀑布流效果

特点:可以自由设置瀑布流的总列数(效果演示为2列)

虽然iphone手机的系统相册没有使用这种布局效果,瀑布流依然是一种很常见的布局方式!!!下面来详细介绍如何实现这种布局.

首先使用的类是uicollectionview

我们要做的是自定义uicollectionviewcell和uicollectionviewlayout

1、自定义uicollectionviewcell类,只需要一个uiimageview即可,frame占满整个cell.

2、重点是自定义uicollectionviewlayout,注意一定要继承于uicollectionviewlayout,千万别继承于uicolletionviewflowlayout.

3、另外还需要计算图片高度.

为什么要自定义uicollectionviewlayout ?

因为我们需要设置每个item的高度以及位置, 注意这里是位置, 我们真的会设置每个item的位置的相信我!!!自定义uicollectionviewlayout必须要重写三个协议方法,后面会讲到.

为什么要计算图片高度 ?

因为图片宽度固定,所以需要按照图片的比例来计算高度,使图片等比例显示.这样的好处是,妈妈再也不用担心我的照片被拉伸的奇形怪状了…而且还需要用图片的高度来计算整个collectionview的contentsize…打完收工!!!

主菜来了!!!

以下内容均在自定义的customcollectionviewlayout类里边

?

1

2

3

4

5

6

7
//自定义uicollectionviewlayout必须要重写的三个协议方法

//1.计算每个item的大小和位置

- (void)preparelayout;

//2.返回每个item的布局属性

- (nullable nsarray<__kindof uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect;

//3.返回collectionview的总高度

- (cgsize)collectionviewcontentsize;

可以看到第三个方法使用了nullability和泛型,系统的方法都添加了ios 9新特性。

通过上边的三个方法名我们可以大致了解需要去做什么.说一下第二个方法,需要返回一个数组,数组存放布局属性(uicollectionviewlayoutattributes)对象.那么我们需要写一个属性数组(attributesarray),将布局属性放入这个属性数组并返回.

详解IOS中如何实现瀑布流效果

还需要什么呢 ?看了文章开头的朋友应该注意到了,设置瀑布流的列数当然得有个属性(numberofcolumns)来表示列数.

请注意,因为要在外部设置列数,所以这个属性需要写在自定义类的.h文件中

另外为了方便,定义一个属性(itemwidth)来表示item的宽度

再定义一个属性(contentheight)来表示整个collectionview的contenview的高度

详解IOS中如何实现瀑布流效果

首先是初始化,并没有什么问题

?

1

2

3

4

5

6

7

8

9

10

11
- (instancetype)init {

self = [super init];

if (self) {

_attributesarray = [nsmutablearray array];

// 默认值设置为2列

_numberofcolumns = 2;

_contentheight = 0.0f;

_cellmargin = 5.0f;/**< 用来表示间距的属性 */

}

return self;

}

然后是getter方法,只需要使用点语法即可得到itemwidth的值(因为它就是固定的)

?

1

2

3

4

5

6

7

8
- (cgfloat)itemwidth {

//所有边距的和.两列时有三个边距, 三列时有四个边距,逻辑强大就是好...

cgfloat allmargin = (_numberofcolumns + 1) * _cellmargin;

//除去边界之后的总宽度

cgfloat nomarginwidth = cgrectgetwidth(self.collectionview.bounds) - allmargin;

//出去边距的总宽度除以列数得到每一列的宽度(也就是itemwidth)

return nomarginwidth / _numberofcolumns;

}

—接下来是难点—

必须重写的第一个方法

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60
- (void)preparelayout {

// 定义变量记录高度最小的列,初始为第0列高度最小.

#pragma mark - 注意这个是从0开始算的啊!!!

nsinteger shortestcolumn = 0;

#pragma mark - 注意这个是从0开始算的啊!!!

// 存储每一列的总高度.因为添加图片的列高度会变,所以需要定义一个数组来记录列的总高度.

nsmutablearray *columnheightarray = [nsmutablearray array];

// 设置列的初始高度为边距的高度,没毛病!!!

for (int i = 0; i < _numberofcolumns; i++) {

// 所有列初始高度均设置为cell的间距

[columnheightarray addobject:@(_cellmargin)];

}

// 遍历collectionview中第 0 区中的所有item

for (int i = 0; i < [self.collectionview numberofitemsinsection:0]; i++) {

//需要用到这个玩意,提前拿到.

nsindexpath *indexpath = [nsindexpath indexpathforitem:i insection:0];

// 创建系统需要的布局属性对象,看后边的参数就知道这就是每个item的布局属性了

uicollectionviewlayoutattributes *layoutattributes = [uicollectionviewlayoutattributes layoutattributesforcellwithindexpath: indexpath];

// 将布局属性放入数组中,这个数组当然是一开始定义的布局属性数组了

[_attributesarray addobject:layoutattributes];

// 设置每个item的位置(x, y, width, height)

// 横坐标的起始位置

#pragma mark - 比如一共两列,现在要放一张图片上去,需要放到高度最小的那一列.

#pragma mark - 假设第0列最短,那么item的x坐标就是从一个边距宽度那里开始.

#pragma mark - (itemwidth + cellmargin)为一个整体

cgfloat x = (self.itemwidth + _cellmargin) * shortestcolumn + _cellmargin;

// 纵坐标就是 总高度数组 中最小列对应的高度

#pragma mark - 图片始终是添加在高度最小的那一列

cgfloat y = [columnheightarray[column] floatvalue];/**<注意类型转换 */

// 宽度没什么好说的

cgfloat width = self.itemwidth;

#pragma mark - 这里给自定义的类声明了一个协议,通过协议得到图片的高度,调用时机就是需要item高度的时候

#pragma mark - 将item的宽度传给代理人(viewcontroller),vc计算好高度后将高度返回给自定义类

#pragma mark - 也就是↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

cgfloat height = [self.delegate collectionview:self.collectionview

layout:self

width:self.itemwidth

heightforitematindexpath:indexpath];

// 大功告成,设置item的位置信息,没什么好说

layoutattributes.frame = cgrectmake(x, y, width, height);

// 上边废了半天劲放了一个item上去了,总高度数组是不是该更新一下数据了

columnheightarray[shortestcolumn] = @([columnheightarray[shortestcolumn] floatvalue] + height + _cellmargin);

// 整个内容的高度,通过比较得到较大值作为整个内容的高度

self.contentheight = max(self.contentheight, [columnheightarray[shortestcolumn] floatvalue]);

// 刚才放了一个item上去,那么此时此刻哪一列的高度比较低呢

for (int i = 0; i < _numberofcolumns; i++) {

//当前列的高度(刚才添加item的那一列)

cgfloat currentheight = [columnheightarray[shortestcolumn] floatvalue];

// 取出第i列中存放列高度

cgfloat height = [columnheightarray[i] floatvalue];

if (currentheight > height) {

//第i列高度(height)最低时,高度最低的列(shortestcolumn)当然就是第i列了

shortestcolumn = i;

}

}

}

// 思考下只使用上边的代码会出现什么问题

// 并不能影响心情,请不要恐慌...

// 提示:这个方法会被多次调用,数组

}

—难点已经结束—

没有理解的朋友请重点看上边的难点方法.

必须重写的第二个方法

?

1

2

3

4
// 2.返回的是, 每个item对应的布局属性

- (nsarray<uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect {

return _attributesarray;

}

必须重写的第三个方法

?

1

2

3

4
// 3.返回collectionview的滚动范围

- (cgsize)collectionviewcontentsize {

return cgsizemake(0, _contentheight);

}

viewcontroller里边关于collectionview的创建和协议方法就没什么好说的了.

看下自定义类customcollectionviewlayout的创建以及属性的赋值情况:

?

1

2

3
customcollectionviewlayout *layout = [[customcollectionviewlayout alloc] init];

layout.numberofcolumns = 2;/**< 在viewcontroller里设置瀑布流的列数,2列或3列为最佳 */

layout.delegate = self;/**< 指定vc为计算高度协议方法的代理人 */

再看下协议方法的实现部分(在viewcontroller.m中实现)

?

1

2

3

4

5

6

7

8

9

10

11

12
- (cgfloat)collectionview:(uicollectionview *)collectionview

layout:(uicollectionviewlayout *)collectionviewlayout

width:(cgfloat)width

heightforitematindexpath:(nonnull nsindexpath *)indexpath {

uiimage *image = _imagesarray[indexpath.row];

// 根据传过来的宽度来设置一个合适的矩形, 高度设为cgfloat_max表示以宽度来计算高度

cgrect boundingrect = cgrectmake(0, 0, width, cgfloat_max);

// 通过系统函数来得到最终的矩形,需要引入头文件

// #import <avfoundation/avfoundation.h>

cgrect imagecurrentrect = avmakerectwithaspectratioinsiderect(image.size, boundingrect);

return imagecurrentrect.size.height;

}

总结

到这里呢,在ios实现瀑布流就算是结束了,有兴趣的朋友可以自己动手试一下,希望本文对大家开发ios有所帮助。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 详解IOS中如何实现瀑布流效果 https://www.kuaiidc.com/93874.html

相关文章

发表评论
暂无评论