IOS实现自定义布局瀑布流
瀑布流是电商应用展示商品通常采用的一种方式,如图示例
瀑布流的实现方式,通常有以下几种
- 通过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、自定义布局常用方法
初始化布局
- (void)preparelayout { //通常在该方法中完成布局的初始化操作 }
当尺寸改变时,是否更新布局
- (bool)shouldinvalidatelayoutforboundschange:(cgrect)newbounds { //默认返回yes }
布局uicollectionview的元素
- (nullable nsarray<__kindof uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect { //该方法需要返回rect区域中所有元素布局属性的数组 } - (nullable uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nsindexpath *)indexpath { //该方法返回indexpath位置的元素的布局属性 }
修改uicollectionview停止滚动是的偏移量
- (cgpoint)targetcontentoffsetforproposedcontentoffset:(cgpoint)proposedcontentoffset withscrollingvelocity:(cgpoint)velocity { //返回值是,uicollectionview最终停留的点 }
三、示例
1、实现效果
2、实现思路
- 通过uicollectionview实现
- 自定义布局,即横向流水布局和圆形普通布局
- 通过uicollectionview的代理方法,当点击cell时,将其删除
- 通过监听uiview的触摸事件,当点解控制器的view时更改布局
3、实现步骤
自定横向流水布局
初始化布局
- (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); }
指定当尺寸改变时,更新布局
- (bool)shouldinvalidatelayoutforboundschange:(cgrect)newbounds { return yes; }
设置所有元素的布局属性
- (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停止滚动是的偏移量,使其为与中心点
- (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; }
自定义圆形普通布局
定义成员属性,保存所有的布局属性
@property (nonatomic, strong) nsmutablearray *attrsarray;
懒加载,初始化attrsarray
- (nsmutablearray *)attrsarray { if (_attrsarray == nil) { _attrsarray = [nsmutablearray array]; } return _attrsarray; }
初始化布局
- (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位置的元素
- (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; }
布局指定区域内所有的元素
- (nullable nsarray<uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect { //返回所有元素的布局属性 return self.attrsarray; }
通过xib自定义ce ll
设置成员属性,保存cell内部的图片
/**图片名字*/
@property (nonatomic, copy) nsstring *imagename;
初始化cell
- (void)awakefromnib { //通过layer设置边框 self.imageview.layer.bordercolor = [uicolor whitecolor].cgcolor; self.imageview.layer.borderwidth = 6; self.imageview.layer.cornerradius = 3; }
设置cell内imageview的image属性
- (void)setimagename:(nsstring *)imagename { _imagename = [imagename copy]; self.imageview.image = [uiimage imagenamed:imagename]; }
加载图片资源
通过成员属性,保存所有的图片名
/**所有的图片*/ @property (nonatomic, strong) nsmutablearray *imagenames;
懒加载,初始化图片名数组
- (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对象,以便更改布局
@property (nonatomic, weak) uicollectionview *collectionview;
创建并设置collectionview
- (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
/**设置重用表示*/ static nsstring *const id = @"photo"; - (void)viewdidload { [super viewdidload]; [self setupcollectionview]; //注册cell [self.collectionview registernib:[uinib nibwithnibname:nsstringfromclass([lypphotocell class]) bundle:nil] forcellwithreuseidentifier:id]; }
设置元素的个数
- (nsinteger)collectionview:(nonnull uicollectionview *)collectionview numberofitemsinsection:(nsinteger)section { return self.imagenames.count; }
设置每个元素的属性
- (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的代理方法,实现点击某个元素将其删除功能
- (void)collectionview:(nonnull uicollectionview *)collectionview didselectitematindexpath:(nonnull nsindexpath *)indexpath { //将图片名从数组中移除 [self.imagenames removeobjectatindex:indexpath.item]; //删除collectionview中的indexpath位置的元素 [self.collectionview deleteitemsatindexpaths:@[indexpath]]; }
监听控制器view的点击,更换布局
- (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]; } }
以上就是本文的全部内容,希望对大家的学习有所帮助。