实现瀑布流布局的方式中利用“UICollectionView”与自定义“UICollectionViewLayout”的方式在内存利用、流畅度、实现难易度等方面都比较优秀
内存利用方面通过UICollectionView的cell复用可以有效的减少内存占用,流畅度也是表现的很出色,实现所需要的代码量也很少
WaterfallLayout
首先介绍一下自定义的布局方式,这个布局与“UICollectionViewFlowLayout”很相似,因此行间距、列间距、内边距都可以直接拿过来用,需要自己新定义这些属性。另外需要一些必须的属性,列数、属性数组,列高数组,为方便也要定义一下列宽(cell宽度)和代理(获取cell高度)属性,其中列宽与列间距、UICollectionView宽度、列数、内边距有关系,所以在这些值改变的时候记得在set方法中改变列宽。
在重写的“prepare”方法中初步设置列宽以及获取所有cell的布局属性。此时需要重写“func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?”,“func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?”方法和“func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?”方法,第一个方法就是在设置每一个cell的布局属性的时候,第二个方法是返回rect范围内所有元素的布局属性,包含cell、组视图等的布局属性.利用一个代理方法直接获取每个item的高度
屏幕快照 2018-01-25 18.03.25.png
import UIKit
protocol WaterfallLayoutDelegate {
func itemHeightForIndexPath(indexpath : IndexPath) -> CGFloat?
}
class WaterfallLayout: UICollectionViewLayout {
//行间距
var minimumLineSpacing: CGFloat = 0.0{
didSet{
//设置item的宽度
self.setUpItemWidth()
}
}
//列间距
var minimumInteritemSpacing: CGFloat = 0.0{
didSet{
//设置item的宽度
self.setUpItemWidth()
}
}
var scrollDirection: UICollectionViewScrollDirection = .vertical // default is UICollectionViewScrollDirectionVertical
fileprivate var item_w : CGFloat = 0//item宽度
//内边距
var sectionInset: UIEdgeInsets = UIEdgeInsets.zero{
didSet{
//设置item的宽度
self.setUpItemWidth()
}
}
//列数,默认2
var columnsNum = 2{
didSet{
//设置列高
self.columnsHeightArray.removeAll()
for _ in 0...self.columnsNum{
//如果数量不对则全部设置为0
self.columnsHeightArray.append(0)
}
//设置item的宽度
self.setUpItemWidth()
}
}
var delegate : WaterfallLayoutDelegate?
fileprivate var attrArray : Array<UICollectionViewLayoutAttributes> = Array<UICollectionViewLayoutAttributes>()//属性数组
fileprivate var columnsHeightArray : Array<CGFloat> = [0,0]//每列的高度
//设置每一个item的属性
func setAttrs() {
guard let secNum = self.collectionView?.numberOfSections else {
return
}
for i in 0...secNum-1{
for i in 0...self.columnsNum - 1{
self.columnsHeightArray[i] = self.getLongValue()
}
self.attrArray.append(self.layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: IndexPath.init(row: 0, section: i))!)
guard let itemsNum = self.collectionView?.numberOfItems(inSection: i) else {
return
}
for j in 0...itemsNum - 1{
self.attrArray.append(self.layoutAttributesForItem(at: IndexPath.init(row: j, section: i))!)
}
}
}
//获取最短列的索引
func getShortesIndex() -> Int {
var index = 0
for i in 0...self.columnsNum - 1{
if self.columnsHeightArray[index] > self.columnsHeightArray[I]{
index = I
}
}
return index
}
//获取最长列的值
func getLongValue() -> CGFloat {
var value : CGFloat = 0
for i in 0...self.columnsNum - 1{
if value < self.columnsHeightArray[I]{
value = self.columnsHeightArray[I]
}
}
return value
}
//设置每列的宽度
func setUpItemWidth(){
guard let collectionWidth = self.collectionView?.frame.size.width else {
return
}
self.item_w = (collectionWidth - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((self.columnsNum - 1))) / CGFloat(self.columnsNum)
}
override var collectionViewContentSize: CGSize{
get{
return CGSize.init(width: 0, height: self.getLongValue() + self.sectionInset.top + self.sectionInset.bottom)
}
}
override func prepare() {
super.prepare()
self.setUpItemWidth()
self.setAttrs()
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attr = UICollectionViewLayoutAttributes.init(forCellWith: indexPath)
let shortesIndex = self.getShortesIndex()
let item_x = self.sectionInset.left + (self.item_w + self.minimumInteritemSpacing) * CGFloat(shortesIndex)
let item_y = self.columnsHeightArray[shortesIndex] + self.sectionInset.top
let item_h = self.delegate?.itemHeightForIndexPath(indexpath: indexPath) ?? 0
attr.frame = CGRect.init(x: item_x, y: item_y , width: self.item_w, height: item_h)
//更新列高数组
self.columnsHeightArray[shortesIndex] += (item_h + self.minimumLineSpacing)
return attr
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attr = UICollectionViewLayoutAttributes.init(forSupplementaryViewOfKind: elementKind, with: indexPath)
let header_x : CGFloat = self.sectionInset.left
let header_y : CGFloat = self.columnsHeightArray[0] + self.minimumLineSpacing + self.sectionInset.top
let header_w = self.item_w * CGFloat(self.columnsNum) + self.minimumInteritemSpacing * CGFloat(self.columnsNum - 1)
let header_h : CGFloat = 40
attr.frame = CGRect.init(x: header_x, y: header_y, width: header_w, height: header_h)
for i in 0...self.columnsNum - 1{
self.columnsHeightArray[i] += (header_h + self.minimumLineSpacing)
}
return attr
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return self.attrArray
}
}
WaterfallViewController
就是普通的collectionview的实现
//
// WaterfallViewController.swift
// qixiaofu
//
// Created by ly on 2018/1/24.
// Copyright © 2018年 qixiaofu. All rights reserved.
//
import UIKit
private let reuseIdentifier = "Cell"
class WaterfallViewController: UIViewController {
fileprivate var collectionView : UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.setUpCollectionView()
}
func setUpCollectionView() {
let layout = WaterfallLayout()
layout.delegate = self
layout.sectionInset = UIEdgeInsets.init(top: 10, left: 10, bottom: 10, right: 10)
layout.minimumInteritemSpacing = 10
layout.minimumLineSpacing = 5
self.collectionView = UICollectionView.init(frame: self.view.bounds, collectionViewLayout: layout)
self.collectionView.delegate = self
self.collectionView.dataSource = self
self.view.addSubview(self.collectionView)
self.collectionView.backgroundColor = UIColor.blue
self.collectionView.register(UINib.init(nibName: "WaterfallCell", bundle: Bundle.main), forCellWithReuseIdentifier: "WaterfallCell")
self.collectionView.register(WaterfallReusableView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "WaterfallReusableView")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
extension WaterfallViewController : UICollectionViewDelegate, UICollectionViewDataSource{
// MARK: UICollectionViewDataSource
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WaterfallCell", for: indexPath)
cell.backgroundColor = UIColor.red
// Configure the cell
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let reusedView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "WaterfallReusableView", for: indexPath)
reusedView.backgroundColor = UIColor.green
return reusedView
}
}
extension WaterfallViewController : WaterfallLayoutDelegate{
func itemHeightForIndexPath(indexpath: IndexPath) -> CGFloat? {
return CGFloat(arc4random() % UInt32(100)) + 10.0
}
}
class WaterfallReusableView: UICollectionReusableView {
}
网友评论