【IOS開發(fā)基礎系列】UICollectionView專題

1 定義

1.1 什么是UICollectionView

????????UICollectionView是一種新的數(shù)據(jù)展示方式啦扬,簡單來說可以把他理解成多列的UITableView(請一定注意這是UICollectionView的最最簡單的形式)垒迂。

????????最簡單的UICollectionView就是一個GridView票髓,可以以多列的方式將數(shù)據(jù)進行展示。標準的UICollectionView包含三個部分管行,它們都是UIView的子類:

????? Cells 用于展示內容的主體,對于不同的cell可以指定不同尺寸和不同的內容,這個稍后再說

????? Supplementary Views 追加視圖 如果你對UITableView比較熟悉的話起胰,可以理解為每個Section的Header或者Footer,用來標記每個section的view

????? Decoration Views 裝飾視圖 這是每個section的背景巫延,比如iBooks中的書架就是這個

????????不管一個UICollectionView的布局如何變化效五,這三個部件都是存在的。再次說明炉峰,復雜的UICollectionView絕不止上面的幾幅圖畏妖,關于較復雜的布局和相應的特性,我會在本文稍后和下一篇筆記中進行一些深入疼阔。


1.2 UICollectionViewDataSource

????? section的數(shù)量-numberOfSectionsInCollection:

????? 某個section里有多少個item-collectionView: numberOfItemsInSection:

????? 對于某個位置應該顯示什么樣的cell -collectionView: cellForItemAtIndexPath:

????????實現(xiàn)以上三個委托方法戒劫,基本上就可以保證CollectionView工作正常了。當然婆廊,還有提供Supplementary View的方法迅细。

????? collectionView: viewForSupplementaryElementOfKind: atIndexPath:

????????對于Decoration Views,提供方法并不在UICollectionViewDataSource中淘邻,而是直接在UICollectionViewLayout類中的(因為它僅僅是視圖相關茵典,而與數(shù)據(jù)無關),放到稍后再說宾舅。


1.3 關于重用

????????為了得到高效的View统阿,對于cell的重用是必須的,避免了不斷生成和銷毀對象的操作筹我,這與在UITableView中的情況是一致的扶平。但值得注意的時,在UICollectionView中崎溃,不僅cell可以重用蜻直,Supplementary View和Decoration View也是可以并且應當被重用的。在iOS5中袁串,Apple對UITableView的重用做了簡化概而,以往要寫類似這樣的代碼:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MY_CELL_ID"];

//如果沒有可重用的cell,那么生成一個?

if (!cell) {

????cell = [[UITableViewCell alloc] init];

}

//配置cell囱修,blablabla

return cell;


????????而如果我們在TableView向數(shù)據(jù)源請求數(shù)據(jù)之前使用-registerNib:forCellReuseIdentifier:方法為@“MY_CELL_ID”注冊過nib的話赎瑰,就可以省下每次判斷并初始化cell的代碼,要是在重用隊列里沒有可用的cell的話破镰,runtime將自動幫我們生成并初始化一個可用的cell餐曼。

????????這個特性很受歡迎压储,因此在UICollectionView中Apple繼承使用了這個特性,并且把其進行了一些擴展源譬。使用以下方法進行注冊:

????? -registerClass: forCellWithReuseIdentifier:

????? -registerClass: forSupplementaryViewOfKind: withReuseIdentifier:

????? -registerNib: forCellWithReuseIdentifier:

????? -registerNib: forSupplementaryViewOfKind: withReuseIdentifier:

????????相比UITableView有兩個主要變化:一是加入了對某個Class的注冊集惋,這樣即使不用提供nib而是用代碼生成的view也可以被接受為cell了;二是不僅只是cell踩娘,Supplementary View也可以用注冊的方法綁定初始化了刮刑。在對collection view的重用ID注冊后,就可以像UITableView那樣簡單的寫cell配置了:

????MyCell*cell=[cvdequeueReusableCellWithReuseIdentifier:@”MY_CELL_ID”];

????// Configure the cell's content

????cell.imageView.image=...

????return cell;

}

????????需要吐槽的是养渴,對collection view雷绢,取重用隊列的方法的名字和UITableView里面不一樣了,在Identifier前面多加了Reuse五個字母理卑,語義上要比以前清晰翘紊,命名規(guī)則也比以前嚴謹了。


1.4 UICollectionViewDelegate

????????數(shù)據(jù)無關的view的外形啊藐唠,用戶交互啊什么的帆疟,由UICollectionViewDelegate來負責:

????? cell的高亮

????? cell的選中狀態(tài)

????? 可以支持長按后的菜單

????????關于用戶交互,UICollectionView也做了改進中捆。每個cell現(xiàn)在有獨立的高亮事件和選中事件的delegate鸯匹,用戶點擊cell的時候,現(xiàn)在會按照以下流程向delegate進行詢問:

????? -collectionView: shouldHighlightItemAtIndexPath: 是否應該高亮泄伪?

????? -collectionView: didHighlightItemAtIndexPath: 如果1回答為是殴蓬,那么高亮

????? -collectionView: shouldSelectItemAtIndexPath: 無論1結果如何,都詢問是否可以被選中蟋滴?

????? -collectionView: didUnhighlightItemAtIndexPath: 如果1回答為是染厅,那么現(xiàn)在取消高亮

????? -collectionView: didSelectItemAtIndexPath: 如果3回答為是,那么選中cell

????????狀態(tài)控制要比以前靈活一些津函,對應的高亮和選中狀態(tài)分別由highlighted和selected兩個屬性表示肖粮。


1.5 關于Cell

????????相對于UITableViewCell來說,UICollectionViewCell沒有這么多花頭尔苦。首先UICollectionViewCell不存在各式各樣的默認的style汤锨,這主要是由于展示對象的性質決定的咱旱,因為UICollectionView所用來展示的對象相比UITableView來說要來得靈活镣隶,大部分情況下更偏向于圖像而非文字隙笆,因此需求將會千奇百怪。因此SDK提供給我們的默認的UICollectionViewCell結構上相對比較簡單稠项,由下至上:

????? 首先是cell本身作為容器view

????? 然后是一個大小自動適應整個cell的backgroundView涯雅,用作cell平時的背景

????? 再其上是selectedBackgroundView,是cell被選中時的背景

????? 最后是一個contentView展运,自定義內容應被加在這個view上

????????這次Apple給我們帶來的好康是被選中cell的自動變化活逆,所有的cell中的子view精刷,也包括contentView中的子view,在當cell被選中時蔗候,會自動去查找view是否有被選中狀態(tài)下的改變怒允。比如在contentView里加了一個normal和selected指定了不同圖片的imageView,那么選中這個cell的同時這張圖片也會從normal變成selected琴庵,而不需要額外的任何代碼误算。


1.6 UICollectionViewLayout

????????這是UICollectionView和UITableView最大的不同。UICollectionViewLayout可以說是UICollectionView的大腦和中樞迷殿,它負責了將各個cell、Supplementary View和Decoration Views進行組織咖杂,為它們設定各自的屬性庆寺,包括但不限于:

????? 位置

????? 尺寸

????? 透明度

????? 層級關系

????? 形狀

????? 等等等等…

????????Layout決定了UICollectionView是如何顯示在界面上的。在展示之前诉字,一般需要生成合適的UICollectionViewLayout子類對象懦尝,并將其賦予CollectionView的collectionViewLayout屬性。關于詳細的自定義UICollectionViewLayout和一些細節(jié)壤圃,我將寫在之后一篇筆記中陵霉。

????????Apple為我們提供了一個最簡單可能也是最常用的默認layout對象,UICollectionViewFlowLayout伍绳。Flow Layout簡單說是一個直線對齊的layout踊挠,最常見的Grid View形式即為一種Flow Layout配置。上面的照片架界面就是一個典型的Flow Layout冲杀。

????? 首先一個重要的屬性是itemSize效床,它定義了每一個item的大小。通過設定itemSize可以全局地改變所有cell的尺寸权谁,如果想要對某個cell制定尺寸剩檀,可以使用-collectionView: layout: sizeForItemAtIndexPath:方法。

? ? ? 間隔 可以指定item之間的間隔和每一行之間的間隔旺芽,和size類似沪猴,有全局屬性,也可以對每一個item和每一個section做出設定:

@property (CGSize) minimumInteritemSpacing

@property (CGSize) minimumLineSpacing

-collectionView: layout: minimumInteritemSpacingForSectionAtIndex:

-collectionView: layout: minimumLineSpacingForSectionAtIndex:

????? 滾動方向 由屬性scrollDirection確定scroll view的方向采章,將影響Flow Layout的基本方向和由header及footer確定的section之間的寬度

????????UICollectionViewScrollDirectionVertical

????????UICollectionViewScrollDirectionHorizontal

????? Header和Footer尺寸 同樣地分為全局和部分运嗜。需要注意根據(jù)滾動方向不同,header和footer的高和寬中只有一個會起作用共缕。垂直滾動時section間寬度為該尺寸的高洗出,而水平滾動時為寬度起作用,如圖图谷。

????@property (CGSize) headerReferenceSize

????@property (CGSize) footerReferenceSize

????- collectionView: layout: referenceSizeForHeaderInSection:

????- collectionView: layout: referenceSizeForFooterInSection:

????? 縮進

????@property UIEdgeInsets sectionInset;

????- collectionView: layout: insetForSectionAtIndex:


1.7 總結

????????一個UICollectionView的實現(xiàn)包括兩個必要部分:UICollectionViewDataSource和UICollectionViewLayout翩活,和一個交互部分:UICollectionViewDelegate阱洪。而Apple給出的UICollectionViewFlowLayout已經(jīng)是一個很強力的layout方案了。


2 實現(xiàn)

2.1 UICollectionView實例的代碼初始化化

????NSUInteger collectionViewHeight = (_myCellVMArray.count + 2)/3 * kMyInfoCollectionCellHeight + 10;

??? //確定是水平滾動菠镇,還是垂直滾動

??? UICollectionViewFlowLayout *flowLayout=[[UICollectionViewFlowLayout alloc] init];

??? [flowLayout setScrollDirection: UICollectionViewScrollDirectionVertical];


??? _myCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, _myAccView.frame.origin.y + _myAccView.frame.size.height, self.view.frame.size.width, collectionViewHeight) collectionViewLayout: flowLayout];

??? _myCollectionView.delegate = self;

??? _myCollectionView.dataSource = self;

??? [_myCollectionView registerClass: [HJMyInfoCollectionCell class] forCellWithReuseIdentifier: kMyCollectionCellIdentifier];

??? [self.view addSubview: _myCollectionView];


2.2 UICollectionView加載過程

????????總的加載過程是先加載UICollectionView的委托回調配置冗荸,再初始化UICollectionViewCell。

????1利耍、設置numberOfSectionsInCollectionView蚌本;

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView

{

??? return 1;

}


????2、設置numberOfItemsInSection隘梨;

- (NSInteger) collectionView: (UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

{

??? return [_myCellVMArray count];

}


????3程癌、設置sizeForItemAtIndexPath;

//定義每個UICollectionViewCell 的大小

- (CGSize) collectionView: (UICollectionView *)collectionView layout: (UICollectionViewLayout*) collectionViewLayout sizeForItemAtIndexPath: (NSIndexPath*)indexPath

{

??? NSInteger width = kCollectionViewCellWidth;

??? return CGSizeMake(width , kInfoCollectionCellHeight);

}


????4轴猎、設置insetForSectionAtIndex嵌莉;

//定義每個UICollectionViewCell 的margin

-(UIEdgeInsets) collectionView: (UICollectionView *)collectionView layout: (UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex: (NSInteger)section

{

??? return UIEdgeInsetsMake(2, 2, 2, 2);

}


????5、配置cellForItemAtIndexPath

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath

{

? ? HJInfoCollectionCell *cell= (HJInfoCollectionCell *)[_myInfoCollectionView dequeueReusableCellWithReuseIdentifier: kInfoCollectionCellIdentifier forIndexPath: indexPath];


??? if(!cell) {

??????? cell = [[HJInfoCollectionCell alloc] initWithFrame:CGRectMake(0, 0, kInfoCollectionViewCellWidth, kInfoCollectionCellHeight)];

??? }

??? cell.infoVM = [_myAccCellVMArray objectAtIndex: indexPath.row];


??? return cell;

}


2.3 UICollectionViewCell重用

????????UICollectionViewCell其實只實例化了能應付一屏顯示范圍的對象實例捻脖,而在cellForItemAtIndexPath方法中重復更新此實例的業(yè)務數(shù)據(jù)來達到顯示不同Cell的目的锐峭。

????????UICollectionViewCell的實例化是通過dequeueReusableCellWithReuseIdentifier來實現(xiàn),代碼示例如下:

HJInfoCollectionCell *cell= (HJInfoCollectionCell *) [_myInfoCollectionView dequeueReusableCellWithReuseIdentifier: kInfoCollectionCellIdentifier forIndexPath: indexPath];


????????在dequeueReusableCell方法中可婶,程序會調用Cell的- (instancetype) initWithFrame:(CGRect)frame實現(xiàn)對象的實例化沿癞。因此自定義Cell的初始化方法實現(xiàn)- (instancetype)initWithFrame:(CGRect)frame就可以了,而不用像筆者一樣去畫蛇添足地實現(xiàn)一個-(instancetype) initWithFrame: (CGRect) frame WithVM:(HJInfoCellVM *) vm方法矛渴,后面這個方法永遠也調用不到椎扬。

? ? ? ? 而對于業(yè)務數(shù)據(jù)的綁定與頁面控件更新邏輯,筆者建議在對應業(yè)務對象的Set方法中實現(xiàn)曙旭,例如上面的HJInfoCellVM業(yè)務對象盗舰,就在其Set方法中實現(xiàn)就可以了,示例如下:

- (void)setInfoVM: (HJInfoCellVM*)infoVM

{

??? _infoVM= infoVM;

??? [self reloadSubViews];

}


3 開發(fā)技巧

3.1 布局技巧

3.1.1 設置每一行顯示cell個數(shù)

????????需要用到UICollectionViewFlowLayout:

HJCollectionViewEqualWidthFlowLayout *flowLayout = [[HJCollectionViewEqualWidthFlowLayout alloc] initWithItemCountPerRow: kShareItemCountPerRow withItemSize: itemSize];

???flowLayout.maximumSpacing = 1;

???flowLayout.minimumLineSpacing = 1;

???flowLayout.minimumInteritemSpacing = 1;

??? _collectionView.collectionViewLayout = flowLayout;


3.1.2 單元格等間距實現(xiàn)

??? 繼承并自定義實現(xiàn)layoutAttributesForElementsInRect方法

@interface HJCollectionViewEqualWidthFlowLayout : UICollectionViewFlowLayout

@property (nonatomic) floatmaximumSpacing;


- (instancetype) initWithItemCountPerRow: (NSInteger) count WithItemSize: (CGSize) size;

-(void) setItemCountPerRow: (NSInteger) count;


@end


-(NSArray*) layoutAttributesForElementsInRect: (CGRect)rect

{

??? NSMutableArray* attributes = [[super layoutAttributesForElementsInRect: rect] mutableCopy];


??? for (UICollectionViewLayoutAttributes *attr in attributes) {

??????? NSLog(@"%@", NSStringFromCGRect([attr frame]));

??? }


??? int j = 0, k= 0;


??? //我們想設置的最大間距桂躏,可根據(jù)需要改

??? NSInteger maximumSpacing = _maximumSpacing;


??? for(int i = 0; i < [attributes count]; ++i) {

??????? //當前attributes

??????? UICollectionViewLayoutAttributes *currentLayoutAttributes = attributes[i];


??????? k = i %_itemCountPerRow;

??????? j = i /_itemCountPerRow;

??????? CGRect frame = currentLayoutAttributes.frame;


??????? frame.origin.x = k * (self.itemSize.width+ maximumSpacing);

??????? frame.origin.y = j * (self.itemSize.height+ maximumSpacing);


??????? currentLayoutAttributes.frame= frame;

??? }


??? return attributes;

}


3.1.3 單元格間隔線

????????自定義Cell钻趋,將ContentView的Frame設置稍小

- (void) initSubViews

{

??? self.contentView.frame = CGRectMake(0, 0, self.frame.size.width - 1, self.frame.size.height - 1);

??? self.backgroundColor = [UIColor colorWithRed: 224.0/255 green: 224.0/255 blue: 224.0/255 alpha:1.0];


??? _infoImageView = [[UIImageView alloc] initWithFrame: CGRectMake(0, 0, self.contentView.frame.size.width, self.contentView.frame.size.height)];

??? [self.contentView addSubview: _infoImageView];

}


3.1.4 ItemSize設置錯誤導致單元格加載不完全問題

CGSize itemSize = CGSizeMake([UIScreen mainScreen].bounds.size.width / 2.0-0.5, kProductListCellHeight - 1);

_flowLayout = [[HJCollectionViewEqualWidthFlowLayout alloc] initWithItemCountPerRow: kRow WithItemSize: itemSize];

_flowLayout.maximumSpacing = 1;

_flowLayout.minimumLineSpacing = 1;

_flowLayout.minimumInteritemSpacing = 1;

_collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout: _flowLayout];


要點1:單元格尺寸計算時要來考慮間隔線寬度的影響;

要點2:minimumLineSpacing與minimumInteritemSpacing屬性必須成對使用剂习;


4 參考鏈接

UICollectionView詳解

http://blog.csdn.net/majiakun1/article/details/17204693


UICollectionView的使用方法及demo

http://www.tuicool.com/articles/QJjeqy


iOS開發(fā)-UICollectionView詳解+實例

http://www.cnblogs.com/ios8/p/iOS-UICollectionView.html


iOS開發(fā) 純代碼創(chuàng)建UICollectionView

http://jingyan.baidu.com/article/eb9f7b6d8a81a5869364e8a6.html


UICollectionView不同大小Cell等間距的實現(xiàn)

http://www.th7.cn/Program/IOS/201505/449935.shtml


UICollectionView cell橫向間距如何調整(列距調整)??

http://www.cocoachina.com/bbs/read.php?tid-245356.html


ios開發(fā)——解決UICollectionView的cell間距與設置不符問題

http://www.bkjia.com/IOSjc/917782.html


UICollectionView使用小結

http://web.gxzj.com.cn/News.aspx?id=308277

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蛮位,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鳞绕,更是在濱河造成了極大的恐慌失仁,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件们何,死亡現(xiàn)場離奇詭異萄焦,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門拂封,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茬射,“玉大人,你說我怎么就攤上這事冒签≡谂祝” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵萧恕,是天一觀的道長刚梭。 經(jīng)常有香客問我,道長票唆,這世上最難降的妖魔是什么朴读? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮走趋,結果婚禮上磨德,老公的妹妹穿的比我還像新娘。我一直安慰自己吆视,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布酥宴。 她就那樣靜靜地躺著啦吧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拙寡。 梳的紋絲不亂的頭發(fā)上授滓,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音肆糕,去河邊找鬼般堆。 笑死,一個胖子當著我的面吹牛诚啃,可吹牛的內容都是我干的淮摔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼始赎,長吁一口氣:“原來是場噩夢啊……” “哼和橙!你這毒婦竟也來了?” 一聲冷哼從身側響起造垛,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤魔招,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后五辽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體办斑,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年杆逗,在試婚紗的時候發(fā)現(xiàn)自己被綠了乡翅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鳞疲。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖峦朗,靈堂內的尸體忽然破棺而出建丧,到底是詐尸還是另有隱情,我是刑警寧澤波势,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布翎朱,位于F島的核電站,受9級特大地震影響尺铣,放射性物質發(fā)生泄漏拴曲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一凛忿、第九天 我趴在偏房一處隱蔽的房頂上張望澈灼。 院中可真熱鬧,春花似錦店溢、人聲如沸叁熔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荣回。三九已至,卻和暖如春戈咳,著一層夾襖步出監(jiān)牢的瞬間心软,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工著蛙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留删铃,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓踏堡,卻偏偏與公主長得像猎唁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子暂吉,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容