瀑布流是电商应用展示商品通常采用的一种方式,如图示例
瀑布流的实现方式,通常有以下几种
- 通过uitableview实现(不常用)
- 通过uiscrollview实现(工作量较大)
- 通过uicollectionview实现(通常采用的方式)
一、uicollectionview基础
1、uicollectionview与uitableview有很多相似的地方,如
- 都通过数据源提供数据
- 都通过代理执行相关的事件
- 都可以自定义cell,且涉及到cell的重用
- 都继承自uiscrollview,具有滚动效果
2、uicollectionview的特性
- 需要有一个uicollectionviewlayout类(通常是uicollectionviewflowlayout类)或其子类对象,来决定cell的布局
- 可以实现uicollectionviewlayout的子类,来定制uicollectionview的滚动方向以及cell的布局
3、uicollectionviewlayout的子类uicollectionviewflowlayout
- uicollectionviewflowlayout即流水布局
- 流水布局uicollectionview的最常用的一种布局方式
二、自定义布局
1、自定义布局需要实现uicollectionviewlayout的子类
2、自定义布局常用方法
初始化布局
1
2
3
4
|
- ( void )preparelayout
{
//通常在该方法中完成布局的初始化操作
}
|
当尺寸改变时,是否更新布局
1
2
3
4
|
- ( bool )shouldinvalidatelayoutforboundschange:(cgrect)newbounds
{
//默认返回yes
}
|
布局uicollectionview的元素
1
2
3
4
5
6
7
8
|
- (nullable nsarray<__kindof uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect
{
//该方法需要返回rect区域中所有元素布局属性的数组
}
- (nullable uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nsindexpath *)indexpath
{
//该方法返回indexpath位置的元素的布局属性
}
|
修改uicollectionview停止滚动是的偏移量
1
2
3
4
|
- (cgpoint)targetcontentoffsetforproposedcontentoffset:(cgpoint)proposedcontentoffset withscrollingvelocity:(cgpoint)velocity
{
//返回值是,uicollectionview最终停留的点
}
|
三、示例
1、实现效果
2、实现思路
- 通过uicollectionview实现
- 自定义布局,即横向流水布局和圆形普通布局
- 通过uicollectionview的代理方法,当点击cell时,将其删除
- 通过监听uiview的触摸事件,当点解控制器的view时更改布局
3、实现步骤
自定横向流水布局
初始化布局
1
2
3
4
5
6
7
8
9
|
- ( void )preparelayout
{
[super preparelayout];
//设置滚动方向
self.scrolldirection = uicollectionviewscrolldirectionhorizontal;
//设置内边距
cgfloat inset = (self.collectionview.frame.size.width - self.itemsize.width) * 0.5;
self.sectioninset = uiedgeinsetsmake(0, inset, 0, inset);
}
|
指定当尺寸改变时,更新布局
1
2
3
4
|
- ( bool )shouldinvalidatelayoutforboundschange:(cgrect)newbounds
{
return yes;
}
|
设置所有元素的布局属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
- (nullable nsarray<uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect
{
//获取rect区域中所有元素的布局属性
nsarray *array = [super layoutattributesforelementsinrect:rect];
//获取uicollectionview的中点,以contentview的左上角为原点
cgfloat centerx = self.collectionview.contentoffset.x + self.collectionview.frame.size.width * 0.5;
//重置rect区域中所有元素的布局属性,即基于他们距离uicollectionview的中点的剧烈,改变其大小
for (uicollectionviewlayoutattributes *attribute in array)
{
//获取距离中点的距离
cgfloat delta = abs (attribute.center.x - centerx);
//计算缩放比例
cgfloat scale = 1 - delta / self.collectionview.bounds.size.width;
//设置布局属性
attribute.transform = cgaffinetransformmakescale(scale, scale);
}
//返回所有元素的布局属性
return [array copy];
}
|
设置uicollectionview停止滚动是的偏移量,使其为与中心点
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
|
- (cgpoint)targetcontentoffsetforproposedcontentoffset:(cgpoint)proposedcontentoffset withscrollingvelocity:(cgpoint)velocity
{
//计算最终显示的矩形框
cgrect rect;
rect.origin.x = proposedcontentoffset.x;
rect.origin.y = 0;
rect.size = self.collectionview.frame.size;
//获取最终显示在矩形框中的元素的布局属性
nsarray *array = [super layoutattributesforelementsinrect:rect];
//获取uicollectionview的中点,以contentview的左上角为原点
cgfloat centerx = proposedcontentoffset.x + self.collectionview.frame.size.width * 0.5;
//获取所有元素到中点的最短距离
cgfloat mindelta = maxfloat;
for (uicollectionviewlayoutattributes *attribute in array)
{
cgfloat delta = attribute.center.x - centerx;
if ( abs (mindelta) > abs (delta))
{
mindelta = delta;
}
}
//改变uicollectionview的偏移量
proposedcontentoffset.x += mindelta;
return proposedcontentoffset;
}
|
自定义圆形普通布局
定义成员属性,保存所有的布局属性
1
|
@property (nonatomic, strong) nsmutablearray *attrsarray;
|
懒加载,初始化attrsarray
1
2
3
4
5
6
7
8
|
- (nsmutablearray *)attrsarray
{
if (_attrsarray == nil)
{
_attrsarray = [nsmutablearray array];
}
return _attrsarray;
}
|
初始化布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
- ( void )preparelayout
{
[super preparelayout];
//移除所有旧的布局属性
[self.attrsarray removeallobjects];
//获取元素的个数
nsinteger count = [self.collectionview numberofitemsinsection:0];
//布局所有的元素
for (nsinteger i = 0; i<count; i++)
{
nsindexpath *indexpath = [nsindexpath indexpathforitem:i insection:0];
//设置并获取indexpath位置元素的布局属性
uicollectionviewlayoutattributes *attrs = [self layoutattributesforitematindexpath:indexpath];
//将indexpath位置元素的布局属性添加到所有布局属性数组中
[self.attrsarray addobject:attrs];
}
}
|
布局indexpath位置的元素
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
|
- (nullable uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nonnull nsindexpath *)indexpath
{
//获取元素的个数
nsinteger count = [self.collectionview numberofitemsinsection:0];
/**设置圆心布局*/
//设置圆形的半径
cgfloat radius = 70;
//圆心的位置
cgfloat ox = self.collectionview.frame.size.width * 0.5;
cgfloat oy = self.collectionview.frame.size.height * 0.5;
//获取indexpath位置的元素的布局属性
uicollectionviewlayoutattributes *attrs = [uicollectionviewlayoutattributes layoutattributesforcellwithindexpath:indexpath];
//设置尺寸
attrs.size = cgsizemake(50, 50);
//设置位置
if (count == 1)
{
attrs.center = cgpointmake(ox, oy);
}
else
{
cgfloat angle = (2 * m_pi / count) * indexpath.item;
cgfloat centerx = ox + radius * sin (angle);
cgfloat centery = oy + radius * cos (angle);
attrs.center = cgpointmake(centerx, centery);
}
//返回indexpath位置元素的布局属性
return attrs;
}
|
布局指定区域内所有的元素
1
2
3
4
5
|
- (nullable nsarray<uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect
{
//返回所有元素的布局属性
return self.attrsarray;
}
|
通过xib自定义ce ll
设置成员属性,保存cell内部的图片
/**图片名字*/
1
|
@property (nonatomic, copy) nsstring *imagename;
|
初始化cell
1
2
3
4
5
6
7
|
- ( void )awakefromnib
{
//通过layer设置边框
self.imageview.layer.bordercolor = [uicolor whitecolor].cgcolor;
self.imageview.layer.borderwidth = 6;
self.imageview.layer.cornerradius = 3;
}
|
设置cell内imageview的image属性
1
2
3
4
5
|
- ( void )setimagename:(nsstring *)imagename
{
_imagename = [imagename copy];
self.imageview.image = [uiimage imagenamed:imagename];
}
|
加载图片资源
通过成员属性,保存所有的图片名
1
2
|
/**所有的图片*/
@property (nonatomic, strong) nsmutablearray *imagenames;
|
懒加载,初始化图片名数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
- (nsmutablearray *)imagenames
{
if (_imagenames == nil)
{
nsmutablearray *imagenames = [nsmutablearray array];
for (nsinteger i = 0; i<20; i++)
{
nsstring *imagename = [nsstring stringwithformat:@ "%zd" , i + 1];
[imagenames addobject:imagename];
}
_imagenames = imagenames;
}
return _imagenames;
}
|
创建uicollectionview
通过成员属性保存uicollectionview对象,以便更改布局
1
|
@property (nonatomic, weak) uicollectionview *collectionview;
|
创建并设置collectionview
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
- ( void )setupcollectionview
{
//设置frame
cgfloat collectionvieww = self.view.bounds.size.width;
cgfloat collectionviewh = 200;
cgrect frame = cgrectmake(0, 150, collectionvieww, collectionviewh);
//创建布局
lypcirclelayout *layout = [[lypcirclelayout alloc] init];
//创建collectionview
uicollectionview *collectionview = [[uicollectionview alloc] initwithframe:frame collectionviewlayout:layout];
self.collectionview = collectionview;
//设置collectionview的数据源和代理
collectionview.datasource = self;
collectionview.delegate = self;
//添加collectionview到控制器的view中
[self.view addsubview:collectionview];
}
|
实现uicollectionview的数据源方法
注册cell
1
2
3
4
5
6
7
8
9
10
11
|
/**设置重用表示*/
static nsstring * const id = @ "photo" ;
- ( void )viewdidload
{
[super viewdidload];
[self setupcollectionview];
//注册cell
[self.collectionview registernib:[uinib nibwithnibname:nsstringfromclass([lypphotocell class ]) bundle:nil] forcellwithreuseidentifier:id];
}
|
设置元素的个数
1
2
3
4
|
- (nsinteger)collectionview:(nonnull uicollectionview *)collectionview numberofitemsinsection:(nsinteger)section
{
return self.imagenames.count;
}
|
设置每个元素的属性
1
2
3
4
5
6
7
8
9
10
|
- (uicollectionviewcell *)collectionview:(nonnull uicollectionview *)collectionview cellforitematindexpath:(nonnull nsindexpath *)indexpath
{
//根据重用标示从缓存池中取出cell,若缓存池中没有,则自动创建
lypphotocell *cell = [collectionview dequeuereusablecellwithreuseidentifier:id forindexpath:indexpath];
//设置cell的imagename属性
cell.imagename = self.imagenames[indexpath.item];
//返回cell
return cell;
}
|
实现uicollectionview的代理方法,实现点击某个元素将其删除功能
1
2
3
4
5
6
7
|
- ( void )collectionview:(nonnull uicollectionview *)collectionview didselectitematindexpath:(nonnull nsindexpath *)indexpath
{
//将图片名从数组中移除
[self.imagenames removeobjectatindex:indexpath.item];
//删除collectionview中的indexpath位置的元素
[self.collectionview deleteitemsatindexpaths:@[indexpath]];
}
|
监听控制器view的点击,更换布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
- ( void )touchesbegan:(nonnull nsset<uitouch *> *)touches withevent:(nullable uievent *)event
{
//判断当前布局的种类
if ([self.collectionview.collectionviewlayout iskindofclass:[lyplinelayout class ]])
{
//流水布局,切换至圆形布局
[self.collectionview setcollectionviewlayout:[[lypcirclelayout alloc] init] animated:yes];
} else
{
//圆形布局,切换至流水布局
lyplinelayout *layout = [[lyplinelayout alloc] init];
//设置元素的尺寸,若不设置,将使用自动计算尺寸
layout.itemsize = cgsizemake(130, 130);
[self.collectionview setcollectionviewlayout:layout animated:yes];
}
}
|
以上就是本文的全部内容,希望对大家的学习有所帮助。