iOS瀑布流的簡(jiǎn)單實(shí)現(xiàn)(Swift)
這段時(shí)間突然想到一個(gè)很久之前用到的知識(shí)-瀑布流,本來(lái)想用一個(gè)簡(jiǎn)單的方法,發(fā)現(xiàn)自己走入了歧途,最終只能狠下心來(lái)重寫(xiě)UICollectionViewFlowLayout.下面我將用兩種方法實(shí)現(xiàn)瀑布流,以及會(huì)介紹第一種實(shí)現(xiàn)的bug.
<1>第一種
效果圖如下所示:

這種實(shí)現(xiàn)方法的思路:
1)首先調(diào)用隨機(jī)函數(shù),產(chǎn)生隨機(jī)高度,并把它保存到數(shù)組中
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat cellW = 100;
CGFloat cellH = 100 + (arc4random() % 80);
[self.heightArrayM addObject:@(cellH)];
return CGSizeMake(cellW, cellH);
}
2)在設(shè)置cell的frame的地方,通過(guò)取余,取整確定cell的高度,并設(shè)定cell的frame
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
//當(dāng)前處于多少行
NSInteger num1 = indexPath.row / count;
//當(dāng)前處于多少列
int num2 = indexPath.row % count;
CGFloat cellX = num2 * 100 + (num2 + 1) * margin;
CGFloat cellY = 0;
for (int i = 0; i < num1; i++) {
NSInteger position = num2 + i * 3;
cellY += [self.heightArrayM[position] floatValue] + margin;
}
CGFloat cellW = 100;
CGFloat cellH = cellHeight;
cell.frame = CGRectMake(cellX, cellY, cellW, cellH);
// cell.backgroundColor = [UIColor redColor];
cell.backgroundColor = [UIColor colorWithRed:(arc4random() % 250) / 250.0 green:(arc4random() % 250) / 250.0 blue:(arc4random() % 250) / 250.0 alpha:1.0];
// NSLog(@"%@", NSStringFromCGRect(cell.frame));
return cell;
}
弊端 : 其實(shí)這種方法的弊端,相信從上面的動(dòng)態(tài)圖中可以看出來(lái),當(dāng)往上面滑的時(shí)候,由于cell的循環(huán)機(jī)制,下面的cell的會(huì)消失,但是由于高度不一致,同時(shí)撤銷的是最后一行的cell,所以下面的cell在屏幕上就會(huì)消失.
下面附上第一種方法的源代碼:
#import "ViewController.h"
#define margin 10
#define count 3
#define cellHeight [self.heightArrayM[indexPath.row] floatValue]
static NSString * const ID = @"cell";
@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray *heightArrayM;
@end
@implementation ViewController
- (NSMutableArray *)heightArrayM {
if (_heightArrayM == nil) {
_heightArrayM = [NSMutableArray array];
}
return _heightArrayM;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:ID];
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
//設(shè)置collectionView
[self setupCollectionView];
}
//設(shè)置collectionView的布局
- (UICollectionViewFlowLayout *)setupCollectionLayout {
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.minimumInteritemSpacing = margin;
flowLayout.minimumLineSpacing = margin;
flowLayout.sectionInset = UIEdgeInsetsMake(margin, margin, margin, margin);
return flowLayout;
}
//設(shè)置collectionView
- (void)setupCollectionView {
self.collectionView.collectionViewLayout =[self setupCollectionLayout];
}
#pragma mark - UICollectionViewDataSouce
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 60;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
//當(dāng)前處于多少行
NSInteger num1 = indexPath.row / count;
//當(dāng)前處于多少列
int num2 = indexPath.row % count;
CGFloat cellX = num2 * 100 + (num2 + 1) * margin;
CGFloat cellY = 0;
for (int i = 0; i < num1; i++) {
NSInteger position = num2 + i * 3;
cellY += [self.heightArrayM[position] floatValue] + margin;
}
CGFloat cellW = 100;
CGFloat cellH = cellHeight;
cell.frame = CGRectMake(cellX, cellY, cellW, cellH);
// cell.backgroundColor = [UIColor redColor];
cell.backgroundColor = [UIColor colorWithRed:(arc4random() % 250) / 250.0 green:(arc4random() % 250) / 250.0 blue:(arc4random() % 250) / 250.0 alpha:1.0];
// NSLog(@"%@", NSStringFromCGRect(cell.frame));
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
CGFloat cellW = 100;
CGFloat cellH = 100 + (arc4random() % 80);
[self.heightArrayM addObject:@(cellH)];
return CGSizeMake(cellW, cellH);
}
@end
<2>下面介紹第二種(Swift實(shí)現(xiàn))
效果圖如下所示:

這種實(shí)現(xiàn)方法就是比較成熟的了,我把它封裝成一個(gè)類.其實(shí)主要是實(shí)現(xiàn)三個(gè)函數(shù)
1)重寫(xiě)父類的prepare方法,準(zhǔn)備所有cell的樣式
extension WaterfallLayout {
// prepare準(zhǔn)備所有Cell的布局樣式
override func prepare() {
super.prepare()
// 0.獲取item的個(gè)數(shù)
let itemCount = collectionView!.numberOfItems(inSection: 0)
// 1.獲取列數(shù)
let cols = dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2
// 2.計(jì)算Item的寬度
let itemW = (collectionView!.bounds.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((cols - 1))) / CGFloat(cols)
// 3.計(jì)算所有的item的屬性
for i in startIndex..<itemCount {
// 1.設(shè)置每一個(gè)Item位置相關(guān)的屬性
let indexPath = IndexPath(item: i, section: 0)
// 2.根據(jù)位置創(chuàng)建Attributes屬性
let attrs = UICollectionViewLayoutAttributes(forCellWith: indexPath)
// 3.隨機(jī)一個(gè)高度
guard let height = dataSource?.waterfallLayout(self, indexPath: indexPath) else {
fatalError("請(qǐng)?jiān)O(shè)置數(shù)據(jù)源,并且實(shí)現(xiàn)對(duì)應(yīng)的數(shù)據(jù)源方法")
}
// 4.取出最小列的位置
var minH = colHeights.min()!
let index = colHeights.index(of: minH)!
minH = minH + height + minimumLineSpacing
colHeights[index] = minH
// 5.設(shè)置item的屬性
attrs.frame = CGRect(x: self.sectionInset.left + (self.minimumInteritemSpacing + itemW) * CGFloat(index), y: minH - height - self.minimumLineSpacing, width: itemW, height: height)
attrsArray.append(attrs)
}
// 4.記錄最大值
maxH = colHeights.max()!
// 5.給startIndex重新復(fù)制
startIndex = itemCount
}
}
2)返回設(shè)置cell樣式的數(shù)組
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attrsArray
}
3)返回當(dāng)前的contentSize
override var collectionViewContentSize: CGSize {
return CGSize(width: 0, height: maxH + sectionInset.bottom - minimumLineSpacing)
}
總結(jié):
在下面我封裝的這個(gè)類中,只需要遵守我的數(shù)據(jù)代理源協(xié)議并且實(shí)現(xiàn)我的協(xié)議中的兩個(gè)方法,傳給我對(duì)應(yīng)得高度(我這里是傳的隨機(jī)的),可選的方法,若是不實(shí)現(xiàn),會(huì)有一個(gè)默認(rèn)值,就可以實(shí)現(xiàn)該功能.協(xié)議如下:
@objc protocol WaterfallLayoutDataSource : class {
func waterfallLayout(_ layout : WaterfallLayout, indexPath : IndexPath) -> CGFloat
@objc optional func numberOfColsInWaterfallLayout(_ layout : WaterfallLayout) -> Int
}
完成代碼如下所示:
ViewController.swift中的代碼:
import UIKit
extension UIColor {
class func randomColor() -> UIColor {
return UIColor(colorLiteralRed: Float(arc4random_uniform(256)) / 255.0, green: Float(arc4random_uniform(256)) / 255.0, blue: Float(arc4random_uniform(256)) / 255.0, alpha: 1.0)
}
}
private let kWaterCellID = "kWaterCellID"
class ViewController: UIViewController {
var count : Int = 20
override func viewDidLoad() {
super.viewDidLoad()
// 1.設(shè)置布局
let layout = WaterfallLayout()
layout.minimumLineSpacing = 10
layout.minimumInteritemSpacing = 10
layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
layout.dataSource = self
// 2.創(chuàng)建UICollectionView
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: kWaterCellID)
view.addSubview(collectionView)
}
}
extension ViewController : UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: kWaterCellID, for: indexPath)
cell.backgroundColor = UIColor.randomColor()
if indexPath.item == count - 1 {
count += 20
collectionView.reloadData()
}
return cell
}
}
extension ViewController : WaterfallLayoutDataSource {
func waterfallLayout(_ layout: WaterfallLayout, indexPath: IndexPath) -> CGFloat {
return CGFloat(arc4random_uniform(80) + 100)
}
func numberOfColsInWaterfallLayout(_ layout: WaterfallLayout) -> Int {
return 3
}
}
封裝自定義布局中的WaterfallLayout.swift代碼如下:
import UIKit
@objc protocol WaterfallLayoutDataSource : class {
func waterfallLayout(_ layout : WaterfallLayout, indexPath : IndexPath) -> CGFloat
@objc optional func numberOfColsInWaterfallLayout(_ layout : WaterfallLayout) -> Int
}
class WaterfallLayout: UICollectionViewFlowLayout {
// MARK: 對(duì)外提供屬性
weak var dataSource : WaterfallLayoutDataSource?
// MARK: 私有屬性
fileprivate lazy var attrsArray : [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()
fileprivate var totalHeight : CGFloat = 0
fileprivate lazy var colHeights : [CGFloat] = {
let cols = self.dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2
var colHeights = Array(repeating: self.sectionInset.top, count: cols)
return colHeights
}()
fileprivate var maxH : CGFloat = 0
fileprivate var startIndex = 0
}
extension WaterfallLayout {
// prepare準(zhǔn)備所有Cell的布局樣式
override func prepare() {
super.prepare()
// 0.獲取item的個(gè)數(shù)
let itemCount = collectionView!.numberOfItems(inSection: 0)
// 1.獲取列數(shù)
let cols = dataSource?.numberOfColsInWaterfallLayout?(self) ?? 2
// 2.計(jì)算Item的寬度
let itemW = (collectionView!.bounds.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((cols - 1))) / CGFloat(cols)
// 3.計(jì)算所有的item的屬性
for i in startIndex..<itemCount {
// 1.設(shè)置每一個(gè)Item位置相關(guān)的屬性
let indexPath = IndexPath(item: i, section: 0)
// 2.根據(jù)位置創(chuàng)建Attributes屬性
let attrs = UICollectionViewLayoutAttributes(forCellWith: indexPath)
// 3.隨機(jī)一個(gè)高度
guard let height = dataSource?.waterfallLayout(self, indexPath: indexPath) else {
fatalError("請(qǐng)?jiān)O(shè)置數(shù)據(jù)源,并且實(shí)現(xiàn)對(duì)應(yīng)的數(shù)據(jù)源方法")
}
// 4.取出最小列的位置
var minH = colHeights.min()!
let index = colHeights.index(of: minH)!
minH = minH + height + minimumLineSpacing
colHeights[index] = minH
// 5.設(shè)置item的屬性
attrs.frame = CGRect(x: self.sectionInset.left + (self.minimumInteritemSpacing + itemW) * CGFloat(index), y: minH - height - self.minimumLineSpacing, width: itemW, height: height)
attrsArray.append(attrs)
}
// 4.記錄最大值
maxH = colHeights.max()!
// 5.給startIndex重新復(fù)制
startIndex = itemCount
}
}
extension WaterfallLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attrsArray
}
override var collectionViewContentSize: CGSize {
return CGSize(width: 0, height: maxH + sectionInset.bottom - minimumLineSpacing)
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- iOS實(shí)現(xiàn)水平方向瀑布流
- 詳解IOS中如何實(shí)現(xiàn)瀑布流效果
- IOS簡(jiǎn)單實(shí)現(xiàn)瀑布流UICollectionView
- IOS實(shí)現(xiàn)自定義布局瀑布流
- Jquery瀑布流插件使用介紹
- js實(shí)現(xiàn)的美女瀑布流效果代碼
- javascript自適應(yīng)寬度的瀑布流實(shí)現(xiàn)思路
- jQuery 瀑布流 浮動(dòng)布局(一)(延遲AJAX加載圖片)
- 瀑布流布局并自動(dòng)加載實(shí)現(xiàn)代碼
- iOS自定義UICollectionViewLayout實(shí)現(xiàn)瀑布流布局
相關(guān)文章
詳解iOS 實(shí)現(xiàn)一對(duì)多代理方案
本文主要介紹了iOS 實(shí)現(xiàn)一對(duì)多代理方案,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
iOS開(kāi)發(fā)之topLayoutGuide和bottomLayoutGuide的使用小技巧分享
這篇文章主要給大家介紹了關(guān)于iOS開(kāi)發(fā)之topLayoutGuide和bottomLayoutGuide使用的一些小技巧,需要的朋友可以參考下2017-11-11
IOS 中l(wèi)oadView,viewDidLoad,viewDidUnload詳解及使用
這篇文章主要介紹了IOS 中l(wèi)oadView,viewDidLoad,viewDidUnload詳解及使用的相關(guān)資料,需要的朋友可以參考下2017-02-02
iOS13適配深色模式(Dark Mode)的實(shí)現(xiàn)
這篇文章主要介紹了iOS13適配深色模式(Dark Mode)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
iOS 頁(yè)面滑動(dòng)與標(biāo)題切換顏色漸變的聯(lián)動(dòng)效果實(shí)例
本篇文章主要介紹了iOS 頁(yè)面滑動(dòng)與標(biāo)題切換顏色漸變的聯(lián)動(dòng)效果實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-04-04
iOS實(shí)現(xiàn)一個(gè)可以在屏幕中自由移動(dòng)的按鈕
經(jīng)常在手機(jī)上看到可以隨意移動(dòng)的按鈕,正巧最近工作遇到了這個(gè)需求,索性就寫(xiě)一個(gè),下面這篇文章主要給大家介紹了利用iOS實(shí)現(xiàn)一個(gè)可以在屏幕中自由移動(dòng)的按鈕的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-07-07
iOS開(kāi)發(fā)中使用NSURLConnection類處理網(wǎng)絡(luò)請(qǐng)求的方法
這篇文章主要介紹了iOS開(kāi)發(fā)中使用NSURLConnection類處理網(wǎng)絡(luò)請(qǐng)求的方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-12-12
iOScollectionView廣告無(wú)限滾動(dòng)實(shí)例(Swift實(shí)現(xiàn))
本篇文章主要介紹了iOScollectionView廣告無(wú)限滾動(dòng)實(shí)例,可以實(shí)現(xiàn)廣告無(wú)限滾動(dòng),有興趣的可以了解一下。2016-11-11

