1. 程式人生 > >自定義UICollectionViewLayout實現瀑布流

自定義UICollectionViewLayout實現瀑布流

該瀑布流的列數,列與列,行與行,距離四周的間距均通過代理由外界傳入

//
//  ViewController.m
//  PuBuFlow
//
//  Created by hq on 16/5/11.
//  Copyright © 2016年 hanqing. All rights reserved.
//

#import "ViewController.h"
#import "HQFlowLayout.h"
#import "HQCollectionViewCell.h"
#import <MJRefresh.h>
#import <MJExtension.h>
#import "HQShop.h"

@interface ViewController () <UICollectionViewDataSource,HQFlowLayoutDelegate>

@property(nonatomic,strong) NSMutableArray *goodsArrays;

@property(nonatomic,weak) UICollectionView *collectionView;


@end

static NSString * const 
[email protected]
"flow_cell"; @implementation ViewController -(NSMutableArray *)goodsArrays{ if (_goodsArrays==nil) { _goodsArrays=[NSMutableArray array]; } return _goodsArrays; } - (void)viewDidLoad { [super viewDidLoad]; [self setUpCollection]; [self setUpRefresh]; } -(void) setUpCollection{ HQFlowLayout *flow=[[HQFlowLayout alloc]init]; flow.delegate=self; UICollectionView *collectionView=[[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:flow]; collectionView.backgroundColor=[UIColor whiteColor]; collectionView.dataSource=self; [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([HQCollectionViewCell class]) bundle:nil] forCellWithReuseIdentifier:cellID]; [self.view addSubview:collectionView]; self.collectionView=collectionView; } -(void) setUpRefresh{ //建立下拉重新整理 self.collectionView.mj_header=[MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewData)]; [self.collectionView.mj_header beginRefreshing]; //建立上拉重新整理 self.collectionView.mj_footer=[MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreData)]; } -(void) loadNewData{ [self.goodsArrays removeAllObjects]; NSMutableArray *array=[HQShop mj_objectArrayWithFilename:@"1.plist"]; [self.goodsArrays addObjectsFromArray:array]; [self.collectionView reloadData]; [self.collectionView.mj_header endRefreshing]; } -(void) loadMoreData{ NSMutableArray *array=[HQShop mj_objectArrayWithFilename:@"1.plist"]; [self.goodsArrays addObjectsFromArray:array]; [self.collectionView reloadData]; [self.collectionView.mj_footer endRefreshing]; } -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return self.goodsArrays.count; } -(UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ HQShop *shop=self.goodsArrays[indexPath.item]; HQCollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath]; cell.shop=shop; return cell; } #pragma mark 我們自定義的瀑布流HQFlowLayout當中的代理實現方法 //設定每個cell的高度 -(CGFloat)flowLayout:(HQFlowLayout *)flowLayout heightForItemAtIndex:(NSInteger)index itemWidth:(CGFloat)itemWidth{ HQShop *shop=self.goodsArrays[index]; //返回高度 return shop.h*itemWidth/shop.w; } //設定cell的列數 -(CGFloat)columNumbersFlowLayout:(HQFlowLayout *)flowLayout{ return 3; } //設定行與行之間的間距 -(CGFloat)rowMarginFlowLayout:(HQFlowLayout *)flowLayout{ return 10; } //設定列與列之間的間距 -(CGFloat)colMarginFlowLayout:(HQFlowLayout *)flowLayout{ return 10; } //設定四周邊緣的距離 -(UIEdgeInsets)edgeIndsetsFlowLayout:(HQFlowLayout *)flowLayout{ UIEdgeInsets sets=UIEdgeInsetsMake(10, 10, 10, 10); return sets; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end


核心方法如下

//
//  HQFlow.h
//  PuBuFlow
//
//  Created by hq on 16/5/11.
//  Copyright © 2016年 hanqing. All rights reserved.
//

#import <UIKit/UIKit.h>
@class HQFlowLayout;

@protocol HQFlowLayoutDelegate <NSObject>

@required

//返回每行的高度
-(CGFloat) flowLayout:(HQFlowLayout *) flowLayout heightForItemAtIndex:(NSInteger) index itemWidth:(CGFloat) itemWidth;

@optional
//返回列數
-(CGFloat) columNumbersFlowLayout:(HQFlowLayout *) flowLayout;

//返回行與行之間的間距
-(CGFloat) rowMarginFlowLayout:(HQFlowLayout *) flowLayout;

//返回列與列之間的間距
-(CGFloat) colMarginFlowLayout:(HQFlowLayout *) flowLayout;

//設定距離邊緣四周的距離
-(UIEdgeInsets) edgeIndsetsFlowLayout:(HQFlowLayout *) flowLayout;

@end


@interface HQFlowLayout : UICollectionViewLayout


@property(nonatomic,weak) id<HQFlowLayoutDelegate> delegate;


@end



//
//  HQFlow.m
//  PuBuFlow
//
//  Created by hq on 16/5/11.
//  Copyright © 2016年 hanqing. All rights reserved.
//

#import "HQFlowLayout.h"
#import <MJExtension.h>
#import <MJRefresh.h>

@interface HQFlowLayout()

@property(nonatomic,strong) NSMutableArray *dataArrays;

@property(nonatomic,strong) NSMutableArray *colHeightArrays;

@end

@implementation HQFlowLayout

//有3列
static NSInteger colNumber=3;

//每行之間的間距
static CGFloat rowMargin=10;

//每列之間的間距
static CGFloat colMargin=10;

//距離邊緣的間距
static UIEdgeInsets boderInsets={10,10,10,10};

#pragma mark 懶載入

-(NSMutableArray *)dataArrays{
    
    if (_dataArrays==nil) {
        _dataArrays=[NSMutableArray array];
    }
    return _dataArrays;
}

//儲存每行的高度
-(NSMutableArray *)colHeightArrays{
    
    if (_colHeightArrays==nil) {
        _colHeightArrays=[NSMutableArray array];
    }
    return _colHeightArrays;
}

#pragma mark 通過代理獲取屬性,獲取不到,則使用預設值

//獲取列數
-(NSInteger) getColumNumber{
    
    if ([self.delegate respondsToSelector:@selector(columNumbersFlowLayout:)]) {
        
        return [self.delegate columNumbersFlowLayout:self];
    }
    
    //沒有設定則用預設值
    return colNumber;
}

//獲取行與行之間的間距
-(CGFloat) getRowMargin{
    
    if ([self.delegate respondsToSelector:@selector(rowMarginFlowLayout:)]) {
        
        return [self.delegate rowMarginFlowLayout:self];
    }
    
    //沒有設定則用預設值
    return rowMargin;
    
}


//獲取列與列之間的間距
-(CGFloat) getColMargin{
    
    if ([self.delegate respondsToSelector:@selector(colMarginFlowLayout:)]) {
        
        return [self.delegate colMarginFlowLayout:self];
    }
    
    //沒有設定則用預設值
    return colMargin;
    
}

-(UIEdgeInsets) getEdgeInsets{
    
    if ([self.delegate respondsToSelector:@selector(edgeIndsetsFlowLayout:)]) {
        
        return [self.delegate edgeIndsetsFlowLayout:self];
    }
    
    //沒有設定則用預設值
    return boderInsets;
    
}



//初始化操作,必須寫super prepareLayout
//每次表格reloaddata,都會呼叫該方法
-(void)prepareLayout{
    
    [self.dataArrays removeAllObjects];
    
    [self.colHeightArrays removeAllObjects];
    
    [super prepareLayout];
    
     NSLog(@"%s",__func__);
    
    //初始化我們每個col的高度,預設為距離頂部的高度
    for (int i=0; i<[self getColumNumber]; i++) {
        
        [self.colHeightArrays addObject:@([self getEdgeInsets].top)];
    }
    
    NSInteger cellCount=[self.collectionView numberOfItemsInSection:0];
    
    //初始化我們所有cell屬性的陣列
    for (int i=0; i<cellCount; i++) {
        
        NSIndexPath *indexpath=[NSIndexPath indexPathForItem:i inSection:0];
        
        UICollectionViewLayoutAttributes *attr=[self layoutAttributesForItemAtIndexPath:indexpath];
        
        [self.dataArrays addObject:attr];
    }
}

//所有元素的屬性
//該方法,每滾動一次就會被呼叫一次,因此我們把陣列的初始化放到preparelayout當中去
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{

    return self.dataArrays;
}

-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    UICollectionViewLayoutAttributes *attr=[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    CGFloat w=(self.collectionView.bounds.size.width-[self getColMargin]*([self getColumNumber]-1)-[self getEdgeInsets].left-[self getEdgeInsets].right)/[self getColumNumber];
    
//    CGFloat h=arc4random_uniform(100)+80;
    
    //通過代理方法獲取高度
    CGFloat h=[self.delegate flowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w];
    
    //接下來計算哪個列最短
    
    CGFloat minHeight=[self.colHeightArrays[0] doubleValue];
    
    NSInteger minCol=0;
    
    for (int i=1; i<[self getColumNumber]; i++) {
        
        CGFloat currentHeight=[self.colHeightArrays[i] doubleValue];
        
        if (currentHeight<minHeight) {
            minCol=i;
            minHeight=currentHeight;
        }
    }

    CGFloat x=[self getEdgeInsets].left+(w+[self getColMargin])*minCol;
    
    CGFloat y=minHeight+[self getRowMargin];
    
    attr.frame=CGRectMake(x, y, w, h);
    
    self.colHeightArrays[minCol][email protected](CGRectGetMaxY(attr.frame));

    return attr;
    
}

-(CGSize)collectionViewContentSize{
    
   //取出我們的最大高度
    CGFloat maxHeight=[self.colHeightArrays[0] doubleValue];
    
    for (int i=1; i<[self getColumNumber]; i++) {
        
        CGFloat currentHeight=[self.colHeightArrays[i] doubleValue];
        
        if (currentHeight>maxHeight) {
            maxHeight=currentHeight;
        }
    }
    
    return CGSizeMake(0, maxHeight);
}


@end


相關推薦

iOS 定義UICollectionViewLayout實現瀑布

前言hihi,勇敢的小夥伴兒們大家好,很高興今天又能更新了,首先照例說一下學習這個瀑布流的人生感悟(一本正經)。在2015年的時候我已經瞭解瀑布流這個概念了,也知道可以用UICollectionView來實現,但是有意思的是我從業幾年來,從未在專案中真正實踐過,所以我就一!直

定義UICollectionViewLayout實現瀑布佈局

移動端訪問不佳,請訪問我的個人部落格 最近專案中需要用到瀑布流的效果,但是用UICollectionViewFlowLayout又達不到效果,自己動手寫了一個瀑布流的layout,下面是我的心路路程 因為是用UICollectionView來

定義UICollectionViewLayout實現瀑布

該瀑布流的列數,列與列,行與行,距離四周的間距均通過代理由外界傳入 // // ViewController.m // PuBuFlow // // Created by hq on 16/5/11. // Copyright © 2016年 hanqing.

定義ViewGroup實現瀑布效果

今天情人節,我卻在家裡看書寫程式碼,真屌絲啊哈~ 回顧:ViewGroup的時間分發流程: dispatchTouchEvent ----- onInterceptTouchEvent----- onTouchEvent 最外層的ViewGroup首先接收到觸控事件,然後

詳細分享UICollectionView的定義布局(瀑布, 線性, 圓形...)

init hide 屬性 png 繼承 del 屏幕旋轉 結束 效果 前言: 本篇文章不是分享collectionView的詳細使用教程, 而是屬於比較‘高級‘的collectionView使用技巧, 閱讀之前, 我想你已經很熟悉collectionView的基本使用,

Xamarin定義佈局系列——瀑布佈局

原文: Xamarin自定義佈局系列——瀑布流佈局 Xamarin.Forms以Xamarin.Android和Xamarin.iOS等為基礎,自己實現了一整套比較完整的UI框架,包含了絕大多數常用的控制元件,如下圖 雖然XF(Xamarin.Forms簡稱XF,下同)為我們提供大這麼多的控制元件,但在

pig定義FilterFunc實現資料的過濾

假設test.txt檔案中有如下資料: xiaojun 28      shanghai yangna  24      lanzhou yangna  24      shanghai xiaojun 28      上海 想實現過濾其中為上海的資料。我們可以寫 : a

iOS定義UICollectionViewLayout佈局實現瀑布

自定義 UICollectionViewLayout 佈局,實現瀑布流;UICollectionView和UICollectionViewCell 另行建立,這只是佈局檔案, 外界控制器只要遵守協議併成為他的代理並實現代理方法heightForItemAtIndex:返回每個cell的高

RecyclerView定義LayoutManager實現橫向瀑布

最近由於公司專案需要,做了一個橫向瀑布流的元件,如下圖;這個元件是通過自定義LayoutManager實現,LayoutManager為我們提供了強大的自定義功能,但是實現過程卻不簡單,搗鼓了兩天,也就算基本可以用了;Demo原始碼在最下面,這裡主要記錄一些自定義Layou

瀑布定義佈局實現

這篇文章主要分享如何用自定義佈局來實現瀑布流,關於瀑布流的其他實現方式可以參考我的另一篇文章 瀑布流(UIScrollView實現),利用UICollectionView實現瀑布流有個非常大的好處就是我們不用關心重用機制,只把注重點放在如何自定義佈局來排布每一個

javascript適應寬度的瀑布實現思路

這裡主要介紹瀑布流的一種實現方法:絕對定位(css)+javascript+ajax+json。簡單一點如果不做滾動載入的話就是絕對定位(css)+javascript了,ajax和json是滾動載入更多內容的時候用到的,感興趣的你可以參考下哦 這樣的佈局並不陌生,從2011年Pinter

android定義View實現式佈局

//先來一張效果圖 //自定義的控制元件 import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.

[置頂] RecyclerView實現瀑布,圖片適應高度

話不多說,先上效果圖 對於RecyclerView,相信大家都不陌生了,這個集listView,GridView,瀑布流效果與一身強大控制元件,漸漸地滲透在每個App.... 還是回到正題,如何讓RecyclerView裡的圖片自適應高度? 我們知道,要讓RecyclerView有瀑布流效果,R

RecyclerView實現瀑布,圖片適應高度

話不多說,先上效果圖 對於RecyclerView,相信大家都不陌生了,這個集listView,GridView,瀑布流效果與一身強大控制元件,漸漸地滲透在每個App.... 還是回到正題,如何讓RecyclerView裡的圖片自適應高度? 我們知道,要讓Recycler

android 定義view實現式佈局

今天搞了一個流式佈局:如圖 網上也有部落格講這方面的,只是每個人實現思路不一樣,這是在網上看到一篇文章講這個,我看了下,說下這個怎麼實現原理,網上好多是直接繼承了ViewGroup,那樣的話就有個換行和計運算元view的大小.子view的排放位置,但是這個就省略了那麼多複

ratelimit+aop定義註解實現應用限

1、基於springboot專案pom.xml新增如下依賴: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>sprin

js實現瀑布

done title pos webkit for ria side 可能 src 下午查找了瀑布流的相關原理,找了一些css3實現的還有js實現的,最後總結了一些比較簡單的,易懂的整理起來 1.css3實現 只要運用到 column-count分列      

【前端】用jQuery實現瀑布效果

scrollto title n) 個性 避免 ive gets type turn jQuery實現瀑布流效果 何為瀑布流:   瀑布流,又稱瀑布流式布局。是比較流行的一種網站頁面布局,視覺表現為參差不齊的多欄布局,隨著頁面滾動條向下滾動,這種布局還會不斷加載數據塊並附加

Android -- 定義view實現keep歡迎頁倒計時效果

super onfinish -m use new getc awt ttr alt 1,最近打開keep的app的時候,發現它的歡迎頁面的倒計時效果還不錯,所以打算自己來寫寫,然後就有了這篇文章。 2,還是老規矩,先看一下我們今天實現的效果   相較於我們常見的倒計時

Android定義View——實現水波紋效果類似剩余流量球

string 三個點 pre ber block span 初始化 move 理解 最近突然手癢就想搞個貝塞爾曲線做個水波紋效果玩玩,終於功夫不負有心人最後實現了想要的效果,一起來看下吧: 效果圖鎮樓 一:先一步一步來分解一下實現的過程 需要繪制一個正弦曲線(sin