手把手教你使用UICollectionView寫公司的項目

公司的UI圖

在很多app中都有這樣通用的頁面,一直沒有機會使用UICollectionView,只是簡單的看過他的使用方法。今天公司美工出圖,使用了他,并且遇到了好多的坑。記錄一下過程,不確定使用的方法是不是最優的,如果有更好的方案,一起討論,一起進步

理論篇

一.UICollectionViewLayout是做什么的?

1.1 在創建UITableView的時候,使用的是- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style用于判斷是普通還是分組
1.2 UICollectionViewLayout實際的作用是一樣的,是用來設置cell的布局的,初始化collectionView的時候,一定要給他設置這個屬性,否者不會顯示。UICollectionViewFlowLayoutUICollectionViewLayout的子類,給collectionView賦值的時候,一定要使用** UICollectionViewFlowLayout**初始化。
1.3 UICollectionViewFlowLayoutUICollectionViewLayout的關系就像是UIGestureRecognizerUITapGestureRecognizer的一樣。一個是父類,一個是子類。使用的時候都用子類

二. UICollectionViewLayout的屬性
每一個綠色款都是cell

如果都是固定的,建議生成layout對象的時候,設置全局屬性,(其布局很有意思,當你的cell設置大小后,一行多少個cell,由cell的寬度決定)

NS_CLASS_AVAILABLE_IOS(6_0) @interface UICollectionViewFlowLayout : UICollectionViewLayout  
  
//每行之間豎直之間的最小間距 (可以大于) 
@property (nonatomic) CGFloat minimumLineSpacing;

//同行的cell與cell之間水平之間的最小間距(可以)
@property (nonatomic) CGFloat minimumInteritemSpacing;  

//每個cell的尺寸,如果都是上圖的那種,整個collectionView都是同一種,那么可以用整個屬性,如果想我們公司那樣的樣式,不建議設置該屬性
@property (nonatomic) CGSize itemSize;  

//預估cell的尺寸,ios8之后可以先去預估cell的尺寸,然后去自適應
@property (nonatomic) CGSize estimatedItemSize NS_AVAILABLE_IOS(8_0); // defaults to CGSizeZero - setting a non-zero size enables cells that self-size via -perferredLayoutAttributesFittingAttributes:  

//滑動的方向,水平或者豎直,看到很多圖片瀏覽器都是用collectionview做出來的(注冊之后,可以復用),非常的好用!但是要記住,水平滑動只有collectionview有,tableview不支持的,默認豎直方法滑動
@property (nonatomic) UICollectionViewScrollDirection scrollDirection; // default is UICollectionViewScrollDirectionVertical  

//組頭組尾的size
@property (nonatomic) CGSize headerReferenceSize;  
@property (nonatomic) CGSize footerReferenceSize;  

//組的四周切的范圍
@property (nonatomic) UIEdgeInsets sectionInset;  
  
@end  

minimumLineSpacing 屬性詳解

綠色是minimumLineSpacing最新行間距
藍色是實際的行間距
在實際開發過程中,很可能行間距會是不同的

sectionInset 屬性詳解

sectionInset的配圖,每個組裝有很多的cell,默認該屬性是0,如圖
但是有的是時候我們會往里面切圖,整個組往里面切,里面的cell也跟著移動

注意,我剛才說的,如果所有的cell都是一樣尺寸,我們可以設置初始化layout之后,直接賦值,如果想我們公司那樣,隨意可能改變,建議看看下邊的代理方法

三. UICollectionViewLayout的代理方法

3.1 過去我們使用UITableView的時候,直接聲明數據源方法,和代理方法,
3.2 使用UICollectionView的時候,也要聲明兩個。
1.UICollectionViewDelegateFlowLayout
2.UICollectionViewDataSource
因為1中包含了3.UICollectionViewDelegate,所以可以省略3

#pragma mark - UICollectionViewDelegateFlowLayout  
//每個cell的大小,因為有indexPath,所以可以判斷哪一組,或者哪一個item,可一個給特定的大小,等同于layout的itemSize屬性
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {  
  return CGSizeMake(34,56);  
}  
  
// 設置整個組的縮進量是多少
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {  
  return UIEdgeInsetsMake(5, 5, 5, 5);  
}  
  
// 設置最小行間距,也就是前一行與后一行的中間最小間隔  
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {  
  return 10;  
}  
  
// 設置最小列間距,也就是左行與右一行的中間最小間隔  
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {  
  return 10;  
}  
  
// 設置section頭視圖的參考大小,與tableheaderview類似  
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {  
  return CGSizeMake(self.view.frame.size.width, 40);  
}  
  
// 設置section尾視圖的參考大小,與tablefooterview類似  
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section {  
  return CGSizeMake(self.view.frame.size.width, 40);  
}  
四. UICollectionView的組頭和組尾(頁眉和頁腳)
組頭和足尾

1.UICollectionView中非常明確是以為單位,可以設置組的組頭和尾巴,這里的頭尾還可以復用
2.復用的時候,首先頭尾view要繼承于
UICollectionReusableView,然后注冊(分為nib和class兩種)
3.用的時候通過collectionView去dequeue一下獲取,和cell的思路一樣
4.可以使用上文中的layout屬性直接設置組頭和組尾的size,也可以使用代理方法,去設置

五. UICollectionView的數據源方法

和tableview的數據源方法一樣,想要成為其數據源,然后聲明數據源

#pragma mark - UICollectionViewDataSource  
// 指定Section個數  
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {  
  return 3;  
}  
  
// 指定section中的collectionViewCell的個數  
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {  
  return 10;  
}  
  
// 配置section中的collectionViewCell的顯示  
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {  
  CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellIdentifier" forIndexPath:indexPath];  
  cell.backgroundColor = [UIColor redColor];  
  cell.textLabel.text = [NSString stringWithFormat:@"(%ld %ld)", indexPath.section, indexPath.row];  
    
  return cell;  
}  
六. UICollectionView的代理方法
#pragma mark - UICollectionViewDelegate  
// 允許選中時,高亮  
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath {  
  NSLog(@"%s", __FUNCTION__);  
  return YES;  
}  
  
// 高亮完成后回調  
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath {  
  NSLog(@"%s", __FUNCTION__);  
}  
  
// 由高亮轉成非高亮完成時的回調  
- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath {  
  NSLog(@"%s", __FUNCTION__);  
}  
  
// 設置是否允許選中  
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {  
  NSLog(@"%s", __FUNCTION__);  
  return YES;  
}  
  
// 設置是否允許取消選中  
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath {  
  NSLog(@"%s", __FUNCTION__);  
  return YES;  
}  
  
// 選中操作  
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {  
  NSLog(@"%s", __FUNCTION__);  
}  
  
// 取消選中操作  
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {  
  NSLog(@"%s", __FUNCTION__);  
}  

實戰篇

一. 將設計圖分解成合理結構
被分解后的設計圖

分解原因及說明

0.創建控制器(繼承自UICollectionViewController),然后創建基本的layout,給某些固定的數據賦值

    UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
    layout.minimumInteritemSpacing = 0;
    layout.minimumLineSpacing = 9;
layout.sectionInset = UIEdgeInsetsMake(0, 9, 0, 9);
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    THFindController * discoverVC = [[THFindController alloc] initWithCollectionViewLayout:layout];
    discoverVC.title = @"發現";

1.說了一頓,特意說明,UICollectionView是很強調這個概念,有組頭組尾這兩個概念,但一直沒有提到tableHeaderView這樣的控件,所以我們將1(輪播圖)+2(兩個按鍵view)+ 間隔+3(精選動態)封裝成第一組的headerView(封裝的類名是THFineAdView),繼承自UICollectionReusableView(繼承自UIView,沒啥功能,除了復用)


2.將5也集成字UICollectionReusableView封裝一下


3.封裝完畢之后,要去注冊一下,注冊的使用,分為nib,和class注冊


3.1 第一組的headerView是同純代碼封裝的,所以注冊的時候這樣

  [self.collectionView registerClass:[THFineAdView class]
          forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                 withReuseIdentifier:kTHFindAdViewIden];

3.2 第二組的headerView使用的是nib方式,所以也要注冊一下

    UINib * nib = [UINib nibWithNibName:@"THFindStyleHeaderView" bundle:nil];
    [self.collectionView registerNib:nib
            forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                   withReuseIdentifier:kTHFindStyleHeaderViewIden];

3.3 (模塊4和模塊5之間的間隙,模塊6和模塊7之間的間隙)可以通過sectionInset來實現,但是我認為成為組1,組2的sectionFooter更加靠譜一些。那就注冊一下

  [self.collectionView registerClass:[THFindSectionFooterView class]
            forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
                   withReuseIdentifier:kFooterViewIden];

注意 UICollectionElementKindSectionHeader這個代表頭的意思,如果注冊尾巴,使用UICollectionElementKindSectionFooter
注冊的三個方法應該寫在一起

4.調用組頭和組尾

#pragma mark - collectionview的代理方法
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
           viewForSupplementaryElementOfKind:(NSString *)kind
                                 atIndexPath:(NSIndexPath *)indexPath{

//先通過kind類型判斷是頭還是尾巴,然后在判斷是哪一組,如果都是一樣的頭尾,那么只要第一次判斷就可以了
     if (kind == UICollectionElementKindSectionHeader){
        if (indexPath.section == 0) {
           THFineAdView *view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                                                                  withReuseIdentifier:kTHFindAdViewIden
                                                                         forIndexPath:indexPath];
            view.bannerArr = self.bannerArr;
            return view;
        }
        else if(indexPath.section == 1){
         THFindStyleHeaderView * view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                                                      withReuseIdentifier:kTHFindStyleHeaderViewIden
                                                             forIndexPath:indexPath];
        view.titleLab.text = @"推薦用戶";
        return view;
        }
     }
     else{
         UICollectionReusableView *footer = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter
                                                                               withReuseIdentifier:kFooterViewIden
                                                                                      forIndexPath:indexPath];
         return footer;
     }
    return nil;
}

5.調用組頭和組尾的高度

設置頭和尾的size,要用兩個代理方法,使用代理方法的好處在于可以分情況判斷

 // 設置section頭視圖的參考大小,與tableheaderview類似
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout
referenceSizeForHeaderInSection:(NSInteger)section {
    if(section == 0){
        return CGSizeMake(ScreenWidth, [THFineAdView adViewHeight]);
    }else if(section == 1){
        return CGSizeMake(ScreenWidth, [THFindStyleHeaderView findStyleHeight]);
    }else{
        return CGSizeZero;
    }
}

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout*)collectionViewLayout
referenceSizeForFooterInSection:(NSInteger)section{
    return CGSizeMake(ScreenWidth, 10*THScreenScaleNum);
}

6.數據源方法

#pragma mark <UICollectionViewDataSource>

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    NSInteger pre = (self.preArr.count != 0);
    NSInteger next = (self.nextArr.count != 0);
    NSInteger users = (self.userArr.count != 0);
    return pre+next+users;
}


- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    
    if (section == 0) {
        return 4;
    }else if(section == 1){
        return 1;
    }else{
        return self.nextArr.count;
    }
    return 0;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    UICollectionViewCell * cell = nil;
    if (indexPath.section == 0) {
        THRecommendCell *recCell = [THRecommendCell recommendCellWithCollectionView:collectionView indePath:indexPath];
        recCell.twitterM = self.preArr[indexPath.item];
        cell = recCell;
    }else if (indexPath.section == 1){
        THRecommendUsersCell * userCell = [THRecommendUsersCell cellWithColletionView:collectionView indexPath:indexPath];
        userCell.users = self.userArr;
        cell = userCell;
    }else{
        THRecommendCell *rCell = [THRecommendCell recommendCellWithCollectionView:collectionView indePath:indexPath];
        rCell.twitterM = self.nextArr[indexPath.item];
        cell = rCell;
    }
    return cell;
}

7.在使用自定義cell之前一定要注冊,否者不能復用,給系統造成很大的壓力,經常卡頓

//我是自定義了一個方法,傳遞indexPAth和collectionview直接注冊
+ (instancetype)recommendCellWithCollectionView:(UICollectionView *)collectionView
                                       indePath:(NSIndexPath *)indexPath{
    [collectionView registerClass:[self class] forCellWithReuseIdentifier:@"THRecommendCell"];
    return [collectionView dequeueReusableCellWithReuseIdentifier:@"THRecommendCell" forIndexPath:indexPath];
}

8.如何自定義cell

他的自定義非常簡單,就幾個方法

#pragma mark - 直接寫這個方法
- (instancetype)initWithFrame:(CGRect)frame{

    if (self = [super initWithFrame:frame]) {
        [self createSub];
    }
    return self;
}

- (void)createSub{
    self.contentView.backgroundColor = [UIColor whiteColor];
    //1.圖片
    [self.contentView addSubview:self.iconImage];
    //2.題目
    [self.contentView addSubview:self.titleLab];
    //3.喜歡數
    [self.contentView addSubview:self.likeBtn];
    //4.評論數
    [self.contentView addSubview:self.recommentBtn];
}

#pragma mark - 布局
- (void)updateConstraints{
    [super updateConstraints];
    //圖片
}

如果是xib加載的話,最多有個awakeFromNib和view的一樣使用

9.代理方法,就懶得寫了

如果各位同行有什么好的建議,可以告訴我,我會虛心接受,再次修改本文的,一起進步~ 順便給有個好文章,可以看看 參考文檔

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容