對于需要使用到列表的頁面,一般是使用UITableView或者是UICollectionView來實現(xiàn)。一直以來都是直接使用UICollectionViewFlowLayout,基本都能實現(xiàn)需求功能,但是對于直接利用UICollectionViewLayout來自定義view的layout沒怎么使用過,這里查了蠻多資料自己寫了demo,僅供日后參考了。
UICollectionViewLayoutAttrubutes
一個很重要的類主要記錄了cells,supplementaryViews,decorationviews的位置,size,透明度,層級等
- @property (nonatomic) CGRect frame; frame信息
- @property (nonatomic) CGPoint center; 中心點
- @property (nonatomic) CGSize size;大小
- @property (nonatomic) CATransform3D transform3D;
- @property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0);
- @property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0);
- @property (nonatomic) CGFloat alpha;透明度
- @property (nonatomic) NSInteger zIndex; // default is 0 層級
- @property (nonatomic, getter=isHidden) BOOL hidden; // As an optimization, UICollectionView might not create a view for items whose hidden attribute is YES
- @property (nonatomic, strong) NSIndexPath *indexPath;位置
- @property (nonatomic, readonly) UICollectionElementCategory representedElementCategory;
- @property (nonatomic, readonly, nullable) NSString *representedElementKind; 響應類型(區(qū)分cell,supple,decaration)
那么當UICollectionView獲取布局的時候會通過訪問每個位置的部件通過其attribute來詢問其布局信息
自定義一個UICollectionViewLayout
繼承自UICollectionViewLayout類,然后一般需要重載下列方法:
-(void)prepareLayout;
每次請求布局時候都會自動調(diào)用,可以在這里修改一些必要的layout的結構和初始需要的參數(shù)等-(CGSize)collectionViewContentSize;
定義UIcollectionView的ContentSize,內(nèi)容大小-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
返回可見區(qū)域rect內(nèi)的所有元素的布局信息
返回包含attributes的數(shù)組-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
定義每個indexpath的item的布局信息
相應的還有定義supplement以及decoration的方法 這里就不在進行列舉-
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;
當邊界發(fā)生改變時,是否應該刷新布局。如果YES則在邊界變化(一般是scroll到其他地方)時,將重新計算需要的布局信息。demo
使用了一組圖片和一個json文件(如果添加過后發(fā)現(xiàn)解析為空,在target ->Build Phase - >copy Bundle Resource添加需要的json文件) 正在減肥用的都是減肥勵志的哈哈
PickModel
使用到的model
.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface PicModel : NSObject
@property(nonatomic,strong) NSString *picPath;//圖像
@property(nonatomic,strong) NSString *picDetail;//詳細內(nèi)容
@property(nonatomic,assign) CGFloat rowHeight;//行高
+(instancetype)initWithDic:(NSDictionary*)dic;
-(instancetype)initWithDic:(NSDictionary*)dic;
@end
.m 通過detail計算高度,計算所在item的高度,這里圖片的高度直接約束限制死了,可以改為按比例計算等。
#import "PicModel.h"
@implementation PicModel
+(instancetype)initWithDic:(NSDictionary*)dic{
PicModel *model = [self alloc];
return [model initWithDic:dic];
}
-(instancetype)initWithDic:(NSDictionary*)dic{
if (self = [super init]) {
self.picPath = dic[@"path"];
self.picDetail = dic[@"detail"];
CGFloat width = ([UIScreen mainScreen].bounds.size.width -(MAXCOUNT - 1) * 10)/MAXCOUNT;
CGSize size = [dic[@"detail"] boundingRectWithSize:CGSizeMake(([UIScreen mainScreen].bounds.size.width -(MAXCOUNT - 1) * 10)/MAXCOUNT, MAXFLOAT) options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:15]} context:nil].size;
self.rowHeight = size.height + 59 *width / 100 ;
}
return self;
}
@end
通過修改定義的列數(shù)
ViewController
.m文件
#import "BaseViewController.h"
#import "PicModel.h"
#import "PureLayout.h"
#import "PureCollectionViewCell.h"
@interface BaseViewController ()<UICollectionViewDelegate,UICollectionViewDataSource>
@property(nonatomic,strong) UICollectionView *mainCollectionView;
@property(nonatomic,strong) NSMutableArray *dataArray;//數(shù)據(jù)源數(shù)組
@end
@implementation BaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.dataArray = [NSMutableArray array];
//解析geojson文件
NSString *jsonPath = [[NSBundle mainBundle] pathForResource:@"Detail" ofType:@"geojson"];
NSArray *detailArr = [[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:jsonPath] options:NSJSONReadingMutableContainers error:nil] objectForKey:@"data"
];
//處理model數(shù)據(jù)
for (int i =0; i<12; i++) {
NSString *path = [NSString stringWithFormat:@"%d.jpg",i];
PicModel *model = [PicModel initWithDic:[NSDictionary dictionaryWithObjectsAndKeys:path,@"path",detailArr[i],@"detail", nil]];
[self.dataArray addObject:model];
}
[self definationMainCollectionView];
//添加MJRefreshFooter 添加數(shù)據(jù)
__weak typeof(self)weakSelf = self;
self.mainCollectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
for (int i =0; i<12; i++) {
NSString *path = [NSString stringWithFormat:@"%d.jpg",i];
PicModel *model = [PicModel initWithDic:[NSDictionary dictionaryWithObjectsAndKeys:path,@"path",detailArr[i],@"detail", nil]];
[weakSelf.dataArray addObject:model];
}
[weakSelf.mainCollectionView reloadData];
[weakSelf.mainCollectionView.mj_footer endRefreshing];
}];
}
/**
* 設置相關UICollectionView信息
*/
-(void)definationMainCollectionView{
PureLayout *layout = [[PureLayout alloc] init];
layout.dataArray = self.dataArray;
self.mainCollectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:layout];
[self.mainCollectionView registerNib:[UINib nibWithNibName:@"PureCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"pureCell"];
self.mainCollectionView.delegate = self;
self.mainCollectionView.dataSource = self;
self.mainCollectionView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.mainCollectionView];
}
#pragma mark - UICollectionViewDataSource
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.dataArray.count;
}
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
PureCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"pureCell" forIndexPath:indexPath];
PicModel *model = self.dataArray[indexPath.row];
cell.model = model;
return cell;
}
PureLayout
.h文件,這里簡單的直接傳遞了數(shù)據(jù)源
@interface PureLayout : UICollectionViewLayout
@property(nonatomic,strong)NSArray *dataArray;//數(shù)據(jù)源
@property(nonatomic,assign) NSInteger maxCount;//列數(shù)
@end
.m文件 定義需要展示的列數(shù),水平 垂直間隔等基本信息
#import "PureLayout.h"
#import "PicModel.h"
static CGFloat horizonalSpace = 10;//水平間隔
static CGFloat verticalSpace = 15;//垂直間隔
@interface PureLayout ()
@property(nonatomic,strong) NSMutableArray *offSets;//用于存儲不同列的MAXY信息
@end
@implementation PureLayout
-(void)prepareLayout{
_maxCount = MAXCOUNT;
}
根據(jù)需要展示的列數(shù),使用數(shù)組分別記錄每行所在列item的frame.origin.y,進行對比,設置UICollectionView的contentsize
-(CGSize)collectionViewContentSize{
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat height = 0.0;
_offSets = [NSMutableArray array];
for (int i =0; i<_maxCount; i++) {
[_offSets addObject:@0];
}
for (int i = 0; i<self.dataArray.count; i++) {
NSInteger col = i % _maxCount;
PicModel *model = self.dataArray[i];
CGFloat offSetY ;
offSetY = [_offSets[col] floatValue] + model.rowHeight + verticalSpace;
_offSets[col] = @(offSetY);
height = MAX(height, offSetY);
}
if (height < [UIScreen mainScreen].bounds.size.height) {
height = [UIScreen mainScreen].bounds.size.height;
}
return CGSizeMake(width, height);
}
返回可見Rect內(nèi)的元素的attributes信息
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
NSMutableArray * attributes = [NSMutableArray array];
for (int i =0 ; i<self.dataArray.count; i++) {
UICollectionViewLayoutAttributes *attribute = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[attributes addObject:attribute];
}
return attributes;
}
對不同的indexpath的items設置attributes信息
-(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
PicModel *model = self.dataArray[indexPath.row];
CGFloat itemWidth = ([UIScreen mainScreen].bounds.size.width - (MAXCOUNT - 1) * 10)/_maxCount;
attribute.size = CGSizeMake(itemWidth, model.rowHeight);
CGFloat itemY = 0.0;
CGFloat itemX = 0.0;
NSInteger col = indexPath.row % _maxCount;
itemX = (([UIScreen mainScreen].bounds.size.width - (MAXCOUNT - 1) * 10)/_maxCount + horizonalSpace)* col;
if (indexPath.row <_maxCount) {
itemY = 0.0;
}else{
UICollectionViewLayoutAttributes *otherAttribute = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row - _maxCount inSection:0]];
itemY = CGRectGetMaxY(otherAttribute.frame) + verticalSpace;
}
attribute.frame = CGRectMake(itemX, itemY, itemWidth, model.rowHeight);
return attribute;
}
最后添加上該方法,邊界改變是重新布局
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
return YES;
}
只是一個簡單的額瀑布流demo,還有蠻多地方需要優(yōu)化,這里僅僅寫下一些基本思路。
官方給出的兩個demo很有學習價值,CircleLayout以及LinLayout,在我之前的給出的參考鏈接里面都是可以直接下載的,對于與文章中的CircleLayout用法,insert和delete方法已經(jīng)被appearing和disappearing取代了,參考的githubdemo被我fork了一份,可以進行下載學習 https://github.com/w467364316/CircleLayout.git
參考資料地址:
http://blog.csdn.net/majiakun1/article/details/17204921
http://www.cnblogs.com/wangyingblock/p/5627448.html