UICollectionView的布局是可以自己定義的,在這篇博客中先在上篇博客的基礎(chǔ)上進(jìn)行擴(kuò)充,我們先使用UICollectionViewFlowLayout,然后好好的介紹一下UICollectionView的一些回調(diào)方法,主要包括UICollectionViewDataSource,UICollectionViewDelegateFlowLayout,UICollectionViewDelegate相關(guān)回調(diào)方法,并通過(guò)實(shí)例來(lái)介紹每個(gè)回調(diào)的用法。并且給每個(gè)Section添加定制的Header和Footer,好廢話少說(shuō)進(jìn)入今天的正題。
一、Demo總覽
下圖是本篇博客中Demo的最終運(yùn)行效果,下面是我們要做的事情:
-
給每個(gè)Section添加自定義的重用Header和Footer
2.調(diào)整第一個(gè)Section的上左下右的邊距(UIEdgeInsets)
3.給UICollectioinView設(shè)置多選
4.處理Cell的高亮事件
5.處理Cell的選中事件
6.調(diào)整Cell的上下左右邊距
7.對(duì)Cell進(jìn)行編輯
二、UICollectionViewDataSource介紹
1、在UICollectionViewDataSource回調(diào)方法中有一個(gè)返回Section數(shù)量的方法,如下所示,該方法和UITableView中的用法一致。在這兒我們返回5個(gè)Section,如下所示:
Objective-C
#pragma mark
/**
* 返回Section的個(gè)數(shù)
*/
- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView {
return 5;
}
2、在UICollectionViewDataSource的回調(diào)方法中,還有一個(gè)是返回每個(gè)Section中Cell的數(shù)量的方法,在這我們返回30個(gè)Cell, 如下代碼所示:
Objective-C
/**
* 返回每個(gè)Section中Cell的個(gè)數(shù)
*/
- (NSInteger)collectionView: (UICollectionView *)collectionView
numberOfItemsInSection: (NSInteger)section {
return 30;
}
3、在UICollectionViewDataSource還有一個(gè)必須實(shí)現(xiàn)的方法, 就是選擇我們CollectionView中所使用的Cell, 在這里我們所使用的Cell是在Storyboard上實(shí)現(xiàn)的,所以不需要在我們的代碼中注冊(cè)Cell, 之間使用重用標(biāo)示符就可以獲取Cell的對(duì)象,如下所示:
Objective-C
/**
* 返回Cell種類
*/
- (UICollectionViewCell *)collectionView: (UICollectionView *)collectionView
cellForItemAtIndexPath: (NSIndexPath *)indexPath {
//通過(guò)Cell重用標(biāo)示符來(lái)獲取Cell
CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier: reuseIdentifier
forIndexPath: indexPath];
return cell;
}
4、在UICollectionViewDataSource方法中有一個(gè)可選的方法就是為我們的Section添加Supplementary View(追加視圖),下面是添Supplementary View(追加視圖)的步驟。在UICollectionView中的Section中我們可以為其增加Header View和Footer View, 也就是官方文檔上提到的Supplementary View(追加視圖)。追加視圖是可以重用的,也就是UICollectionReusableView。我們可以創(chuàng)建兩個(gè)UICollectionReusableView的子類,一個(gè)是Header View, 另一個(gè)是Footer View。
(1)創(chuàng)建UICollectionReusableView
追加視圖可以在Storyboard上添加,然后設(shè)置重用標(biāo)示符,在代碼中使用即可。這里我們是從xib文件來(lái)加載的Supplementary View, 先創(chuàng)建兩個(gè)UICollectionReusableView子類,在創(chuàng)建該子類的同時(shí)創(chuàng)建相應(yīng)的xib文件,如下所示:
創(chuàng)建Header View和Footer View的UICollectionReusableView,創(chuàng)建后的文件目錄如下:
(2) 因?yàn)槲覀兪菑膞ib文件中加載的UICollectionReusableView,所以需要在相應(yīng)的UICollectionView上進(jìn)行注冊(cè)。如果你是使用的Storyboard, 只需要在Storyboard中指定重用標(biāo)示符即可。下面的代碼就是在ViewDidLoad中調(diào)用注冊(cè)UICollectionReusableView的方法。
Objective-C
/**
* 注冊(cè)Header和FooterView
* 便于在UICollectionViewDataSource中使用
*/
- (void) registerHeaderAndFooterView {
//注冊(cè)headerView
//獲取含有UICollectionReusableView的Nib文件。
UINib *headerNib = [UINib nibWithNibName: @"CollectionHeaderReusableView"
bundle: [NSBundle mainBundle]];
//注冊(cè)重用View
[self.collectionView registerNib: headerNib
forSupplementaryViewOfKind: UICollectionElementKindSectionHeader
withReuseIdentifier: @"CollectionHeaderReusableView"];
//注冊(cè)FooterView
UINib *footerNib = [UINib nibWithNibName: @"CollectionFooterReusableView"
bundle:[ NSBundle mainBundle]];
[self.collectionView registerNib: footerNib
forSupplementaryViewOfKind: UICollectionElementKindSectionFooter
withReuseIdentifier: @"CollectionFooterReusableView"];
}
(3)在UICollectionViewDataSource中的設(shè)置Supplementary View的方法中通過(guò)Header View和Footer View的重用標(biāo)示符來(lái)為我們的Section設(shè)置Supplementary View,具體代碼如下所示:
Objective-C
/**
* 設(shè)置Setion的Header和Footer(Supplementary View)
*/
- (UICollectionReusableView *)collectionView: (UICollectionView *)collectionView
viewForSupplementaryElementOfKind: (NSString *)kind
atIndexPath: (NSIndexPath *)indexPath{
//設(shè)置SectionHeader
if ([kind isEqualToString: UICollectionElementKindSectionHeader]) {
UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"CollectionHeaderReusableView" forIndexPath:indexPath];
return view;
}
//設(shè)置SectionFooter
UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"CollectionFooterReusableView" forIndexPath:indexPath];
return view;
}
UICollectionViewDataSource中的四個(gè)方法在上面都進(jìn)行了實(shí)現(xiàn),UICollectionViewDataSource主要是負(fù)責(zé)加載數(shù)據(jù)源的,包括Section的個(gè)數(shù),每個(gè)Section中Cell的個(gè)數(shù),每個(gè)Section中Supplementary View的種類。
三.UICollectionViewDelegateFlowLayout回調(diào)實(shí)現(xiàn)
UICollectionViewDelegateFlowLayout主要是負(fù)責(zé)顯示的,比如Secion的大小、邊距,Cell的大小邊距,headerView的大小已經(jīng)FooterView的大小,都是在UICollectionViewDelegateFlowLayout的相應(yīng)協(xié)議的方法來(lái)實(shí)現(xiàn)的。接下來(lái)詳細(xì)的介紹一下UICollectionViewDelegateFlowLayout協(xié)議中的方法。
1.同一個(gè)Section中同一種Cell(通過(guò)同一個(gè)Cell重用標(biāo)示符獲取的對(duì)象)可以有不同的尺寸,下面的代碼是給Cell定制尺寸。代碼的具體意思是第一個(gè)Section中的所有Cell的尺寸是(50,50)。 其余的時(shí)(60,60)。
Objective-C
#pragma mark
/**
* 改變Cell的尺寸
*/
- (CGSize)collectionView: (UICollectionView *)collectionView
layout: (UICollectionViewLayout*)collectionViewLayout
sizeForItemAtIndexPath: (NSIndexPath *)indexPath{
if (indexPath.section == 0) {
return CGSizeMake(50, 50);
}
return CGSizeMake(60, 60);
}
2.改變Section的上下左右邊距–UIEdgeInsetsMake(上, 左, 下, 右),逆時(shí)針旋轉(zhuǎn)。第一個(gè)Section的上左下右的邊距都是50, 其余的Section上左下右的邊距是0。具體實(shí)現(xiàn)看如下代碼:
Objective-C
/**
* Section的上下左右邊距--UIEdgeInsetsMake(上, 左, 下, 右);逆時(shí)針
*/
- (UIEdgeInsets)collectionView: (UICollectionView *)collectionView
layout: (UICollectionViewLayout*)collectionViewLayout
insetForSectionAtIndex: (NSInteger)section{
if (section == 0) {
return UIEdgeInsetsMake(50, 50, 50, 50);
}
return UIEdgeInsetsMake(0, 0, 0, 0);
}
3.設(shè)置每個(gè)Cell的上下邊距的回調(diào)如下所示,第一個(gè)Section的Cell上下邊距是5.0f, 其余的為20.0f。
Objective-C
/**
* Section中每個(gè)Cell的上下邊距
*/
- (CGFloat)collectionView: (UICollectionView *)collectionView
layout: (UICollectionViewLayout*)collectionViewLayout
minimumLineSpacingForSectionAtIndex: (NSInteger)section{
if (section == 0) {
return 5.0f;
}
return 20.0f;
}
4.設(shè)置Cell的左右邊距,第一個(gè)Section的Cell左右邊距是5.0f, 其余的為20.0f。
Objective-C
/**
* Section中每個(gè)Cell的左右邊距
*/
- (CGFloat)collectionView: (UICollectionView *)collectionView
layout: (UICollectionViewLayout*)collectionViewLayout
minimumInteritemSpacingForSectionAtIndex: (NSInteger)section{
if (section == 0) {
return 5.0f;
}
return 20.0f;
}
5.設(shè)置Header View和Footer View的大小的回調(diào)如下。
Objective-C
/**
* headerView的大小
*/
- (CGSize)collectionView: (UICollectionView *)collectionView
layout: (UICollectionViewLayout*)collectionViewLayout
referenceSizeForHeaderInSection: (NSInteger)section{
return CGSizeMake(200, 50);
}
/**
* footerView的大小
*/
- (CGSize)collectionView: (UICollectionView *)collectionView
layout: (UICollectionViewLayout*)collectionViewLayout
referenceSizeForFooterInSection: (NSInteger)section{
return CGSizeMake(200, 50);
}
上面的方法就是UICollectionViewDelegateFlowLayout中所有的方法了,負(fù)責(zé)布局顯示的。
四、UICollectionViewDelegate回調(diào)實(shí)現(xiàn)
UICollectionViewDelegate中的代理方法主要是負(fù)責(zé)Cell的交互的,比如是否高亮,是否選,是否可編輯等,接下來(lái)要為大家詳細(xì)的介紹UICollectionViewDelegate中的代理方法。
1.為了這部分的效果展示,我們需要對(duì)Cell添加一些控件,并且設(shè)置其Highlight和Selected的一些狀態(tài)。為Cell添加上ImageView, Cell的高亮狀態(tài)和非高亮狀態(tài)對(duì)應(yīng)的ImageView上的圖片是不同的。再添加一個(gè)Button, 并為Button設(shè)置Selected和Default狀態(tài)下的圖片,Button的選中和默認(rèn)狀態(tài)由Cell的選中狀態(tài)來(lái)定。Cell中改變ImageView的圖片的代碼如下所示,函數(shù)傳入的參數(shù)是當(dāng)前Cell的高亮狀態(tài),根據(jù)高亮狀態(tài)來(lái)設(shè)置ImageView上的Image。(有的小伙伴會(huì)問(wèn)為什么給ImageView在Default狀態(tài)和Highlight下設(shè)置不同的圖片,然后直接改變ImageView的高亮狀態(tài)即可。你可以試一下,達(dá)不到預(yù)期的效果)
Objective-C
- (void) changeHighLightWithBool: (BOOL) highlight{
NSString *imageName = @"003.jpg";
if (highlight) {
imageName = @"002.jpg";
}
[_highlightImage setImage: [UIImage imageNamed:imageName]];
}
2.設(shè)置Cell可以高亮, 返回YES代表Cell可以高亮,返回NO代表Cell不可高亮。高亮就是觸摸Cell時(shí)該Cell變?yōu)楦吡翣顟B(tài),在代碼中的反應(yīng)就是Cell的Highligth屬性變?yōu)閅ES。而觸摸結(jié)束時(shí),Cell的Highligth屬性就變?yōu)镹O。
Objective-C
#pragma mark
/**
* Cell是否可以高亮
*/
- (BOOL)collectionView: (UICollectionView *)collectionView
shouldHighlightItemAtIndexPath: (NSIndexPath *)indexPath{
return YES;
}
3.下面這個(gè)方法是自己寫的,用來(lái)在界面上反應(yīng)Cell的高亮狀態(tài)。 ImageView在當(dāng)前Cell高亮狀態(tài)下和非高亮狀態(tài)下所加載的圖片不同,所以可以看出Cell高亮和非高亮。
Objective-C
/**
* 根據(jù)高亮狀態(tài)修改背景圖片
*/
- (void) changeHighlightCellWithIndexPaht: (NSIndexPath *) indexPath{
//獲取當(dāng)前變化的Cell
CollectionViewCell *currentHighlightCell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
[currentHighlightCell changeHighLightWithBool:currentHighlightCell.highlighted];
if (currentHighlightCell.highlighted == YES){
NSLog(@"第%ld個(gè)Section上第%ld個(gè)Cell變?yōu)楦吡?,indexPath.section ,indexPath.row);
return;
}
if (currentHighlightCell.highlighted == NO){
NSLog(@"第%ld個(gè)Section上第%ld個(gè)Cell變?yōu)榉歉吡?,indexPath.section ,indexPath.row);
}
}
4.Cell從非高亮變?yōu)楦吡翣顟B(tài)時(shí)回調(diào)用下面的方法,為了反映Cell的高亮狀態(tài),我們?nèi)ジ淖円幌翪ell上ImageView的圖片。
Objective-C
/**
* 如果Cell可以高亮,Cell變?yōu)楦吡梁笳{(diào)用該方法
*/
- (void)collectionView: (UICollectionView *)collectionView
didHighlightItemAtIndexPath: (NSIndexPath *)indexPath{
[self changeHighlightCellWithIndexPath:indexPath];
}
/**
* 如果Cell可以高亮,Cell從高亮變?yōu)榉歉吡琳{(diào)用該方法
*/
- (void)collectionView: (UICollectionView *)collectionView
didUnhighlightItemAtIndexPath: (NSIndexPath *)indexPath{
[self changeHighlightCellWithIndexPath:indexPath];
}
5.設(shè)定Cell是否可選的回調(diào)如下所示,Cell被選中時(shí)該Cell的Selected為YES, 取消選中Selected為NO;
Objective-C
/**
* Cell是否可以選中
*/
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
- 如果想讓你的Cell支持多選,就需要設(shè)定一下CollectionView的allowsMultipleSelection屬性,下面的代碼是在ViewDidLoad中添加的,如下所示:
Objective-C
//設(shè)置Cell多選
self.collectionView.allowsMultipleSelection = YES;
7.如果在多選狀態(tài)下需要支持取消Cell的多選,那么就去執(zhí)行下面的方法,并返回YES。就是支持在多選狀態(tài)下取消選中狀態(tài)。
Objective-C
/**
* Cell多選時(shí)是否支持取消功能
*/
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
8.下面這個(gè)方法是自己封裝的,用來(lái)根據(jù)Cell的選中狀態(tài)來(lái)改變Cell上Button的選中狀態(tài),具體代碼實(shí)現(xiàn)如下:
Objective-C
/**
* Cell根據(jù)Cell選中狀態(tài)來(lái)改變Cell上Button按鈕的狀態(tài)
*/
- (void) changeSelectStateWithIndexPath: (NSIndexPath *) indexPath{
//獲取當(dāng)前變化的Cell
CollectionViewCell *currentSelecteCell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
currentSelecteCell.selectButton.selected = currentSelecteCell.selected;
if (currentSelecteCell.selected == YES){
NSLog(@"第%ld個(gè)Section上第%ld個(gè)Cell被選中了",indexPath.section ,indexPath.row);
return;
}
if (currentSelecteCell.selected == NO){
//NSLog(@"第%ld個(gè)Section上第%ld個(gè)Cell取消選中",indexPath.section ,indexPath.row);
}
}
9.在Cell選中和取消選中時(shí)都會(huì)調(diào)用上面的方法來(lái)改變Button的選中狀態(tài),下面是Cell在選中時(shí)以及取消選中時(shí)所調(diào)用的方法:
Objective-C
/**
* Cell選中調(diào)用該方法
*/
- (void)collectionView: (UICollectionView *)collectionView
didSelectItemAtIndexPath: (NSIndexPath *)indexPath{
[self changeSelectStateWithIndexPath:indexPath];
}
/**
* Cell取消選中調(diào)用該方法
*/
- (void)collectionView: (UICollectionView *)collectionView didDeselectItemAtIndexPath: (NSIndexPath *)indexPath{
[self changeSelectStateWithIndexPath:indexPath];
}
10.下方四個(gè)方法是Cell將要出現(xiàn),Cell出現(xiàn)后,Supplementary View將要出現(xiàn)以及Supplementary View已經(jīng)出現(xiàn)所調(diào)用的方法,具體信息請(qǐng)看下方代碼實(shí)現(xiàn):
Objective-C
/**
* Cell將要出現(xiàn)的時(shí)候調(diào)用該方法
*/
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0){
NSLog(@"第%ld個(gè)Section上第%ld個(gè)Cell將要出現(xiàn)",indexPath.section ,indexPath.row);
}
/**
* Cell出現(xiàn)后調(diào)用該方法
*/
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath{
NSLog(@"第%ld個(gè)Section上第%ld個(gè)Cell已經(jīng)出現(xiàn)",indexPath.section ,indexPath.row);
}
/**
* headerView或者footerView將要出現(xiàn)的時(shí)候調(diào)用該方法
*/
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0){
NSLog(@"第%ld個(gè)Section上第%ld個(gè)擴(kuò)展View將要出現(xiàn)",indexPath.section ,indexPath.row);
}
/**
* headerView或者footerView出現(xiàn)后調(diào)用該方法
*/
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath{
NSLog(@"第%ld個(gè)Section上第%ld個(gè)擴(kuò)展View已經(jīng)出現(xiàn)",indexPath.section ,indexPath.row);
}
在UICollectionViewDelegate回調(diào)方法中還有三個(gè)回調(diào)方法是關(guān)于Cell編輯的,比如copy, past, cut等操作,具體代碼就不在此贅述了。在Demo中給出了實(shí)現(xiàn)方式,主要涉及到UIPasteboard的操作,本篇博客的整體的Demo回分享到Github上,下方是Github上的分享鏈接,感興趣的小伙伴可以進(jìn)行Clone。
參考文章:iOS開發(fā)之窺探UICollectionViewController(二) :詳解CollectionView各種回調(diào)