在iOS中,要实现九宫格数据展示,最常用的做法就是使用UICollectionView
UICollectionView继承自UIScrollView,因此支持垂直滚动或水平滚动,而且性能极佳
UICollectionView在iOS6中推出得,也是UIKit视图类中的一颗新星。它和UITableView共享API设计,但也在UITableView上做了一些扩展。UICollectionView最强大、同时显著超出UITableView的特色就是其完全灵活的布局结构
UITableView和UICollectionView都是由dataSoure和delegate驱动的。他们为其显示的子视图集扮演为愚蠢的容器,对他们真实的内容毫不知情。
**UICollectionViewFlowLayout简介
UICollectionView进一步抽象了。它将其子视图的位置,大小和外观的控制权委托给一个单独的布局对象。通过提供一个自定义布局对象,你几乎可以实现任何你能想象到的布局。布局继承自UICollectionViewLayout这个抽象基类。iOS6中以UICollectionViewFlowLayout类的形式提出了一个具体的布局实现。
flow layout可以被用来实现一个标准的grid view,这可能是在collection view中最常见的使用案例了。尽管大多数人都这么想,但是Apple很聪明,没有明确的命名这个类为UICollectionViewGridLayout。而使用了更为通用的术语flow layout,这更好的描述了该类的能力:它通过一个接一个的放置cell来建立自己的布局,当需要的时候,插入横排或竖排的分栏符。通过自定义滚动方向,大小和cell之间的间距,flow layout也可以在单行或单列中布局cell。
**如何展示数据
UICollectionView需要layout和数据源(dataSource) 来显示数据,
UICollecitonView会向数据源查询一共有多少行数据以及每一个显示什么数据等,在查询每一个显示什么数据前要确定设置了layout而且itemSize不能小于{0,0}
没有设置layout布局对象程序会崩溃
没有设置数据源和布局对象的UICollectionView只是个空壳
凡是遵守UITableViewDataSource协议的OC对象,都可以是UICollectionView的数据源
**UICollectionView常用数据源方法
调用数据源的下面方法得知一共有多少组数据
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;
调用数据源的下面方法得知每一组有多少项数据
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
调用数据源的下面方法得知每一项显示什么内容
- (UICollectionViewCell *)collectionView:(UICollectionView *) collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
UICollectionView的数据源必须实现第二个方法和第三个方法,第一个方法不实现默认就是1组
**UICollectionView的常见属性
布局对象
@property (nonatomic, strong) UICollectionViewLayout *collectionViewLayout;
背景视图,会自动填充整个UICollectionView
@property (nonatomic, strong) UIView *backgroundView;
是否允许选中cell 默认允许选中
@property (nonatomic) BOOL allowsSelection;
是否可以多选 默认只是单选
@property (nonatomic) BOOL allowsMultipleSelection;
水平进度条:shows。。。
**UICollectionViewFlowLayout常用属性
cell之间的最小行间距
@property (nonatomic) CGFloat minimumLineSpacing
cell之间的最小列间距
@property (nonatomic) CGFloat minimumInteritemSpacing;默认值为10;
cell的尺寸
@property (nonatomic) CGSize itemSize;
cell的预估尺寸
@property (nonatomic) CGSize estimatedItemSize;
UICollectionView的滚动方向,默认是垂直滚动
@property (nonatomic) UICollectionViewScrollDirection scrollDirection;
HeaderView的尺寸
@property (nonatomic) CGSize headerReferenceSize;
FooterView的尺寸
@property (nonatomic) CGSize footerReferenceSize;
分区的四边距
@property (nonatomic) UIEdgeInsets sectionInset;
设置是否当元素超出屏幕之后固定页眉视图位置,默认NO
@property (nonatomic) BOOL sectionHeadersPinToVisibleBounds;
设置是否当元素超出屏幕之后固定页脚视图位置,默认NO
@property (nonatomic) BOOL sectionFootersPinToVisibleBounds
****自定义布局的常用方法
UICollectionView将要显示时准备布局,每当布局更新时,调用该方法做布局前的准备 - (void)prepareLayout;
创建指定索引的cell的布局属性
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPat;
返回UICollectionView内所有的控件的布局属性
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
自定义布局时一定要实现此方法来返回UICollectionView的contentSize,内容尺寸,UICollectionView的滚动范围
- (CGSize)collectionViewContentSize;
纯代码方式使用
CollectionView
#import
"ViewController.h"
@interface ViewController () < UICollectionViewDataSource >
@end
@implementation ViewController
static NSString * const ID = @"test_cell" ;
#pragma mark - collection view data source
// 设置组的个数
- ( NSInteger )numberOfSectionsInCollectionView:( UICollectionView *)collectionView {
return 7 ;
}
// 设置每组有多少个项(格子)
- ( NSInteger )collectionView:( UICollectionView *)collectionView numberOfItemsInSection:( NSInteger )section {
return section + 1 ;
}
// 返回每组的每行的 cell
- ( UICollectionViewCell *)collectionView:( UICollectionView *)collectionView cellForItemAtIndexPath:( NSIndexPath *)indexPath {
// 1. 创建 Cell
// 创建一个 Cell
// dequeueReusableCellWithReuseIdentifier: 方法 , 首先从缓存池中查找是否有对应的 cell, 如果有 , 那么就返回;如果没有那么这个方法内部会帮我们创建一个 cell 。前提是我们需要告诉 collectionview ,我们要的 cell 的类型【将来要创建哪个类型的对象作为我们的 cell 】
// 注册 Cell : 前提是我们需要告诉 collectionview ,我们要的 cell 的类型【将来要创建哪个类型的对象作为我们的 cell 】
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier : ID forIndexPath :indexPath];
cell. backgroundColor = [ UIColor redColor ];
// 2. 返回 Cell
return cell;
}
#pragma mark - viewDidLoad
- ( void )viewDidLoad {
[ super viewDidLoad ];
// 1. 创建一个 UICollectionView 控件
// 1.1 先创建一个布局对象
@interface ViewController () < UICollectionViewDataSource >
@end
@implementation ViewController
static NSString * const ID = @"test_cell" ;
#pragma mark - collection view data source
// 设置组的个数
- ( NSInteger )numberOfSectionsInCollectionView:( UICollectionView *)collectionView {
return 7 ;
}
// 设置每组有多少个项(格子)
- ( NSInteger )collectionView:( UICollectionView *)collectionView numberOfItemsInSection:( NSInteger )section {
return section + 1 ;
}
// 返回每组的每行的 cell
- ( UICollectionViewCell *)collectionView:( UICollectionView *)collectionView cellForItemAtIndexPath:( NSIndexPath *)indexPath {
// 1. 创建 Cell
// 创建一个 Cell
// dequeueReusableCellWithReuseIdentifier: 方法 , 首先从缓存池中查找是否有对应的 cell, 如果有 , 那么就返回;如果没有那么这个方法内部会帮我们创建一个 cell 。前提是我们需要告诉 collectionview ,我们要的 cell 的类型【将来要创建哪个类型的对象作为我们的 cell 】
// 注册 Cell : 前提是我们需要告诉 collectionview ,我们要的 cell 的类型【将来要创建哪个类型的对象作为我们的 cell 】
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier : ID forIndexPath :indexPath];
cell. backgroundColor = [ UIColor redColor ];
// 2. 返回 Cell
return cell;
}
#pragma mark - viewDidLoad
- ( void )viewDidLoad {
[ super viewDidLoad ];
// 1. 创建一个 UICollectionView 控件
// 1.1 先创建一个布局对象
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
//也可以这样获取布局
1 .获取
flowlaytout(
通过拖线获取布局
);,
修改
item
的大小
// self.flowLayout.itemSize = CGSizeMake(100, 100);
2 .
获取布局(通过代码获取)
UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *) self.collectionView.collectionViewLayout;
//
设置每个
Cell
的大小
flowLayout. itemSize = CGSizeMake ( 60 , 60 );
// 设置组与组之间的间隔
flowLayout. sectionInset = UIEdgeInsetsMake ( 50 , 10 , 0 , 10 );
// 设置每个格子之间的最小水平间距
flowLayout. minimumInteritemSpacing = 2 ;
// 设置行间距 ( 设置最小行间距 )
flowLayout. minimumLineSpacing = 20 ;
// 1.2 创建一个 collection view 对象
UICollectionView *collectionView = [[ UICollectionView alloc ] initWithFrame : self . view . bounds collectionViewLayout :flowLayout];
// 设置背景色
collectionView. backgroundColor = [ UIColor blueColor ];
// 为 collection view 设置数据源对象
collectionView. dataSource = self ;
flowLayout. itemSize = CGSizeMake ( 60 , 60 );
// 设置组与组之间的间隔
flowLayout. sectionInset = UIEdgeInsetsMake ( 50 , 10 , 0 , 10 );
// 设置每个格子之间的最小水平间距
flowLayout. minimumInteritemSpacing = 2 ;
// 设置行间距 ( 设置最小行间距 )
flowLayout. minimumLineSpacing = 20 ;
// 1.2 创建一个 collection view 对象
UICollectionView *collectionView = [[ UICollectionView alloc ] initWithFrame : self . view . bounds collectionViewLayout :flowLayout];
// 设置背景色
collectionView. backgroundColor = [ UIColor blueColor ];
// 为 collection view 设置数据源对象
collectionView. dataSource = self ;
//
注册
Cell:
[collectionView
registerClass
:[
UICollectionViewCell
class
]
forCellWithReuseIdentifier
:
ID
];
// 2. 把 CollectionView 添加到控制器的 view 中
[ self . view addSubview :collectionView];
}
- ( void )didReceiveMemoryWarning {
[ super didReceiveMemoryWarning ];
// Dispose of any resources that can be recreated.
}
// 2. 把 CollectionView 添加到控制器的 view 中
[ self . view addSubview :collectionView];
}
- ( void )didReceiveMemoryWarning {
[ super didReceiveMemoryWarning ];
// Dispose of any resources that can be recreated.
}
//
选定第一个为选中状态
NSIndexPath * indexPath = [ NSIndexPath indexPathForItem : 0 inSection : 0 ];
// 默认选中某一行的方法
NSIndexPath * indexPath = [ NSIndexPath indexPathForItem : 0 inSection : 0 ];
// 默认选中某一行的方法
[ self selectItemAtIndexPath:indexPath animated:YES scrollPosition:UICollectionViewScrollPositionNone];
//
[collectionView
scrollToItemAtIndexPath
:indexpath
atScrollPosition
:
UICollectionViewScrollPositionNone
animated
:
YES
];
@end
注意:自定义cell时。注册cell 之后,首先需要实例化cell,必须使用initWithFrame:方法;
属性:
代理方法:
选中某行的方法
- (
void
)collectionView:(
UICollectionView
*)collectionView didSelectItemAtIndexPath:(
NSIndexPath
*)indexPath{
// 点击的时候 , 新闻主界面加载对应的新闻 ( 滚动到对应的频道就会自动加载数据 .)
// 当选中某一行时,发出通知,
[[ NSNotificationCenter defaultCenter ] postNotificationName : @"newsData" object :indexPath];
//cell 本身也滚动到中心位置
[ self scrollToItemAtIndexPath :indexPath atScrollPosition : UICollectionViewScrollPositionCenteredVertically animated : YES ];
// 获取模型数据
// 1. 取出这一行对应的数据
LZJNewsChannelModel * channel = self . channels [indexPath. item ];
// 2. 重新赋值
// 2.1 取出这一行对应的 cell
LZJNewsChannelCell * cell =( LZJNewsChannelCell *)[collectionView cellForItemAtIndexPath :indexPath];
// 重新赋值
cell. channel = channel;
// 点击的时候 , 新闻主界面加载对应的新闻 ( 滚动到对应的频道就会自动加载数据 .)
// 当选中某一行时,发出通知,
[[ NSNotificationCenter defaultCenter ] postNotificationName : @"newsData" object :indexPath];
//cell 本身也滚动到中心位置
[ self scrollToItemAtIndexPath :indexPath atScrollPosition : UICollectionViewScrollPositionCenteredVertically animated : YES ];
// 获取模型数据
// 1. 取出这一行对应的数据
LZJNewsChannelModel * channel = self . channels [indexPath. item ];
// 2. 重新赋值
// 2.1 取出这一行对应的 cell
LZJNewsChannelCell * cell =( LZJNewsChannelCell *)[collectionView cellForItemAtIndexPath :indexPath];
// 重新赋值
cell. channel = channel;
}
已经选中某行的方法:
- (
void
)collectionView:(
UICollectionView
*)collectionView didDeselectItemAtIndexPath:(
NSIndexPath
*)indexPath{
// 获取模型数据
// 1. 取出这一行对应的数据
LZJNewsChannelModel * channel = self . channels [indexPath. item ];
// 2. 重新赋值
// 2.1 取出这一行对应的 cell
LZJNewsChannelCell * cell =( LZJNewsChannelCell *)[collectionView cellForItemAtIndexPath :indexPath];
// 重新赋值
cell. channel = channel;
// 获取模型数据
// 1. 取出这一行对应的数据
LZJNewsChannelModel * channel = self . channels [indexPath. item ];
// 2. 重新赋值
// 2.1 取出这一行对应的 cell
LZJNewsChannelCell * cell =( LZJNewsChannelCell *)[collectionView cellForItemAtIndexPath :indexPath];
// 重新赋值
cell. channel = channel;
}