我们用纯代码实现一下瀑布流:
AppDelegate:
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: (UIScreen.mainScreen().bounds))
window?.rootViewController = WaterFlowViewController()
window?.makeKeyAndVisible()
return true
}
- 搞一个随机色:
extension UIColor {
//随机颜色
class func randomColor() ->UIColor {
return UIColor(red: randomValue(), green: randomValue(), blue: randomValue(), alpha: 1)
}
class func randomValue()->CGFloat {
return CGFloat(arc4random_uniform(256))/255
}
}
- 自定义布局:
protocol WaterFlowViewLayoutDelegate:NSObjectProtocol
{
//collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath)
///width是瀑布流每列的宽度
func waterFlowViewLayout(waterFlowViewLayout:WaterFlowViewLayout,heightForWidth:CGFloat,atIndextPath:NSIndexPath)->CGFloat
}
class WaterFlowViewLayout: UICollectionViewLayout {
weak var delegate:WaterFlowViewLayoutDelegate?
///所有cell的布局属性
var layoutAttributes = [UICollectionViewLayoutAttributes]()
///使用一个字典记录每列的最大Y值
var maxYDict = [Int:CGFloat]()
static var Margin:CGFloat = 8
///瀑布流四周的间距
var sectionInsert = UIEdgeInsets(top: Margin, left: Margin, bottom: Margin, right: Margin)
//列间距
var columnMargin:CGFloat = Margin
//行间距
var rowMargin:CGFloat = Margin
///瀑布流列数
var column = 4
var maxY:CGFloat = 0
var columnWidth:CGFloat = 0
///prepareLayout会在调用collectionView.reloadData()
override func prepareLayout() {
//设置布局
//需要清空字典里面的值
for key in 0..<column
{
maxYDict[key] = 0
}
//清空之前的布局属性
layoutAttributes.removeAll()
//清空最大列的Y值
maxY = 0
///清空列宽
columnWidth = 0
//计算每列的宽度,需要在布局之前算好
columnWidth = (UIScreen.mainScreen().bounds.width - sectionInsert.left - sectionInsert.right - (CGFloat(column) - 1)*columnMargin)/CGFloat(column)
let number = collectionView?.numberOfItemsInSection(0) ?? 0
for i in 0..<number
{
//布局每一个cell的frame
let layoutAttr = layoutAttributesForItemAtIndexPath(NSIndexPath(forItem: i, inSection: 0))!
layoutAttributes.append(layoutAttr)
}
calcMaxY()
}
func calcMaxY(){
//获取最大这一列的Y
//默认第0列最长
var maxYCoulumn = 0
//for 循环比较,获取最长的这列
for (key,value) in maxYDict
{
if value > maxYDict[maxYCoulumn]{
//key这列的Y值是最大的
maxYCoulumn = key
}
}
//获取到Y值最大的这一列
maxY = maxYDict[maxYCoulumn]! + sectionInsert.bottom
}
//返回collectionViewContentSize 大小
override func collectionViewContentSize() -> CGSize {
return CGSize(width: UIScreen.mainScreen().bounds.width, height: maxY)
}
// 返回每一个cell的布局属性(layoutAttributes)
// UICollectionViewLayoutAttributes: 1.cell的frame 2.indexPath
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes?
{
assert(delegate != nil,"瀑布流必须实现代理来返回cell的高度")
let height = delegate!.waterFlowViewLayout(self, heightForWidth: columnWidth, atIndextPath: indexPath)
// 找到最短的那一列,去maxYDict字典中找
// 最短的这一列
var minYColumn = 0
//通过for循环去和其他列比较
for(key, value) in maxYDict {
if value < maxYDict[minYColumn]
{
minYColumn = key
}
}
// minYColumn 就是短的那一列
let x = sectionInsert.left + CGFloat(minYColumn) * (columnWidth + columnMargin)
//最短这列的Y值 + 行间距
let y = maxYDict[minYColumn]! + rowMargin
//设置cell的frame
let frame = CGRect(x: x, y: y, width: columnWidth, height: height)
//更新最短这列的最大Y值
maxYDict[minYColumn] = CGRectGetMaxY(frame)
//创建每个cell对应的布局属性
let layoutAttr = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
layoutAttr.frame = frame
return layoutAttr
}
//预加载下一页数据
override func layoutAttributesForElementsInRect(rect:CGRect) -> [UICollectionViewLayoutAttributes]{
return layoutAttributes
}
}
- 一个类继承UICollectionViewController,遵循:WaterFlowViewLayoutDelegate协议
class WaterFlowViewController: UICollectionViewController,WaterFlowViewLayoutDelegate {
let ReuseIdentifier = "ReuseIdentifier"
let layout = WaterFlowViewLayout()
init(){
super.init(collectionViewLayout: layout)
}
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor.whiteColor()
collectionView?.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: ReuseIdentifier)
layout.delegate = self
}
func waterFlowViewLayout(waterFlowViewLayout: WaterFlowViewLayout, heightForWidth: CGFloat, atIndextPath: NSIndexPath) -> CGFloat {
return CGFloat(100 + arc4random_uniform(50))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 500
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(ReuseIdentifier, forIndexPath: indexPath)
cell.backgroundColor = UIColor.randomColor()
return cell
}
}
其实就是一个自定义布局 算术的问题,看一下效果: