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

公司的UI圖

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

理論篇

一.UICollectionViewLayout是做什麼的?

1.1 在創建UITableView的時候,使用的是- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style用於判斷是普通還是分組,

1.2 UICollectionViewLayout實際的作用是一樣的,是用來設置cell的布局的,初始化collectionView的時候,一定要給他設置這個屬性,否者不會顯示。UICollectionViewFlowLayout是UICollectionViewLayout的子類,給collectionView賦值的時候,一定要使用 UICollectionViewFlowLayout初始化。

1.3 UICollectionViewFlowLayout和UICollectionViewLayout的關係就像是UIGestureRecognizer和UITapGestureRecognizer的一樣。一個是父類,一個是子類。使用的時候都用子類

二. UICollectionViewLayout的屬性

每一個綠色款都是cell

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

NS_CLASS_AVAILABLE_IOS(6_0) @interface UICollectionViewFlowLayout : UICollectionViewLayout nn//每行之間豎直之間的最小間距 (可以大於) n@property (nonatomic) CGFloat minimumLineSpacing;nn//同行的cell與cell之間水平之間的最小間距(可以)n@property (nonatomic) CGFloat minimumInteritemSpacing; nn//每個cell的尺寸,如果都是上圖的那種,整個collectionView都是同一種,那麼可以用整個屬性,如果想我們公司那樣的樣式,不建議設置該屬性n@property (nonatomic) CGSize itemSize; nn//預估cell的尺寸,ios8之後可以先去預估cell的尺寸,然後去自適應n@property (nonatomic) CGSize estimatedItemSize NS_AVAILABLE_IOS(8_0); // defaults to CGSizeZero - setting a non-zero size enables cells that self-size via -perferredLayoutAttributesFittingAttributes: nn//滑動的方向,水平或者豎直,看到很多圖片瀏覽器都是用collectionview做出來的(註冊之後,可以復用),非常的好用!但是要記住,水平滑動只有collectionview有,tableview不支持的,默認豎直方法滑動n@property (nonatomic) UICollectionViewScrollDirection scrollDirection; // default is UICollectionViewScrollDirectionVertical nn//組頭組尾的sizen@property (nonatomic) CGSize headerReferenceSize; n@property (nonatomic) CGSize footerReferenceSize; nn//組的四周切的範圍n@property (nonatomic) UIEdgeInsets sectionInset; nn@endn

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

四. UICollectionView的組頭和組尾(頁眉和頁腳)

組頭和足尾

1.UICollectionView中非常明確是以組為單位,可以設置組的組頭和尾巴,這裡的頭尾還可以復用

2.復用的時候,首先頭尾view要繼承於

UICollectionReusableView,然後註冊(分為nib和class兩種)

3.用的時候通過collectionView去dequeue一下獲取,和cell的思路一樣

4.可以使用上文中的layout屬性直接設置組頭和組尾的size,也可以使用代理方法,去設置

五. UICollectionView的數據源方法

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

#pragma mark - UICollectionViewDataSource n// 指定Section個數 n- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { n return 3; n} nn// 指定section中的collectionViewCell的個數 n- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { n return 10; n} nn// 配置section中的collectionViewCell的顯示 n- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { n CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellIdentifier" forIndexPath:indexPath]; n cell.backgroundColor = [UIColor redColor]; n cell.textLabel.text = [NSString stringWithFormat:@"(%ld %ld)", indexPath.section, indexPath.row]; nn return cell; n}n

六. UICollectionView的代理方法

#pragma mark - UICollectionViewDelegate n// 允許選中時,高亮 n- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath { n NSLog(@"%s", __FUNCTION__); n return YES; n} nn// 高亮完成後回調 n- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath { n NSLog(@"%s", __FUNCTION__); n} nn// 由高亮轉成非高亮完成時的回調 n- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath { n NSLog(@"%s", __FUNCTION__); n} nn// 設置是否允許選中 n- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { n NSLog(@"%s", __FUNCTION__); n return YES; n} nn// 設置是否允許取消選中 n- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath { n NSLog(@"%s", __FUNCTION__); n return YES; n} nn// 選中操作 n- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { n NSLog(@"%s", __FUNCTION__); n} nn// 取消選中操作 n- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { n NSLog(@"%s", __FUNCTION__); n}n

實戰篇

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

被分解後的設計圖

分解原因及說明:

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

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

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]n forSupplementaryViewOfKind:UICollectionElementKindSectionHeadern withReuseIdentifier:kTHFindAdViewIden];n

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

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

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

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

注意 UICollectionElementKindSectionHeader這個代表頭的意思,如果註冊尾巴,使用UICollectionElementKindSectionFooter

註冊的三個方法應該寫在一起

4.調用組頭和組尾

#pragma mark - collectionview的代理方法n- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionViewn viewForSupplementaryElementOfKind:(NSString *)kindn atIndexPath:(NSIndexPath *)indexPath{nn//先通過kind類型判斷是頭還是尾巴,然後在判斷是哪一組,如果都是一樣的頭尾,那麼只要第一次判斷就可以了n if (kind == UICollectionElementKindSectionHeader){ n if (indexPath.section == 0) {n THFineAdView *view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeadern withReuseIdentifier:kTHFindAdViewIdenn forIndexPath:indexPath];n view.bannerArr = self.bannerArr; n return view;n } n else if(indexPath.section == 1){n THFindStyleHeaderView * view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeadern withReuseIdentifier:kTHFindStyleHeaderViewIdenn forIndexPath:indexPath];n view.titleLab.text = @"推薦用戶"; n return view;n }n } n else{ n UICollectionReusableView *footer = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFootern withReuseIdentifier:kFooterViewIdenn forIndexPath:indexPath]; return footer;n } n return nil;n}n

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

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

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

6.數據源方法

#pragma mark <UICollectionViewDataSource>nn- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { n NSInteger pre = (self.preArr.count != 0); n NSInteger next = (self.nextArr.count != 0); n NSInteger users = (self.userArr.count != 0); n return pre+next+users;n}nn- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { n if (section == 0) { n return 4;n }else if(section == 1){ n return 1;n }else{ n return self.nextArr.count;n } n return 0;n}nn- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionViewn cellForItemAtIndexPath:(NSIndexPath *)indexPath { nn UICollectionViewCell * cell = nil; n if (indexPath.section == 0) {n THRecommendCell *recCell = [THRecommendCell recommendCellWithCollectionView:collectionView indePath:indexPath];n recCell.twitterM = self.preArr[indexPath.item];n cell = recCell;n }else if (indexPath.section == 1){n THRecommendUsersCell * userCell = [THRecommendUsersCell cellWithColletionView:collectionView indexPath:indexPath];n userCell.users = self.userArr;n cell = userCell;n }else{n THRecommendCell *rCell = [THRecommendCell recommendCellWithCollectionView:collectionView indePath:indexPath];n rCell.twitterM = self.nextArr[indexPath.item];n cell = rCell;n } n return cell;n}n

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

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

8.如何自定義cell

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

#pragma mark - 直接寫這個方法n- (instancetype)initWithFrame:(CGRect)frame{ nn if (self = [super initWithFrame:frame]) {n [self createSub];n } n return self;n}nn- (void)createSub{ n self.contentView.backgroundColor = [UIColor whiteColor]; n //1.圖片n [self.contentView addSubview:self.iconImage]; n //2.題目n [self.contentView addSubview:self.titleLab]; n //3.喜歡數n [self.contentView addSubview:self.likeBtn]; n //4.評論數n [self.contentView addSubview:self.recommentBtn];n}nn#pragma mark - 布局n- (void)updateConstraints{n [super updateConstraints]; n //圖片n}n

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

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

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

閱讀原文

推薦閱讀:

iOS 的「描述文件」是什麼?
iOS 11.2.5正式版發布:這次你們還會去更新嗎?
很多 iOS 開發者收到 Apple 警告郵件,是要全面封殺熱修復方案嗎?
潑辣修圖,通過人工智慧與卓越體驗,幫助用戶更好地處理照片 | NEXT Big
只要10秒,就能讓照片擁有老照片的復古感

TAG:iOS开发 | iOS应用 | iOS |