UICollectionView

UICollectionView和UITableView很類似毁习,不過對于我個人來講嵌牺,UITableView是經(jīng)常用到的東西俏险,UICollectionView使用較少仇轻,所以這篇文章講UICollectionView鸟妙。

1.類和協(xié)議

1).UICollectionViewController:與UITableViewController功能類似
2).UICollectionViewCell:與UITableViewCell功能類似,同樣有ReuseIdentifier,所以它也有復用機制焦人。

從storyBoard中出列:

MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"myCell" forIndexPath:indexPath];
cell.cellLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.item];

從nib中注冊:

[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];

3).UICollectionViewDataSource:數(shù)據(jù)源協(xié)議

4).UICollectionViewDelegate:處理包含選中事件的各種方法的協(xié)議

5).UICollectionViewDelegateFlowLayout:這是UICollectionView和UITableView不同的地方,它可以用來定制一些布局重父。

2.例子

1).初始化

新建一個工程花椭,刪除ViewController類,將storyBoard中的ViewController替換為UICollectionViewController房午。
像往常一樣矿辽,你的主要內(nèi)容顯示在 cell 中,cell 可以被任意分組到 section 中。Collection view 的 cell 必須是 UICollectionViewCell 的子類袋倔。所以我們新建UICollectionViewController與UICollectionViewCell的子類雕蔽,將storyBoard中UICollectionViewController的custom class設(shè)置為MyCollectionViewController,將UICollectionViewCell的custom class設(shè)置為MyCollectionViewCell奕污。

在UICollectionViewCell中新增如下圖兩個控件萎羔,UIImageView和UILabel

不要忘記設(shè)置cell的Identifier:

建立兩個IBOutlet:

2).實現(xiàn)數(shù)據(jù)源方法

MyCollectionViewController.m:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 20;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"myCell" forIndexPath:indexPath];
    cell.cellLabel.text = [NSString stringWithFormat:@"%ld",(long)indexPath.item];
    
    return cell;
}

配置cell,MyCollectionViewCell.m

-(void)awakeFromNib{
    [super awakeFromNib];
    self.backgroundColor = [UIColor randomColor];

}

現(xiàn)在運行碳默,如下圖:

IMG_0939.PNG

旋轉(zhuǎn)屏幕后柵格會自動旋轉(zhuǎn)并對齊:

IMG_0940.PNG

2).實現(xiàn)委托方法

a.高亮

在cell中添加一個selectedBackgroundView視圖:

-(void)awakeFromNib{
    [super awakeFromNib];
    self.selectedBackgroundView = [[UIView alloc]initWithFrame:self.frame];
    self.selectedBackgroundView.backgroundColor = [UIColor blackColor];
    
    self.backgroundColor = [UIColor randomColor];
}

實現(xiàn)以下代理方法:

-(BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath{
    return YES;
}

//放大縮小效果
-(void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell *selectedCell = [collectionView cellForItemAtIndexPath:indexPath];
    [UIView animateWithDuration:kAnimationDuration animations:^{
        selectedCell.transform = CGAffineTransformMakeScale(2.0f, 2.0f);
    }];
}

-(void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell *selectedCell = [collectionView cellForItemAtIndexPath:indexPath];
    [UIView animateWithDuration:kAnimationDuration animations:^{
        selectedCell.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
    }];
}

現(xiàn)在按下collectionCell會顯示高亮狀態(tài):背景顏色變黑色贾陷,且有一個彈跳的放大縮小效果。

b.選中

如上右邊新建一個MyDetailsViewController嘱根,并且從左邊控制器中segue到MyDetailsViewController髓废。

MyDetailsViewController.m

-(IBAction) doneTapped:(id) sender {
  [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.imageView.image = [UIImage imageNamed:@"image"];
}

實現(xiàn)以下代理方法:
MyCollectionViewController.m

-(BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    return YES;
}

-(void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    dispatch_time_t delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, (int64_t)1*NSEC_PER_SEC);
    dispatch_after(delayInNanoSeconds, dispatch_get_main_queue(), ^{
        [self performSegueWithIdentifier:@"MainSegue" sender:indexPath];
    });
}

這樣在高亮效果1秒后會進行視圖切換。

4).添加頭部和尾部視圖

collection view 額外管理著兩種視圖:supplementary views 该抒, Supplementary views 相當于 table view 的 section header 和 footer views慌洪。像 cells 一樣,他們的內(nèi)容都由數(shù)據(jù)源對象驅(qū)動凑保。然而和 table view 中用法不一樣冈爹,supplementary view 并不一定會作為 header 或 footer view;他們的數(shù)量和放置的位置完全由布局控制欧引。

Supplementary views必須是 UICollectionReusableView的子類频伤。布局使用的每個視圖類都需要在 collection view 中注冊,這樣當 data source 讓它們從 reuse pool 中出列時芝此,它們才能夠創(chuàng)建新的實例憋肖。首先我們需要在storyBoard中啟用"Section Header"和"Section Footer"

之后XCode會自動生成兩個UICollectionResuableView到視圖中:

然后同樣的你可以設(shè)置Identifier,然后在以下代理方法中dequeue即可婚苹,確實很分別岸更,相比UITableView又進一步封裝。

-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
          viewForSupplementaryElementOfKind:(NSString *)kind
                                atIndexPath:(NSIndexPath *)indexPath{
    NSString *resueIndentifier = kCollectionViewHeaderIndentifier;
    UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:resueIndentifier forIndexPath:indexPath];
    return  [collectionView dequeueReusableSupplementaryViewOfKind: UICollectionElementKindSectionFooter
                                               withReuseIdentifier:SupplementaryViewIdentifier
                                                      forIndexPath:indexPath];
}

在這個demo膊升,我演示下如何通過加載自定義的nib控件來添加頭部和尾部視圖怎炊,如下我們新建兩個自定義nib控件:

MyCollectionViewController中加載并注冊nib:

-(void)awakeFromNib{
    UINib *headerNib = [UINib nibWithNibName:NSStringFromClass([Header class]) bundle:[NSBundle mainBundle]];
    [self.collectionView registerNib:headerNib forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kCollectionViewHeaderIndentifier];
    
    UINib *footerNib = [UINib nibWithNibName:NSStringFromClass([Footer class]) bundle:[NSBundle mainBundle]];
    [self.collectionView registerNib:footerNib forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:kCollectionViewFooterIndentifier];
}

代理方法類似:

-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
          viewForSupplementaryElementOfKind:(NSString *)kind
                                atIndexPath:(NSIndexPath *)indexPath{
    NSString *resueIndentifier = kCollectionViewHeaderIndentifier;
    if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
        resueIndentifier = kCollectionViewFooterIndentifier;
    }
    
    UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:resueIndentifier forIndexPath:indexPath];
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
        Header *header = (Header *)view;
        header.label.text = [NSString stringWithFormat:@"Section Header %lu",(unsigned long)indexPath.section+1];
    }else if ([kind isEqualToString:UICollectionElementKindSectionFooter]){
        Footer *footer = (Footer *)view;
        NSString *title = [NSString stringWithFormat:@"Section Footer %lu",(unsigned long)indexPath.section+1];
        [footer.button setTitle:title forState:UIControlStateNormal];
    }
    return  view;    
}

UICollectionView和UITableView最重要的區(qū)別就是UICollectionView并不知道如何布局,它把布局機制委托給了UICollectionViewLayout子類廓译,默認的布局方式是UICollectionFlowViewLayout類提供的流式布局(flow layout)结胀,也就是上面例子顯示的那樣子。這個類允許你通過UICollectionDelegateViewFlowLayout協(xié)議調(diào)整各自屬性责循。

不過你也可以創(chuàng)建自己的布局方式,通過繼承UICollectionViewLayout攀操,現(xiàn)在是一個例子院仿。

3.UICollectionViewLayout子類

上面的例子中,我們所有cell的大小都是一樣的,那如果我們的cell大小不一樣呢歹垫?我們需要實現(xiàn)UICollectionViewDelegateFlowLayout的協(xié)議方法collectionView:layout:sizeForItemAtIndexPath:剥汤,但這會使得效果就像下面左邊那張圖。它會計算每一排中的最大高度排惨,這樣會讓效果看起來不怎么樣吭敢。我們可以繼承UICollectionViewLayout來實現(xiàn)右圖中的效果。

我們新建一個UICollectionViewController暮芭,并把程序運行開始移到改控制器鹿驼。

像上面的例子那樣,顯示50個同樣大小的單元辕宏,具體上面已經(jīng)介紹了畜晰,之后它看起來像這樣:

現(xiàn)在實現(xiàn)UICollectionViewDelegateFlowLayout的協(xié)議方法隨機改變cell大小的高度:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {    
    CGFloat randomHeight = 80 + (arc4random() % 150);
    return CGSizeMake(80, randomHeight);
}

現(xiàn)在效果是這樣:

現(xiàn)在創(chuàng)建一個UICollectionViewLayout的子類:CustomCollectionViewLayout.首先我們需要像UICollectionViewDelegateFlowLayout一樣通過代理的方式來獲取特定indexPath上cell的高度。

@class CustomCollectionViewLayout;

@protocol CustomCollectionViewLayoutDelegate <NSObject>
@required
- (CGFloat) collectionView:(UICollectionView*) collectionView
                    layout:(CustomCollectionViewLayout*) layout
  heightForItemAtIndexPath:(NSIndexPath*) indexPath;
@end

子類需要覆蓋父類以下3個方法:

-(void) prepareLayout;
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
-(CGSize) collectionViewContentSize;

prepareLayout在布局開始之前會被調(diào)用瑞筐,我們需要在這個方法中計算邊框凄鼻,所以我們引入numberOfColumns 和 interItemSpacing兩個變量。分別是item每行的數(shù)目和item間的間距聚假,所以頭文件如下:

@interface CustomCollectionViewLayout : UICollectionViewLayout

@property (nonatomic, assign) NSUInteger numberOfColumns;
@property (nonatomic, assign) CGFloat interItemSpacing;
@property (weak, nonatomic) id<CustomCollectionViewLayoutDelegate> delegate;

@end

在開始布局前會執(zhí)行的方法prepareLayout中块蚌,我們需要計算每個item的frame值,并把它存入字典layoutInfo中膘格,然后峭范,在我們覆蓋父類的方法layoutAttributesForElementsInRect中,可以返回這個字典中的全部frame總值的數(shù)組闯袒。

在prepareLayout中虎敦,frame的height可以通過代理傳入:

CGFloat height = [((id<CustomCollectionViewLayoutDelegate>)self.collectionView.delegate)
                              collectionView:self.collectionView
                              layout:self
                              heightForItemAtIndexPath:indexPath];

frame的width則和numberOfColumns 和 interItemSpacing有關(guān),如下:

//計算Item的寬度
    CGFloat fullWidth = self.collectionView.frame.size.width;
    CGFloat availableSpaceExcludingPadding = fullWidth - (self.interItemSpacing * (self.numberOfColumns + 1));
    CGFloat itemWidth = availableSpaceExcludingPadding / self.numberOfColumns;

x軸和y軸則和當前的indexPath有關(guān)政敢,所以我們遍歷section和item其徙,得到x軸和y軸,并將之前的高度和寬度加起來得到frame值喷户。

    NSIndexPath *indexPath;
    NSInteger numSections = [self.collectionView numberOfSections];
    //遍歷section
    for(NSInteger section = 0; section < numSections; section++)  {
        NSInteger numItems = [self.collectionView numberOfItemsInSection:section];
        //遍歷item
        for(NSInteger item = 0; item < numItems; item++){
            indexPath = [NSIndexPath indexPathForItem:item inSection:section];
            UICollectionViewLayoutAttributes *itemAttributes =
            [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];  
            //計算x軸
            CGFloat x = self.interItemSpacing + (self.interItemSpacing + itemWidth) * currentColumn;
            //計算y軸
            CGFloat y = [self.lastYValueForColumn[@(currentColumn)] doubleValue];
            //通過協(xié)議回傳高度值
            CGFloat height = [((id<CustomCollectionViewLayoutDelegate>)self.collectionView.delegate)
                              collectionView:self.collectionView
                              layout:self
                              heightForItemAtIndexPath:indexPath];
            itemAttributes.frame = CGRectMake(x, y, itemWidth, height);
            //下一個item的y軸是當前y軸加上item高度唾那,并且加上間距
            y += height;
            y += self.interItemSpacing;
            
            //把下一個item的y軸記入到字典中
            self.lastYValueForColumn[@(currentColumn)] = @(y);
            
            currentColumn ++;
            if(currentColumn == self.numberOfColumns) currentColumn = 0;
            //將item的屬性記錄到字典中
            self.layoutInfo[indexPath] = itemAttributes;
        }
    }

然后在我們需要覆蓋的第二個方法中,使用enumerateKeysAndObjectsUsingBlock遍歷prepareLayout中的layoutInfo加入一個數(shù)組中:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *allAttributes = [NSMutableArray arrayWithCapacity:self.layoutInfo.count];
    [self.layoutInfo enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath,
                                                         UICollectionViewLayoutAttributes *attributes,
                                                         BOOL *stop) {
        if (CGRectIntersectsRect(rect, attributes.frame)) {
            [allAttributes addObject:attributes];
        }
    }];
    return allAttributes;
}

最后一個方法是計算collectionView的內(nèi)容大小褪尝,在第一個方法中闹获,我們已經(jīng)把下每個item的y軸記入到字典lastYValueForColumn中,所以我們通過do-while循環(huán)把這個最大的y值給取出來河哑,加上寬度值即可返回collectionView的內(nèi)容大小避诽。

-(CGSize) collectionViewContentSize {
    NSUInteger currentColumn = 0;
    CGFloat maxHeight = 0;
    do {
        //最大高度就是之前字典中的y軸
        CGFloat height = [self.lastYValueForColumn[@(currentColumn)] doubleValue];
        if(height > maxHeight)
            maxHeight = height;
        currentColumn ++;
    } while (currentColumn < self.numberOfColumns);
    
    return CGSizeMake(self.collectionView.frame.size.width, maxHeight);
}

Done!運行下效果如何:

你可以在這里下載完整的代碼璃谨。如果你覺得對你有幫助沙庐,希望你不吝嗇你的star:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鲤妥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拱雏,更是在濱河造成了極大的恐慌棉安,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铸抑,死亡現(xiàn)場離奇詭異贡耽,居然都是意外死亡,警方通過查閱死者的電腦和手機鹊汛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門蒲赂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柒昏,你說我怎么就攤上這事凳宙。” “怎么了职祷?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵氏涩,是天一觀的道長。 經(jīng)常有香客問我有梆,道長是尖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任泥耀,我火速辦了婚禮饺汹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痰催。我一直安慰自己兜辞,他們只是感情好,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布夸溶。 她就那樣靜靜地躺著逸吵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缝裁。 梳的紋絲不亂的頭發(fā)上扫皱,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音捷绑,去河邊找鬼韩脑。 笑死,一個胖子當著我的面吹牛粹污,可吹牛的內(nèi)容都是我干的段多。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼壮吩,長吁一口氣:“原來是場噩夢啊……” “哼衩匣!你這毒婦竟也來了蕾总?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤琅捏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后递雀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柄延,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年缀程,在試婚紗的時候發(fā)現(xiàn)自己被綠了搜吧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡杨凑,死狀恐怖滤奈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撩满,我是刑警寧澤蜒程,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站伺帘,受9級特大地震影響昭躺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伪嫁,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一领炫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧张咳,春花似錦帝洪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至婚陪,卻和暖如春族沃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泌参。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工脆淹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沽一。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓盖溺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铣缠。 傳聞我的和親對象是個殘疾皇子烘嘱,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 概述 UICollectionView是iOS開發(fā)中最常用的UI控件之一昆禽,可以用它來管理一組有序的不同尺寸的視圖,...
    漸z閱讀 2,977評論 0 3
  • UICollectionView是我們常說的集合視圖蝇庭,它在iOS 6中引入醉鳖,是iOS開發(fā)者中最受歡迎的UI元素之一...
    pro648閱讀 10,691評論 18 44
  • UICollectionView是一種類似于UITableView但又比UITableView功能更強大、更靈活的...
    浪漫紫薇星閱讀 1,089評論 0 0
  • 什么是UICollectionView哮内? UICollectionView是一種新的數(shù)據(jù)展示方式盗棵,簡單來說可以把他...
    凌峰Mical閱讀 43,351評論 11 201
  • 我有個師妹,做事認真踏實北发,我很喜歡纹因。前段她說工作不順心,想跳槽琳拨,正好我一個做外貿(mào)的朋友需要助理瞭恰,待遇優(yōu)厚,我就推薦...
    老滿講故事閱讀 330評論 0 0