iOS中瀑布流布局詳解

前段時(shí)間在逛淘寶的時(shí)候發(fā)現(xiàn)淘寶的商品界面的布局是瀑布流(我記得明明之前不是瀑布流的??)。剛好手上活忙完了,寫(xiě)了一個(gè)瀑布流的布局,簡(jiǎn)單的封裝了下,以便日后使用??。其實(shí)說(shuō)到底瀑布流也就是UICollectionView做的,只是修改了CollectionView的流式布局(FlowLayout),以后要用就直接把自定義的FlowLayout拿過(guò)來(lái)用就行了。


瀑布流
1.要有瀑布流首先得有colletionView,所以先在viewController中把我們的colletionView弄出來(lái)。因?yàn)闆](méi)有做網(wǎng)絡(luò)請(qǐng)求,所以現(xiàn)在我模擬了一份數(shù)據(jù),都是在plist里面裝著在呢。
plist數(shù)據(jù)
相關(guān)圖片
這里強(qiáng)調(diào)一下,因?yàn)槲沂侵苯佑胹b的方式加載的,所以代理啊,數(shù)據(jù)源啊都不用寫(xiě)了,包括后面自定義cell中的那些控件都是直接拉線連的。那這里我們就直接上數(shù)據(jù)源方法
#pragma mark 數(shù)據(jù)源 

-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return self.dataArr.count;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    CYWCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    cell.model = self.dataArr[indexPath.item];
    return cell;
}
用懶加載的方式去加載plist中的數(shù)據(jù)
@interface ViewController ()
//懶加載之后的數(shù)據(jù)數(shù)組
@property (nonatomic,strong) NSMutableArray *dataArr;
@end
#pragma mark 懶加載
-(NSMutableArray *)dataArr{
    if (_dataArr == nil) {
        NSArray *arr = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1.plist" ofType:nil]];  
        NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:arr.count];
        for (NSDictionary *dict in arr) {
            CYWModel *model = [CYWModel modelWithDict:dict];
            [arrM addObject:model];
        }
        _dataArr = arrM;
    }
    return _dataArr;
}
用來(lái)接受數(shù)據(jù)的模型

記得.h中要引入U(xiǎn)IKit框架,不然CGFloat是敲不出來(lái)的

#import <UIKit/UIKit.h>

@interface CYWModel : NSObject
//高
@property (nonatomic,assign) CGFloat height;
//寬
@property (nonatomic,assign) CGFloat width;
//圖片
@property (nonatomic,copy) NSString *icon;
//價(jià)格
@property (nonatomic,copy) NSString *price;
+(instancetype) modelWithDict:(NSDictionary *)dict;

@end

.m中最好實(shí)現(xiàn)下orUndefinedKey:方法,怕萬(wàn)一通過(guò)KVC沒(méi)找到對(duì)應(yīng)的key時(shí)候崩掉

#import "CYWModel.h"
@implementation CYWModel
+(instancetype)modelWithDict:(NSDictionary *)dict{
    id obj = [[self  alloc] init];
    [obj setValuesForKeysWithDictionary:dict];
    return obj;
}
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
    NSLog(@"%@ --- %@",value,key);
}
@end
接下來(lái)自定義的cell中引入模型屬性,重寫(xiě)set方法就好了。
@interface CYWCell ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *priceLabel;
@end
@implementation CYWCell
-(void)setModel:(CYWModel *)model{
    _model = model;
    self.imageView.image = [UIImage imageNamed:model.icon];
    self.priceLabel.text = model.price;
}
接下來(lái)在模擬器中如果能運(yùn)行出如下圖的樣子就行了
示例
那么醉關(guān)鍵了位置就是接下來(lái)自定義我們的UICollectionViewFlowLayout

這是.h中留出來(lái)給控制器賦值的屬性,到時(shí)候你還可以自己添加你需要的屬性。

#import <UIKit/UIKit.h>

@interface CYWWaterFallLayout : UICollectionViewFlowLayout

//列數(shù)
@property (nonatomic,assign) NSInteger colCount;

//數(shù)據(jù)
@property (nonatomic,strong) NSArray *dataList;

@end

這是.m文件,關(guān)鍵的地方的注釋我也寫(xiě)上去了。

另外在插句嘴,其實(shí)瀑布流最主要就是限寬不限高去計(jì)算;另外,你以后在需要瀑布流布局的時(shí)候一定要記得找后臺(tái)的哥們把圖片的真實(shí)尺寸傳過(guò)來(lái),因?yàn)槟阈枰?jì)算長(zhǎng)寬比。#######
#import "CYWWaterFallLayout.h"
#import "CYWModel.h"


@interface CYWWaterFallLayout ()

//用來(lái)返回布局的數(shù)組
@property (nonatomic,strong) NSMutableArray *dataArr;
// 用來(lái)保存每一列item當(dāng)前的總高

@property (nonatomic, strong) NSMutableArray *eachColumnMaxHight;

@end

@implementation CYWWaterFallLayout


/*
 * 準(zhǔn)備開(kāi)始布局 調(diào)用collectionView的 relodata方法也會(huì)調(diào)用這個(gè)方法 
 * 所以在這個(gè)方法里面自定義瀑布流的布局
 */
-(void)prepareLayout{
//    獲取collectionView中第0組的item個(gè)數(shù)
    NSInteger itemNum= [self.collectionView numberOfItemsInSection:0];
    
    for (NSInteger i = 0; i < itemNum; i++) {
        
        NSIndexPath *indexpath = [NSIndexPath indexPathForItem:i inSection:0];
//        布局
        UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexpath];
        
//        總寬
        CGFloat contentW = self.collectionView.bounds.size.width - self.sectionInset.left - self.sectionInset.right;
//        item寬
        CGFloat itemW = (contentW - (self.colCount - 1) * self.minimumInteritemSpacing) / self.colCount;
//        獲取item高
        CYWModel *model = self.dataList[i];
        CGFloat itemH = itemW * (model.height /model.width);
        
//        itemX
        NSInteger colNum = i % self.colCount;
        CGFloat itemX = self.sectionInset.left + (itemW + self.minimumInteritemSpacing) * colNum;
        
//        itemY
        CGFloat itemY = [self.eachColumnMaxHight[colNum] floatValue];
        
        attr.frame = CGRectMake(itemX, itemY, itemW, itemH);
//        重新給數(shù)組中的最高y賦值
        self.eachColumnMaxHight[colNum] = @(itemY + itemH + self.minimumLineSpacing);
        
        [self.dataArr addObject:attr];
        
    }
    
}

//返回collectioView的滾動(dòng)范圍
-(CGSize)collectionViewContentSize{
    NSInteger maxCol = [self calculateMaxHeightCol];
    return CGSizeMake(0, [self.eachColumnMaxHight[maxCol] floatValue] - self.minimumLineSpacing);
}

-(NSInteger)calculateMaxHeightCol{
    NSInteger maxCol = 0;
    CGFloat maxHeight = 0;
    
    for (NSInteger i = 0; i < self.colCount; i++) {
        if (maxHeight < [self.eachColumnMaxHight[i] floatValue]) {
            maxHeight = [self.eachColumnMaxHight[i] floatValue];
            maxCol = i;
        }
    }
    return maxCol;
}

//這個(gè)方法中可以返回collectionView上所用item的索引,rect
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    return self.dataArr;
}


#pragma mark 懶加載
-(NSMutableArray *)dataArr{
    if (_dataArr == nil) {
        _dataArr = [NSMutableArray array];
    }
    return _dataArr;
}


- (NSMutableArray *)eachColumnMaxHight {
    if (_eachColumnMaxHight == nil) {
        // 初始化可變數(shù)組
        _eachColumnMaxHight = [NSMutableArray arrayWithCapacity:self.colCount];
        // 給數(shù)組中的中賦值初始值
        for (NSInteger i = 0; i < self.colCount; i++) {
            // 讓每一列當(dāng)前的高度為一個(gè)組的頂部間距
            _eachColumnMaxHight[i] = @(self.sectionInset.top);
        }
    }
    return _eachColumnMaxHight;
}
@end

最后,我把這個(gè)demo放在了GitHub上,有興趣的可以看看哈https://github.com/YWDrenched/C1
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容