自定义CollectionViewCell之-----瀑布流效果
2016-04-27 16:11
281 查看
一.前期工作
1.定义一个自定义CollectionViewCell,继承于UICollectionViewCell,定义imageView属性,在.m文件里重写dealloc,并且重写initWithFrame方法(viewController中创建自定义collectionCell时会用到这个方法)
#import "myCollectionViewCell.h"
@implementation myCollectionViewCell
- (void)dealloc {
[_imageV release];
[super dealloc];
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super
initWithFrame:frame];
if (self) {
self.imageV = [[UIImageView
alloc] initWithFrame:self.contentView.bounds];
[self.contentView
addSubview:self.imageV];
[self.imageV
release];
}
return
self;
}
@end
2.定义model类,传值和赋值的过程,类的属性包括图片的网址,图片的width,height(要记住里面使用KVC赋值,重写initWithDic方法, 并重写KVC的两个方法,防止崩溃)
.h
#import <Foundation/Foundation.h>
@interface model : NSObject
@property(nonatomic,
copy)NSString *thumbURL;
@property(nonatomic,
retain)NSNumber *width;
@property(nonatomic,
retain)NSNumber *height;
- (instancetype)initWithDic:(NSDictionary *)deic;
@end
.m
#import "model.h"
@implementation model
- (void)dealloc{
[_thumbURL
release];
[_width release];
[_height release];
[super dealloc];
}
//使用 kvc
赋值
- (instancetype)initWithDic:(NSDictionary *)dic{
self = [super
init];
if (self) {
[self
setValuesForKeysWithDictionary:dic];
}
return
self;
}
//重写防止崩溃的方法
//当对象的属性类型是非对象类型而且赋值时为 nil
时,会崩溃的情况.
- (void)setNilValueForKey:(NSString *)key{
}
//解决当没有对应的 key
时 ,会崩溃的情况
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
@end
2.在viewController中做基本工作(1.解析文件,在这里我传入的是json
文件2.设定UICollectionView并遵循协议,写重用方法,设定分区数,设定item个数
)
在这里就不放出代码段了,会在最后直接给出全部的viewController中的代码
二.开始最重要的自定义布局,我们知道collectionCell在布局中 需要一个继承于UICollectionViewLayout类的布局类对象flowLayout.所以我们想要自定义布局,就要自定义一个类,继承于UICollectionViewLayout,然后再其中写自定义的布局代码.
在自定义UICollectionViewLayout类中,定义属性(这些属性在系统的layout中是有的,因为现在是自定义,所以要自己写出来),然后写出一系列方法,下面是myLayout中的代码
myLayout.h
#import <UIKit/UIKit.h>
@protocol myLayoutDelegate <NSObject>
//给位置,返回对应的item高度
- (CGFloat) collectionView:(UICollectionView *)collectionView heightForItemAtIndexPath:(NSIndexPath *)indexPath;
@end
@interface myLayout :
UICollectionViewLayout
//设置属性(这些属性在系统的layout中是有的,因为现在是自定义,所以要自己写出来)
@property(nonatomic)
CGFloat itemWidth;//item的宽
@property(nonatomic)
CGFloat minLineSpace;//最小行间距
@property(nonatomic)
CGFloat itemSpace;//item之间的间距
@property(nonatomic)
UIEdgeInsets sectionInsets;//分区缩进
@property(nonatomic)
NSUInteger numberOfColumns;//列数
@property(nonatomic)
NSUInteger numberOfitems;//item的个数
//设置代理属性
@property(nonatomic,
assign)id <myLayoutDelegate>delegate;
@end
myLayout.m.
#import "myLayout.h"
#define kScreenWIDTH [UIScreen mainScreen].bounds.size.width
@interface myLayout()
@property(nonatomic,
retain)NSMutableArray *columnsHeightArr;//用来存放每一列当前的高
@property(nonatomic,
retain)NSMutableArray *attributeArr;//用来存放每一个布局属性对象
@end
@implementation myLayout
//重写dealloc
- (void)dealloc {
[_columnsHeightArr
release];
[_attributeArr
release];
[super dealloc];
}
//两个属性数组的懒加载
- (NSMutableArray *)columnsHeightArr {
if (!_columnsHeightArr) {
self.columnsHeightArr = [NSMutableArray
arrayWithCapacity:1];
}
return [[_columnsHeightArr
retain]
autorelease];
}
- (NSMutableArray *)attributeArr {
if (!_attributeArr) {
self.attributeArr = [NSMutableArray
arrayWithCapacity:1];
}
return [[_attributeArr
retain] autorelease];
}
//重写init方法
- (instancetype)init {
self = [super
init];
if (self) {
//设置默认的布局
self.itemWidth =
110;
self.sectionInsets =
UIEdgeInsetsMake(10,
10, 10,
10);
self.minLineSpace =
5;
self.numberOfColumns =
3;
self.itemSpace = (kScreenWIDTH -
self.numberOfColumns *
self.itemWidth -
self.sectionInsets.left -
self.sectionInsets.right) /
2.0;
}
return
self;
}
//重写准备布局的方法,在该方法内布局
- (void)prepareLayout {
[super
prepareLayout];
//写我们自己的布局代码
[self mylayout];
}
- (void)mylayout {
//获取item个数
self.numberOfitems = [self.collectionView
numberOfItemsInSection:0];
//初始化每一列的高度,放在columnsHeightArr这个数组中
for (int i =
0; i < self.numberOfColumns; i++) {
[self.columnsHeightArr
addObject:@(self.sectionInsets.top)];
}
//根据有多少个items,不断获取每一个item的高,并计算每个item所在的位置的frame(x,
y, w, h).并将所有的位置信息放进属性数组里
for (int i =
0; i < self.numberOfitems; i++) {
if ([self.delegate
respondsToSelector:@selector(collectionView:heightForItemAtIndexPath:)]) {
//获取到每一个item对应的高
CGFloat newHeight = [self.delegate
collectionView:self.collectionView
heightForItemAtIndexPath:[NSIndexPath
indexPathForRow:i
inSection:0]];
//确定需要将本次获取的高放到哪一列
NSInteger shortIndex = [self
shortestIndex];
//设置item的x, y
CGFloat x =
self.sectionInsets.left + shortIndex * (self.itemWidth +
self.itemSpace);
CGFloat y = [self.columnsHeightArr[shortIndex]floatValue] +
self.minLineSpace;
//创建布局
UICollectionViewLayoutAttributes *attrubutes = [UICollectionViewLayoutAttributes
layoutAttributesForCellWithIndexPath:[NSIndexPath
indexPathForRow:i
inSection:0]];
//设置frame
attrubutes.frame =
CGRectMake(x, y, self.itemWidth, newHeight);
//把每次建好的属性对象放进数组
[self.attributeArr
addObject:attrubutes];
//更新高度
self.columnsHeightArr[shortIndex] =
@(y + newHeight +
self.minLineSpace);
}
}
}
//设置应该显示的最大区域
- (CGSize)collectionViewContentSize {
//获取当前的内容区域
CGSize contentSize =
self.collectionView.contentSize;
//获取最长列的坐标
NSInteger longIndex = [self
longestIndex];
//更新内容区域的height
contentSize.height = [self.columnsHeightArr[longIndex]
floatValue] + self.sectionInsets.bottom;
return contentSize;
}
//返回需要布局的属性对象
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.attributeArr[indexPath.row];
}
//返回存储布局属性的数组
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
return
self.attributeArr;
}
//获取最短列的方法
- (NSInteger)shortestIndex {
NSInteger index =
0;
CGFloat temp =
CGFLOAT_MAX;
for (id obj
in self.columnsHeightArr) {
if ([obj floatValue] < temp) {
temp = [obj floatValue];
index = [self.columnsHeightArr
indexOfObject:obj];
}
}
return index;
}
//获取最长列的方法
- (NSInteger)longestIndex {
NSInteger index =
0;
CGFloat temp =
CGFLOAT_MIN;
for (id obj
in self.columnsHeightArr) {
if ([obj floatValue] > temp) {
temp = [obj floatValue];
index = [self.columnsHeightArr
indexOfObject:obj];
}
}
return index;
}
@end
三.ok,最后一步,我们只需要在ViewController的.m文件里,遵循协议,创建myLayout对象,实现布局传值就OK了.
#import "ViewController.h"
#import "myCollectionViewCell.h"
#import "model.h"
#import "myLayout.h"
#import "UIImageView+WebCache.h"
@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, myLayoutDelegate>
@property(nonatomic,
retain)NSMutableArray *datasource;
@end
@implementation ViewController
- (void)dealloc {
[_datasource release];
[super dealloc];
}
- (NSMutableArray *)datasource {
if (!_datasource) {
self.datasource = [NSMutableArray arrayWithCapacity:1];
}
return [[_datasource retain] autorelease];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//1.
myLayout *flowLayout = [[myLayout alloc] init];
//2.
flowLayout.delegate = self;
/////
UICollectionView *collecttionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:flowLayout];
collecttionView.dataSource = self;
collecttionView.delegate = self;
collecttionView.backgroundColor = [UIColor colorWithWhite:0.298 alpha:1.000];
[self.view addSubview:collecttionView];
[collecttionView release];
[flowLayout release];
//注册
[collecttionView registerClass:[myCollectionViewCell class] forCellWithReuseIdentifier:@"item"];
//解析jeon文件
NSString *path = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"json"];
NSData *data = [NSData dataWithContentsOfFile:path];
NSError *error = nil;
NSArray *arr = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
for (NSDictionary *dic
in arr) {
model *m = [[model alloc] initWithDic:dic];
[self.datasource addObject:m];
[m release];
}
}
#pragma mark - myLayoutDelegate
//给位置,返回对应的item高度
- (CGFloat) collectionView:(UICollectionView *)collectionView heightForItemAtIndexPath:(NSIndexPath *)indexPath{
model *m = self.datasource[indexPath.row];
CGFloat height = [m.height floatValue];
CGFloat newheight = 110 / [m.width floatValue] * height;
return newheight;
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.datasource.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
myCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"item" forIndexPath:indexPath];
model *m = self.datasource[indexPath.row];
[cell.imageV sd_setImageWithURL:[NSURL URLWithString:m.thumbURL]];
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
好啦,这就是瀑布流的效果,中间没有显示出来的是因为图片的网址失效了.大家有什么不懂的可以评论回复我~
1.定义一个自定义CollectionViewCell,继承于UICollectionViewCell,定义imageView属性,在.m文件里重写dealloc,并且重写initWithFrame方法(viewController中创建自定义collectionCell时会用到这个方法)
#import "myCollectionViewCell.h"
@implementation myCollectionViewCell
- (void)dealloc {
[_imageV release];
[super dealloc];
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super
initWithFrame:frame];
if (self) {
self.imageV = [[UIImageView
alloc] initWithFrame:self.contentView.bounds];
[self.contentView
addSubview:self.imageV];
[self.imageV
release];
}
return
self;
}
@end
2.定义model类,传值和赋值的过程,类的属性包括图片的网址,图片的width,height(要记住里面使用KVC赋值,重写initWithDic方法, 并重写KVC的两个方法,防止崩溃)
.h
#import <Foundation/Foundation.h>
@interface model : NSObject
@property(nonatomic,
copy)NSString *thumbURL;
@property(nonatomic,
retain)NSNumber *width;
@property(nonatomic,
retain)NSNumber *height;
- (instancetype)initWithDic:(NSDictionary *)deic;
@end
.m
#import "model.h"
@implementation model
- (void)dealloc{
[_thumbURL
release];
[_width release];
[_height release];
[super dealloc];
}
//使用 kvc
赋值
- (instancetype)initWithDic:(NSDictionary *)dic{
self = [super
init];
if (self) {
[self
setValuesForKeysWithDictionary:dic];
}
return
self;
}
//重写防止崩溃的方法
//当对象的属性类型是非对象类型而且赋值时为 nil
时,会崩溃的情况.
- (void)setNilValueForKey:(NSString *)key{
}
//解决当没有对应的 key
时 ,会崩溃的情况
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
@end
2.在viewController中做基本工作(1.解析文件,在这里我传入的是json
文件2.设定UICollectionView并遵循协议,写重用方法,设定分区数,设定item个数
)
在这里就不放出代码段了,会在最后直接给出全部的viewController中的代码
二.开始最重要的自定义布局,我们知道collectionCell在布局中 需要一个继承于UICollectionViewLayout类的布局类对象flowLayout.所以我们想要自定义布局,就要自定义一个类,继承于UICollectionViewLayout,然后再其中写自定义的布局代码.
在自定义UICollectionViewLayout类中,定义属性(这些属性在系统的layout中是有的,因为现在是自定义,所以要自己写出来),然后写出一系列方法,下面是myLayout中的代码
myLayout.h
#import <UIKit/UIKit.h>
@protocol myLayoutDelegate <NSObject>
//给位置,返回对应的item高度
- (CGFloat) collectionView:(UICollectionView *)collectionView heightForItemAtIndexPath:(NSIndexPath *)indexPath;
@end
@interface myLayout :
UICollectionViewLayout
//设置属性(这些属性在系统的layout中是有的,因为现在是自定义,所以要自己写出来)
@property(nonatomic)
CGFloat itemWidth;//item的宽
@property(nonatomic)
CGFloat minLineSpace;//最小行间距
@property(nonatomic)
CGFloat itemSpace;//item之间的间距
@property(nonatomic)
UIEdgeInsets sectionInsets;//分区缩进
@property(nonatomic)
NSUInteger numberOfColumns;//列数
@property(nonatomic)
NSUInteger numberOfitems;//item的个数
//设置代理属性
@property(nonatomic,
assign)id <myLayoutDelegate>delegate;
@end
myLayout.m.
#import "myLayout.h"
#define kScreenWIDTH [UIScreen mainScreen].bounds.size.width
@interface myLayout()
@property(nonatomic,
retain)NSMutableArray *columnsHeightArr;//用来存放每一列当前的高
@property(nonatomic,
retain)NSMutableArray *attributeArr;//用来存放每一个布局属性对象
@end
@implementation myLayout
//重写dealloc
- (void)dealloc {
[_columnsHeightArr
release];
[_attributeArr
release];
[super dealloc];
}
//两个属性数组的懒加载
- (NSMutableArray *)columnsHeightArr {
if (!_columnsHeightArr) {
self.columnsHeightArr = [NSMutableArray
arrayWithCapacity:1];
}
return [[_columnsHeightArr
retain]
autorelease];
}
- (NSMutableArray *)attributeArr {
if (!_attributeArr) {
self.attributeArr = [NSMutableArray
arrayWithCapacity:1];
}
return [[_attributeArr
retain] autorelease];
}
//重写init方法
- (instancetype)init {
self = [super
init];
if (self) {
//设置默认的布局
self.itemWidth =
110;
self.sectionInsets =
UIEdgeInsetsMake(10,
10, 10,
10);
self.minLineSpace =
5;
self.numberOfColumns =
3;
self.itemSpace = (kScreenWIDTH -
self.numberOfColumns *
self.itemWidth -
self.sectionInsets.left -
self.sectionInsets.right) /
2.0;
}
return
self;
}
//重写准备布局的方法,在该方法内布局
- (void)prepareLayout {
[super
prepareLayout];
//写我们自己的布局代码
[self mylayout];
}
- (void)mylayout {
//获取item个数
self.numberOfitems = [self.collectionView
numberOfItemsInSection:0];
//初始化每一列的高度,放在columnsHeightArr这个数组中
for (int i =
0; i < self.numberOfColumns; i++) {
[self.columnsHeightArr
addObject:@(self.sectionInsets.top)];
}
//根据有多少个items,不断获取每一个item的高,并计算每个item所在的位置的frame(x,
y, w, h).并将所有的位置信息放进属性数组里
for (int i =
0; i < self.numberOfitems; i++) {
if ([self.delegate
respondsToSelector:@selector(collectionView:heightForItemAtIndexPath:)]) {
//获取到每一个item对应的高
CGFloat newHeight = [self.delegate
collectionView:self.collectionView
heightForItemAtIndexPath:[NSIndexPath
indexPathForRow:i
inSection:0]];
//确定需要将本次获取的高放到哪一列
NSInteger shortIndex = [self
shortestIndex];
//设置item的x, y
CGFloat x =
self.sectionInsets.left + shortIndex * (self.itemWidth +
self.itemSpace);
CGFloat y = [self.columnsHeightArr[shortIndex]floatValue] +
self.minLineSpace;
//创建布局
UICollectionViewLayoutAttributes *attrubutes = [UICollectionViewLayoutAttributes
layoutAttributesForCellWithIndexPath:[NSIndexPath
indexPathForRow:i
inSection:0]];
//设置frame
attrubutes.frame =
CGRectMake(x, y, self.itemWidth, newHeight);
//把每次建好的属性对象放进数组
[self.attributeArr
addObject:attrubutes];
//更新高度
self.columnsHeightArr[shortIndex] =
@(y + newHeight +
self.minLineSpace);
}
}
}
//设置应该显示的最大区域
- (CGSize)collectionViewContentSize {
//获取当前的内容区域
CGSize contentSize =
self.collectionView.contentSize;
//获取最长列的坐标
NSInteger longIndex = [self
longestIndex];
//更新内容区域的height
contentSize.height = [self.columnsHeightArr[longIndex]
floatValue] + self.sectionInsets.bottom;
return contentSize;
}
//返回需要布局的属性对象
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.attributeArr[indexPath.row];
}
//返回存储布局属性的数组
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
return
self.attributeArr;
}
//获取最短列的方法
- (NSInteger)shortestIndex {
NSInteger index =
0;
CGFloat temp =
CGFLOAT_MAX;
for (id obj
in self.columnsHeightArr) {
if ([obj floatValue] < temp) {
temp = [obj floatValue];
index = [self.columnsHeightArr
indexOfObject:obj];
}
}
return index;
}
//获取最长列的方法
- (NSInteger)longestIndex {
NSInteger index =
0;
CGFloat temp =
CGFLOAT_MIN;
for (id obj
in self.columnsHeightArr) {
if ([obj floatValue] > temp) {
temp = [obj floatValue];
index = [self.columnsHeightArr
indexOfObject:obj];
}
}
return index;
}
@end
三.ok,最后一步,我们只需要在ViewController的.m文件里,遵循协议,创建myLayout对象,实现布局传值就OK了.
#import "ViewController.h"
#import "myCollectionViewCell.h"
#import "model.h"
#import "myLayout.h"
#import "UIImageView+WebCache.h"
@interface ViewController ()<UICollectionViewDelegate, UICollectionViewDataSource, myLayoutDelegate>
@property(nonatomic,
retain)NSMutableArray *datasource;
@end
@implementation ViewController
- (void)dealloc {
[_datasource release];
[super dealloc];
}
- (NSMutableArray *)datasource {
if (!_datasource) {
self.datasource = [NSMutableArray arrayWithCapacity:1];
}
return [[_datasource retain] autorelease];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//1.
myLayout *flowLayout = [[myLayout alloc] init];
//2.
flowLayout.delegate = self;
/////
UICollectionView *collecttionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:flowLayout];
collecttionView.dataSource = self;
collecttionView.delegate = self;
collecttionView.backgroundColor = [UIColor colorWithWhite:0.298 alpha:1.000];
[self.view addSubview:collecttionView];
[collecttionView release];
[flowLayout release];
//注册
[collecttionView registerClass:[myCollectionViewCell class] forCellWithReuseIdentifier:@"item"];
//解析jeon文件
NSString *path = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"json"];
NSData *data = [NSData dataWithContentsOfFile:path];
NSError *error = nil;
NSArray *arr = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
for (NSDictionary *dic
in arr) {
model *m = [[model alloc] initWithDic:dic];
[self.datasource addObject:m];
[m release];
}
}
#pragma mark - myLayoutDelegate
//给位置,返回对应的item高度
- (CGFloat) collectionView:(UICollectionView *)collectionView heightForItemAtIndexPath:(NSIndexPath *)indexPath{
model *m = self.datasource[indexPath.row];
CGFloat height = [m.height floatValue];
CGFloat newheight = 110 / [m.width floatValue] * height;
return newheight;
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return self.datasource.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
myCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"item" forIndexPath:indexPath];
model *m = self.datasource[indexPath.row];
[cell.imageV sd_setImageWithURL:[NSURL URLWithString:m.thumbURL]];
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
好啦,这就是瀑布流的效果,中间没有显示出来的是因为图片的网址失效了.大家有什么不懂的可以评论回复我~
相关文章推荐
- C#实现自定义双击事件
- WinForm实现自定义右下角提示效果的方法
- MFC自定义消息的实现方法
- C#实现ProperTyGrid自定义属性的方法
- php自定义错误处理用法实例
- 异步加载技术实现当滚动条到最底部的瀑布流效果
- ThinkPHP中自定义目录结构的设置方法
- C#自定义事件监听实现方法
- C#自定义事件及用法实例
- C#自定义签名章实现方法
- 基于JavaScript实现瀑布流布局
- 基于JavaScript实现瀑布流效果(循环渐近)
- 原生JS实现美图瀑布流布局赏析
- 详解javascript实现瀑布流列式布局
- javascript瀑布流式图片懒加载实例
- C#中使用IFormattable实现自定义格式化字符串输出示例
- javascript 自定义常用方法第1/2页
- javascript实现仿百度图片的瀑布流加载效果
- avalonjs制作响应式瀑布流特效
- javascript实现瀑布流加载图片原理