心靈雞湯可不是誰想喝就喝的! --------------棟哥
看完千篇一律的UI布局之后,當我們看到瀑布流的布局是不是覺得有種耳目一新的感覺呢?今天我們就說一下如果實現(xiàn)瀑布流,對于瀑布流,現(xiàn)在iOS中總共存在著三種實現(xiàn)方法.
1.實現(xiàn)瀑布流的布局,我們需要計算每一張圖片的尺寸大小,然后根據(jù)列數(shù)布局到我們的UIScrollView上去
2.UITableView實現(xiàn)瀑布流效果,就是每一列都是一個視圖.
3.UICollectionView實現(xiàn)瀑布流就是對UICollectionView的FlowLayout重寫.
UICollectionView 實現(xiàn)瀑布流
瀑布流的實現(xiàn),現(xiàn)在大多數(shù)人都是使用集合視圖 UICollectionView 這個類做的,我們需要把集合視圖的布局進行重新定義.
對于UICollectionViewFlowLayout 這里有個封裝好的類,有需要的可以直接拿去使用了
WaterfallLayout.h文件中.
//
// WaterfallLayout.h
// Abe的瀑布流的封裝
//
// Created by dongge on 16/3/2.
// Copyright ? 2016年 Abe. All rights reserved.
//
#import <UIKit/UIKit.h>
@protocol WaterfallLayoutDelegate <NSObject>
// 獲取圖片高度
- (CGFloat)heightForItemIndexPath:(NSIndexPath *)indexPath;
@end
@interface WaterfallLayout : UICollectionViewFlowLayout
// item大小
@property (nonatomic,assign)CGSize itemSize;
// 內(nèi)邊距
@property (nonatomic,assign)UIEdgeInsets sectionInsets;
// 間距
@property (nonatomic,assign)CGFloat insertItemSpacing;
// 列數(shù)
@property (nonatomic,assign)NSUInteger numberOfColumn;
// 代理(提供圖片高度)
@property (nonatomic,weak)id<WaterfallLayoutDelegate>delegate;
@end
WaterfallLayout.m中
//
// WaterfallLayout.m
// Abe的瀑布流的封裝
//
// Created by dongge on 16/3/2.
// Copyright ? 2016年 Abe. All rights reserved.
//
#import "WaterfallLayout.h"
@interface WaterfallLayout()
// 所有Item的數(shù)量
@property (nonatomic,assign)NSUInteger numberOfItems;
// 這是一個數(shù)組反症,保存每一列的高度
@property (nonatomic,strong)NSMutableArray *columnHeights;
// 這是一個數(shù)組茬贵,數(shù)組中保存的是一種類型,這種類型決定item的位置和大小姥饰。
@property (nonatomic,strong)NSMutableArray *itemAttributes;
// 獲取最長列索引
- (NSInteger)indexForLongestColumn;
// 獲取最短列索引
- (NSInteger)indexForShortestColumn;
@end
@implementation WaterfallLayout
- (NSMutableArray *)columnHeights{
if (nil == _columnHeights) {
self.columnHeights = [NSMutableArray array];
}
return _columnHeights;
}
-(NSMutableArray *)itemAttributes{
if (nil == _itemAttributes) {
self.itemAttributes = [NSMutableArray array];
}
return _itemAttributes;
}
// 獲取最長列索引
- (NSInteger)indexForLongestColumn{
// 記錄索引
NSInteger longestIndex = 0;
// 記錄當前最長列高度
CGFloat longestHeight = 0;
for (int i = 0; i < self.numberOfColumn; i++) {
// 取出列高度
CGFloat currentHeight = [self.columnHeights[i] floatValue];
// 判斷
if (currentHeight > longestHeight) {
longestHeight = currentHeight;
longestIndex = i;
}
}
return longestIndex;
}
// 獲取最短列索引
- (NSInteger)indexForShortestColumn{
// 記錄索引
NSInteger shortestIndex = 0;
// 記錄最短高度
CGFloat shortestHeight = MAXFLOAT;
for (int i = 0; i < self.numberOfColumn; i++) {
CGFloat currentHeight = [self.columnHeights[i] floatValue];
if (currentHeight < shortestHeight) {
shortestHeight = currentHeight;
shortestIndex = i;
}
}
return shortestIndex;
}
// 這里計算每一個item的x,y孝治,w列粪,h。并放入數(shù)組
-(void)prepareLayout{
[super prepareLayout];
// 循環(huán)添加top高度
for (int i = 0; i < self.numberOfColumn; i++) {
self.columnHeights[i] = @(self.sectionInsets.top);
}
// 獲取item數(shù)量
self.numberOfItems = [self.collectionView numberOfItemsInSection:0];
// 循環(huán)計算每一個item的x,y,width,height
for (int i = 0; i < self.numberOfItems; i++) {
// x,y
// 獲取最短列
NSInteger shortIndex = [self indexForShortestColumn];
// 獲取最短列高度
CGFloat shortestH = [self.columnHeights[shortIndex] floatValue];
// x
CGFloat detalX = self.sectionInsets.left + (self.itemSize.width + self.insertItemSpacing) * shortIndex;
// y
CGFloat detalY = shortestH + self.insertItemSpacing;
// h
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
CGFloat itemHeight = 0;
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(heightForItemIndexPath:)]){
itemHeight = [self.delegate heightForItemIndexPath:indexPath];
}
// 保存item frame屬性的對象
UICollectionViewLayoutAttributes *la = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
la.frame = CGRectMake(detalX, detalY, self.itemSize.width, itemHeight);
// 放入數(shù)組
[self.itemAttributes addObject:la];
// 更新高度
self.columnHeights[shortIndex] = @(detalY + itemHeight);
}
}
// 計算contentSize
- (CGSize)collectionViewContentSize{
// 獲取最高列
NSInteger longestIndex = [self indexForLongestColumn];
CGFloat longestH = [self.columnHeights[longestIndex] floatValue];
// 計算contentSize
CGSize contentSize = self.collectionView.frame.size;
contentSize.height = longestH + self.sectionInsets.bottom;
return contentSize;
}
// 返回所有item的位置和大小(數(shù)組)
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.itemAttributes;
}
@end
接下來我們就在我們需要的地方調(diào)用我們已經(jīng)封裝好的類和實現(xiàn)協(xié)議方法就可以了.這里我在ViewController.m中做了一下測試
//
// ViewController.m
// Abe的瀑布流的封裝
//
// Created by dongge on 16/3/2.
// Copyright ? 2016年 Abe. All rights reserved.
//
#import "ViewController.h"
#import "WaterFlowModel.h"
#import "WaterFlowCell.h"
#import "WaterfallLayout.h"
#import "UIImageView+WebCache.h"
@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate,WaterfallLayoutDelegate>
@property(nonatomic,strong)NSMutableArray *dataArray;//創(chuàng)建可變數(shù)組,存儲json文件數(shù)據(jù)
@end
@implementation ViewController
-(NSMutableArray *)dataArray{
if (nil == _dataArray) {
_dataArray = [NSMutableArray array];
}
return _dataArray;
}
//解析json文件
-(void)parserJsonData {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSMutableArray *arr = [NSMutableArray array ];
arr = [NSJSONSerialization JSONObjectWithData:jsonData options:(NSJSONReadingAllowFragments) error:nil];
for (NSDictionary *dic in arr) {
WaterFlowModel *model = [[WaterFlowModel alloc]init];
[model setValuesForKeysWithDictionary:dic];
[self.dataArray addObject: model];
}
}
//在viewDidLoad設(shè)置集合視圖的flowLayout,我們要做的就是新創(chuàng)建一個繼承于UICollectionViewLayout的類,在這個子類當中,做出瀑布流的布局.
- (void)viewDidLoad {
[super viewDidLoad];
// UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
self.view.backgroundColor = [UIColor whiteColor];
WaterfallLayout *flowLayout = [[WaterfallLayout alloc] init];
// 高度
flowLayout.delegate = self;
CGFloat w = ([UIScreen mainScreen].bounds.size.width - 40) / 3;
flowLayout.itemSize = CGSizeMake(w, w);
// 間隙
flowLayout.insertItemSpacing = 10;
// 內(nèi)邊距
flowLayout.sectionInsets = UIEdgeInsetsMake(10, 10, 10, 10);
// 列數(shù)
flowLayout.numberOfColumn = 3;
UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:flowLayout];
collectionView.delegate = self;
collectionView.dataSource = self;
[self.view addSubview:collectionView];
[collectionView registerClass:[WaterFlowCell class] forCellWithReuseIdentifier:@"cell"];
collectionView.backgroundColor = [UIColor whiteColor];
[self parserJsonData];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.dataArray.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
WaterFlowCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
WaterFlowModel *m = self.dataArray[indexPath.item];
[cell.imgView sd_setImageWithURL:[NSURL URLWithString:m.thumbURL]];
return cell;
}
// 計算高度
- (CGFloat)heightForItemIndexPath:(NSIndexPath *)indexPath{
WaterFlowModel *m = self.dataArray[indexPath.item];
CGFloat w = ([UIScreen mainScreen].bounds.size.width - 40) / 3;
CGFloat h = (w * m.height) / m.width;
return h;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
上面的WaterFlowCell 和 WaterFlowModel 就是測試用的,
總結(jié):瀑布流的實現(xiàn)原理就是當我們需要往某一行添加上新的圖片的時候,我們就先判斷那一行的長度最短,然后就添加上去即可.