iOS开发进阶 - 自定义UICollectionViewLayout实现瀑布流布局
移动端访问不佳,请访问我的个人博客
最近项目中需要用到瀑布流的效果,但是用UICollectionViewFlowLayout又达不到效果,自己动手写了一个瀑布流的layout,下面是我的心路路程
先先上效果图与demo地址:
因为是用UICollectionView来实现瀑布流的,决定继承UICollectionViewLayout来自定义一个layout来实现一个简单瀑布流的布局,下面是需要重写的方法:
- 重写这个属性得出UICollectionView的ContentSize:
collectionViewContentSize
- 重写这个方法来得到每个item的布局:
layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
- 重写这个方法给UICollectionView所有item的布局:
layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
- 重写这个方法来实现UICollectionView前的操作:
prepare()
实现思路
通过代理模式获得到需要的列数和每一item的高度,用过列数与列之间的间隔和UICollectionView的宽度来得出每一列的宽度,item从左边到右布局,下一列的item放到高度最小的列下面,防止每列的高度不均匀,下面贴上代码和注释:
import UIKit
@objc protocol WCLWaterFallLayoutDelegate {
//waterFall的列数
func columnOfWaterFall(_ collectionView: UICollectionView) -> Int
//每个item的高度
func waterFall(_ collectionView: UICollectionView, layout waterFallLayout: WCLWaterFallLayout, heightForItemAt indexPath: IndexPath) -> CGFloat
}
class WCLWaterFallLayout: UICollectionViewLayout {
//代理
weak var delegate: WCLWaterFallLayoutDelegate?
//行间距
@IBInspectable var lineSpacing: CGFloat = 0
//列间距
@IBInspectable var columnSpacing: CGFloat = 0
//section的top
@IBInspectable var sectionTop: CGFloat = 0 {
willSet {
sectionInsets.top = newValue
}
}
//section的Bottom
@IBInspectable var sectionBottom: CGFloat = 0 {
willSet {
sectionInsets.bottom = newValue
}
}
//section的left
@IBInspectable var sectionLeft: CGFloat = 0 {
willSet {
sectionInsets.left = newValue
}
}
//section的right
@IBInspectable var sectionRight: CGFloat = 0 {
willSet {
sectionInsets.right = newValue
}
}
//section的Insets
@IBInspectable var sectionInsets: UIEdgeInsets = UIEdgeInsets.zero
//每行对应的高度
private var columnHeights: [Int: CGFloat] = [Int: CGFloat]()
private var attributes: [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()
//MARK: Initial Methods
init(lineSpacing: CGFloat, columnSpacing: CGFloat, sectionInsets: UIEdgeInsets) {
super.init()
self.lineSpacing = lineSpacing
self.columnSpacing = columnSpacing
self.sectionInsets = sectionInsets
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
//MARK: Public Methods
//MARK: Override
override var collectionViewContentSize: CGSize {
var maxHeight: CGFloat = 0
for height in columnHeights.values {
if height > maxHeight {
maxHeight = height
}
}
return CGSize.init(width: collectionView?.frame.width ?? 0, height: maxHeight + sectionInsets.bottom)
}
override func prepare() {
super.prepare()
guard collectionView != nil else {
return
}
if let columnCount = delegate?.columnOfWaterFall(collectionView!) {
for i in 0..<columnCount {
columnHeights[i] = sectionInsets.top
}
}
let itemCount = collectionView!.numberOfItems(inSection: 0)
attributes.removeAll()
for i in 0..<itemCount {
if let att = layoutAttributesForItem(at: IndexPath.init(row: i, section: 0)) {
attributes.append(att)
}
}
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
if let collectionView = collectionView {
//根据indexPath获取item的attributes
let att = UICollectionViewLayoutAttributes.init(forCellWith: indexPath)
//获取collectionView的宽度
let width = collectionView.frame.width
if let columnCount = delegate?.columnOfWaterFall(collectionView) {
guard columnCount > 0 else {
return nil
}
//item的宽度 = (collectionView的宽度 - 内边距与列间距) / 列数
let totalWidth = (width - sectionInsets.left - sectionInsets.right - (CGFloat(columnCount) - 1) * columnSpacing)
let itemWidth = totalWidth / CGFloat(columnCount)
//获取item的高度,由外界计算得到
let itemHeight = delegate?.waterFall(collectionView, layout: self, heightForItemAt: indexPath) ?? 0
//找出最短的那一列
var minIndex = 0
for column in columnHeights {
if column.value < columnHeights[minIndex] ?? 0 {
minIndex = column.key
}
}
//根据最短列的列数计算item的x值
let itemX = sectionInsets.left + (columnSpacing + itemWidth) * CGFloat(minIndex)
//item的y值 = 最短列的最大y值 + 行间距
let itemY = (columnHeights[minIndex] ?? 0) + lineSpacing
//设置attributes的frame
att.frame = CGRect.init(x: itemX, y: itemY, width: itemWidth, height: itemHeight)
//更新字典中的最大y值
columnHeights[minIndex] = att.frame.maxY
}
return att
}
return nil
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attributes
}
}
最后附带demo地址,大家喜欢的话可以star一下
上面是简单的瀑布流的实现过程,希望大家能学到东西,有很多地方考虑的不足,欢迎大家交流学习,谢谢大家的阅读~~
iOS开发进阶 - 自定义UICollectionViewLayout实现瀑布流布局的更多相关文章
- iOS:UICollectionView纯自定义的布局:瀑布流布局
创建瀑布流有三种方式: 第一种方式:在一个ScrollView里面放入三个单元格高度一样的tableView,禁止tableView滚动,只需让tableView随着ScrollView滚动即可. ...
- iOS开发进阶
<iOS开发进阶>基本信息作者: 唐巧 出版社:电子工业出版社ISBN:9787121247453上架时间:2014-12-26出版日期:2015 年1月开本:16开页码:268版次:1- ...
- iOS开发进阶(唐巧)读书笔记(一)
如何提高iOS开发技能 1.阅读博客:https://github.com/tangqiaoboy/iOSBlogCN 40多位iOS开发博主的博客地址 2.读书:每年阅读一本高质量的iOS开发书籍 ...
- iOS开发之自定义表情键盘(组件封装与自动布局)
下面的东西是编写自定义的表情键盘,话不多说,开门见山吧!下面主要用到的知识有MVC, iOS开发中的自动布局,自定义组件的封装与使用,Block回调,CoreData的使用.有的小伙伴可能会问写一个自 ...
- 详解iOS开发之自定义View
iOS开发之自定义View是本文要将介绍的内容,iOS SDK中的View是UIView,我们可以很方便的自定义一个View.创建一个 Window-based Application程序,在其中添加 ...
- Xamarin自定义布局系列——瀑布流布局
Xamarin.Forms以Xamarin.Android和Xamarin.iOS等为基础,自己实现了一整套比较完整的UI框架,包含了绝大多数常用的控件,如下图 虽然XF(Xamarin.Forms简 ...
- 【Swift】IOS开发中自定义转场动画
在IOS开发中,我们model另外一个控制器的时候,一般都使用默认的转场动画. 其实我们可以自定义一些转场动画.达到不同的转场效果. 步骤如下:(photoBrowser是目标控制器) 1.在源控制器 ...
- iOS开发进阶--1.多线程简介
学习是由已知的知识模型推理未知的知识模型的的过程. 本文适合学习完objective-c基础,想进一步提高做iOS开发的同学阅读. 在说线程的时候,我们先看看进程. 1.进程 每一个运行在系统中的应用 ...
- 自定义UICollectionViewLayout 实现瀑布流
今天研究了一下自定义UICollectionViewLayout. 看了看官方文档,要自定义UICollectionViewLayout,需要创建一个UICollectionViewLayout的子类 ...
随机推荐
- Python全栈day28(类的装饰器)
类是一个对象也可以像函数一样加装饰器 类的装饰器.py def deco(obj): print('======',obj) obj.x=1 obj.y=2 obj.z=3 return obj # ...
- Windows配置MinGW环境变量
先安装MinGW 1.添加3个系统变量(根据自己的实际路径) MinGW_INCLUDE_PATH MinGW_LIBRARY_PATH MinGW_PATH 2.将MinGW_PATH添加到Path ...
- 20165330 2017-2018-2 《Java程序设计》第1周学习总结
教材学习内容总结 java的历史,地位,特点. java的平台介绍 java应用程序的开发及源文件的编写规则 java反编译特点 安装JDK Windows上 在安装JDK后设置系统环境变量,因为我的 ...
- Exchange Management Console Error
Exchange 2010 error solved : The WS-Management service cannot process the request. The system load q ...
- 用于把List<Object>转换成Map<String,Object>形式
/** * 用于把List<Object>转换成Map<String,Object>形式,便于存入缓存 * @author zhang_bo * @param keyName ...
- python and 我爱自然语言处理
曾经因为NLTK的 缘故开始学习Python,之后渐渐成为我工作中的第一辅助脚本语言,虽然开发语言是C/C++,但平时的很多文本数据处理任务都交给了Python.离 开腾讯创业后,第一个作品课程图谱也 ...
- python在处理CSV文件时,字符串和列表写入的区别
概述 Python在处理CSV文件时,如果writerow的对象是<type 'unicode'>字符串时,写入到CSV文件时将会出现一个字符占一个单元格的情况: 但是将字符串转换为列表类 ...
- yum install mysql on centos 6.5 zz
http://www.cnblogs.com/xiaoluo501395377/archive/2013/04/07/3003278.html 1.使用yum命令进行mysql的安装 yum list ...
- Yii 2.x 和1.x区别以及yii2.0安装
知乎上有个类似的问题:http://www.zhihu.com/question/22924271/answer/23085751 大致思路不会变,开发流程变化也不是很大.有变化的是1.yii2带入的 ...
- 5、RabbitMQ - Exchange之 fanout \ 【direct 关键字发送】 \ topic
pytho系列之 RabbitMQ - Exchange几种模式 RabbitMQ中,所有生产者提交的消息都由Exchange来接受,然后Exchange按照特定的策略转发到Queue进行存储 Rab ...