转载注明出处: http://blog.csdn.net/qxuewei/article/details/53490560
在项目中偶尔会用到瀑布流的布局,目前多数直播类软件,信息展示类软件等
接下来分析一种简单的实现方法,利用swift3实现.
在实现这种瀑布流首先想到的是使用UICollectionView实现,类似于不规则的流水布局.我们可以通过自定义 UICollectionViewFlowLayout ,将collectionView的item通过我们想要的格式显示
核心代码:
自定义UICollectionViewFlowLayout 类 XWWaterFallLayout
//
// XWWaterFallLayout.swift
// XWWaterFallSwiftDemo
//
// Created by 邱学伟 on 2016/12/6.
// Copyright © 2016年 邱学伟. All rights reserved.
//
import UIKit
//MARK: - 数据源协议
protocol XWWaterFallLayoutDataSource : class {
func numberOfCols(_waterFallLayout : XWWaterFallLayout) -> Int
func itemHeight(_waterFallLayout : XWWaterFallLayout, item : Int) -> CGFloat
}
class XWWaterFallLayout: UICollectionViewFlowLayout {
weak var dataSource : XWWaterFallLayoutDataSource?
fileprivate lazy var cellAttributes : [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()
fileprivate lazy var cols : Int = {
return self.dataSource?.numberOfCols(_waterFallLayout: self) ?? 2
}()
fileprivate lazy var totalHeight : [CGFloat] = Array(repeating: 0.0, count: self.cols)
}
//MARK: - 准备布局
extension XWWaterFallLayout {
override func prepare() {
super.prepare()
let itemCount : Int = collectionView!.numberOfItems(inSection: 0)
//为每个cell创建 UICollectionViewLayoutAttributes(决定cell位置)
let width : CGFloat = (collectionView!.bounds.width - sectionInset.left - sectionInset.right - CGFloat(cols - 1) * minimumInteritemSpacing) / CGFloat(cols)
for i in 0..<itemCount {
let indexPath : IndexPath = IndexPath(item: i, section: 0)
let attributes : UICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
let minH : CGFloat = totalHeight.min()!
let minIndex = totalHeight.index(of: minH)!
let X : CGFloat = sectionInset.left + (minimumInteritemSpacing + width) * CGFloat(minIndex)
let Y : CGFloat = minH + minimumLineSpacing
guard let height : CGFloat = dataSource?.itemHeight(_waterFallLayout: self, item: i) else {
fatalError("请遵守数据源实现对应方法返回cell高度")
}
attributes.frame = CGRect(x: X, y: Y, width: width, height: height)
cellAttributes.append(attributes)
totalHeight[minIndex] = minH + minimumLineSpacing + height
}
}
}
//MARK: - 返回布局
extension XWWaterFallLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return cellAttributes
}
}
//MARK: - 设置ContentSize
extension XWWaterFallLayout {
override var collectionViewContentSize: CGSize{
return CGSize(width: collectionView!.bounds.width, height: totalHeight.max()! + sectionInset.bottom)
}
}
在使用中,直接在定义collectionView中使用自定义Layout
//通过自定义layout改变collectionView的布局
let layout : XWWaterFallLayout = XWWaterFallLayout()
let itemMargin : CGFloat = 10
layout.minimumLineSpacing = itemMargin
layout.minimumInteritemSpacing = itemMargin
layout.sectionInset = UIEdgeInsets(top: itemMargin, left: itemMargin, bottom: itemMargin, right: itemMargin)
layout.dataSource = self
let collectionView : UICollectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
collectionView.dataSource = self
需要注意的是,所封装的Layout需要外界传入item的行数和每个item的高度.需要实现自定义layout的数据源方法
//MARK: - XWWaterFallLayoutDataSource数据源方法
extension ViewController : XWWaterFallLayoutDataSource {
func numberOfCols(_waterFallLayout: XWWaterFallLayout) -> Int {
return 4
}
func itemHeight(_waterFallLayout: XWWaterFallLayout, item: Int) -> CGFloat {
return CGFloat(arc4random_uniform(200) + 100)
}
}
项目完整Demo链接:https://github.com/qxuewei/XWWaterFallSwift