IOS 6中开放了集合视图API——UICollectionView,方便了网格视图的开发。集合视图有4个重要组成部分:
1、单元格,它是集合视图中的一个单元格。
2、节,它是集合视图中的一个行数据,由多个单元格组成。
3、补充视图,它是节的头和脚。
4、装饰视图,集合视图中的背景视图。
UICollectionView继承自UIScrollView。与选择器类似,集合视图也有两个协议:UICollectionViewDelegate委托协议和UICollectionViewDataSource数据源协议。UICollectionViewCell是单元格类。集合视图的布局由UICollectionViewLayout类定义,它是一个抽象类。UICollectionViewFlowLayout是UICollectionViewLayout的子类。对于复杂的布局,可以自定义UICollectionViewLayout类。UICollectionView对应的控制器是UICollectionViewController类(具体如下图所示)。
下面是一个简单的sample。
// ViewController.h #import <UIKit/UIKit.h> @interface ViewController : UICollectionViewController @property (strong, nonatomic) NSArray * events; @end
这里ViewController的父类是UICollectionViewController(需要实现集合视图的数据源和代理协议,这里我们直接在storyboard里操作),events是一个数组,用于存放数据。数据保存在plist文件中,组织方式如下所示,
我们在viewDidLoad方法中读取数据:
- (void)viewDidLoad { [super viewDidLoad]; NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"events"ofType:@"plist"]; //获取文件中的数据 self.events = [[NSArray alloc] initWithContentsOfFile:plistPath]; }
接下来我们需要定义集合视图的单元格,我们可以在storyboard中设计也可以通过代码来设计。单元格是一个视图,可以在它的内部放置其它视图或控件。
首先我们定义一个单元格类,注意它的父类必须是UICollectionViewCell。因为数据单元中定义了key分别为image和name的两个键值对,所以在单元格类中显示数据就需要两个属性与之对应。这里我们定义一个UIImageView和一个UILabel属性(并定义成输出口)。
// Cell.h #import <UIKit/UIKit.h> @interface Cell : UICollectionViewCell @property (strong, nonatomic) IBOutlet UIImageView *imageView; @property (strong, nonatomic) IBOutlet UILabel *label; @end
#import "Cell.h" @implementation Cell - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code } return self; } @end
接下来打开ViewController的storyboard文件,将界面的View Controller下的View换成Collection View(如下图)。
这里我们把结合视图的背景改成白色,选择Collection View并修改其背景色。然后选择Collection View Cell,打开其标识检查器,将Custom Class改成Cell(如下左图);打开其属性检查器,在Collection Reusable View的Identifier中输入Cell(如下右图),这是可重用单元格标识。
上面提到的“可重用单元格”是为了节约内存开销而设计的,当屏幕在翻动时,旧的单元格退出屏幕,新的单元格进入屏幕,如果每次都实例化单元格,必然增加内心开销,可重用单元格就是不去实例化新的单元格,而是先使用可重用单元格标识到视图中去找,找到了就使用,没有则创建。IOS 6对可重用单元格进行优化,不在需要做nil判断了。之前代码
Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; if(!cell){ ... }改成如下形式
Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
说了这么多,我们的单元格视图还没设计呢。接下来从对象库中分别拖拽一个UIImageView和UILabel到单元格中。由于要往里面动态填数据,所以我们要将这两个控件与Cell类中的两个输出口连线。具体操作方法是:选中Cell,然后按住control将鼠标拖拽到UIImageView控件上,这时会弹出一个菜单,选择Image View。用同样的方法将Label控件与label属性连接起来。
下面直接看ViewController.m中实现的集合数据源和代理协议方法:
#pragma mark - UICollectionViewDataSource - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return [self.events count] / 2; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return 2; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; NSDictionary *event = [self.events objectAtIndex:(indexPath.section*2 + indexPath.row)]; cell.label.text = [event objectForKey:@"name"]; cell.imageView.image = [UIImage imageNamed:[event objectForKey:@"image"]]; return cell; } #pragma mark - UICollectionViewDelegate - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *event = [self.events objectAtIndex:(indexPath.section*2 + indexPath.row)]; NSLog(@"select event name : %@", [event objectForKey:@"name"]); }
UICollectionViewDataSource协议的定义如下:
@protocol UICollectionViewDataSource <NSObject> @required - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section; // The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath: - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath; @optional - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView; // The view that is returned must be retrieved from a call to -dequeueReusableSupplementaryViewOfKind:withReuseIdentifier:forIndexPath: - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; @end
UICollectionViewDelegate协议定义如下:
@protocol UICollectionViewDelegate <UIScrollViewDelegate> @optional // Methods for notification of selection/deselection and highlight/unhighlight events. // The sequence of calls leading to selection from a user touch is: // // (when the touch begins) // 1. -collectionView:shouldHighlightItemAtIndexPath: // 2. -collectionView:didHighlightItemAtIndexPath: // // (when the touch lifts) // 3. -collectionView:shouldSelectItemAtIndexPath: or -collectionView:shouldDeselectItemAtIndexPath: // 4. -collectionView:didSelectItemAtIndexPath: or -collectionView:didDeselectItemAtIndexPath: // 5. -collectionView:didUnhighlightItemAtIndexPath: - (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath; - (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath; - (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath; - (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath; - (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath; // called when the user taps on an already-selected item in multi-select mode - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath; - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath; - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath; - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; // These methods provide support for copy/paste actions on cells. // All three should be implemented if any are. - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath; - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender; - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender; @end