UICollectionView專題


什么是UICollectionView

UICollectionView是一種新的數據展示方式,簡單來說可以把他理解成多列的UITableView(請一定注意這是UICollectionView的最最簡單的形式)。如果你用過iBooks的話,可能你還對書架布局有一定印象:一個虛擬書架上放着你下載和購買的各類圖書,整齊排列。其實這就是一個UICollectionView的表現形式,或者iPad的iOS6中的原生時鍾應用中的各個時鍾,也是UICollectionView的最簡單的一個布局,如圖:

iOS6 iPad版時鍾應用iOS6 iPad版時鍾應用最簡單的UICollectionView就是一個GridView,可以以多列的方式將數據進行展示。標准的UICollectionView包含三個部分,它們都是UIView的子類:

  • Cells 用於展示內容的主體,對於不同的cell可以指定不同尺寸和不同的內容,這個稍后再說
  • Supplementary Views 追加視圖 如果你對UITableView比較熟悉的話,可以理解為每個Section的Header或者Footer,用來標記每個section的view
  • Decoration Views 裝飾視圖 這是每個section的背景,比如iBooks中的書架就是這個

 

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


實現一個簡單的UICollectionView

先從最簡單的開始,UITableView是iOS開發中的非常非常非常重要的一個類,相信如果你是開發者的話應該是對這個類非常熟悉了。實現一個UICollectionView和實現一個UITableView基本沒有什么大區別,它們都同樣是datasource和delegate設計模式的:datasource為view提供數據源,告訴view要顯示些什么東西以及如何顯示它們,delegate提供一些樣式的小細節以及用戶交互的相應。因此在本節里會大量對比collection view和table view來進行說明,如果您還不太熟悉table view的話,也是個對照着復習的好機會。

UICollectionViewDataSource

  • section的數量 -numberOfSectionsInCollection:
  • 某個section里有多少個item -collectionView:numberOfItemsInSection:
  • 對於某個位置應該顯示什么樣的cell -collectionView:cellForItemAtIndexPath:

實現以上三個委托方法,基本上就可以保證CollectionView工作正常了。當然,還有提供Supplementary View的方法

  • collectionView:viewForSupplementaryElementOfKind:atIndexPath:

對於Decoration Views,提供方法並不在UICollectionViewDataSource中,而是直接UICollectionViewLayout類中的(因為它僅僅是視圖相關,而與數據無關),放到稍后再說。

關於重用

為了得到高效的View,對於cell的重用是必須的,避免了不斷生成和銷毀對象的操作,這與在UITableView中的情況是一致的。但值得注意的時,在UICollectionView中,不僅cell可以重用,Supplementary View和Decoration View也是可以並且應當被重用的。在iOS5中,Apple對UITableView的重用做了簡化,以往要寫類似這樣的代碼:

1
2
3
4
5
6
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MY_CELL_ID"]; if (!cell) { //如果沒有可重用的cell,那么生成一個   cell = [[UITableViewCell alloc] init]; } //配置cell,blablabla  return cell 

而如果我們在TableView向數據源請求數據之前使用-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配置了:

1
2
3
4
5
6
- (UICollectionView*)collectionView:(UICollectionView*)cv cellForItemAtIndexPath:(NSIndexPath*)indexPath {  MyCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@”MY_CELL_ID];  // Configure the cell's content   cell.imageView.image = ...  return cell; } 

需要吐槽的是,對collection view,取重用隊列的方法的名字和UITableView里面不一樣了,在Identifier前面多加了Reuse五個字母,語義上要比以前清晰,命名規則也比以前嚴謹了..不知道Apple會不會為了追求完美而把UITableView中的命名不那么好的方法deprecate掉。

UICollectionViewDelegate

數據無關的view的外形啊,用戶交互啊什么的,由UICollectionViewDelegate來負責:

  • cell的高亮
  • cell的選中狀態
  • 可以支持長按后的菜單

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

  1. -collectionView:shouldHighlightItemAtIndexPath: 是否應該高亮?
  2. -collectionView:didHighlightItemAtIndexPath: 如果1回答為是,那么高亮
  3. -collectionView:shouldSelectItemAtIndexPath: 無論1結果如何,都詢問是否可以被選中?
  4. -collectionView:didUnhighlightItemAtIndexPath: 如果1回答為是,那么現在取消高亮
  5. -collectionView:didSelectItemAtIndexPath: 如果3回答為是,那么選中cell

狀態控制要比以前靈活一些,對應的高亮和選中狀態分別由highlighted和selected兩個屬性表示。

關於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是否有被選中狀態下的改變。比如在contentView里加了一個normal和selected指定了不同圖片的imageView,那么選中這個cell的同時這張圖片也會從normal變成selected,而不需要額外的任何代碼。

UICollectionViewLayout

終於到UICollectionView的精髓了…這也是UICollectionView和UITableView最大的不同。UICollectionViewLayout可以說是UICollectionView的大腦和中樞,它負責了將各個cell、Supplementary View和Decoration Views進行組織,為它們設定各自的屬性,包括但不限於:

  • 位置
  • 尺寸
  • 透明度
  • 層級關系
  • 形狀
  • 等等等等…
  • Layout決定了UICollectionView是如何顯示在界面上的。在展示之前,一般需要生成合適的UICollectionViewLayout子類對象,並將其賦予CollectionView的collectionViewLayout屬性。關於詳細的自定義UICollectionViewLayout和一些細節,我將寫在之后一篇筆記中。

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尺寸 同樣地分為全局和部分。需要注意根據滾動方向不同,header和footer的高和寬中只有一個會起作用。垂直滾動時section間寬度為該尺寸的高,而水平滾動時為寬度起作用,如圖。

    • @property (CGSize) headerReferenceSize
    • @property (CGSize) footerReferenceSize
    • -collectionView:layout:referenceSizeForHeaderInSection:
    • -collectionView:layout:referenceSizeForFooterInSection:
  • 縮進

    • @property UIEdgeInsets sectionInset;
    • -collectionView:layout:insetForSectionAtIndex:

總結

一個UICollectionView的實現包括兩個必要部分:UICollectionViewDataSource和UICollectionViewLayout,和一個交互部分:UICollectionViewDelegate。而Apple給出的UICollectionViewFlowLayout已經是一個很強力的layout方案了。


幾個自定義的Layout

但是光是UICollectionViewFlowLayout的話,顯然是不夠用的,而且如果單單是這樣的話,就和現有的開源各類Grid View沒有區別了…UICollectionView的強大之處,就在於各種layout的自定義實現,以及它們之間的切換。先看幾個相當exiciting的例子吧~

比如,堆疊布局:

圓形布局:

和Cover Flow布局:

所有這些布局都采用了同樣的數據源和委托方法,因此完全實現了model和view的解耦。但是如果僅這樣,那開源社區也已經有很多相應的解決方案了。Apple的強大和開源社區不能比擬的地方在於對SDK的全局掌控,CollectionView提供了非常簡單的API可以令開發者只需要一次簡單調用,就可以使用CoreAnimation在不同的layout之間進行動畫切換,這種切換必定將大幅增加用戶體驗,代價只是幾十行代碼就能完成的布局實現,以及簡單的一句API調用,不得不說現在所有的開源代碼與之相比,都是相形見拙了…不得不佩服和感謝UIKit團隊的努力。

 

UICollectionViewLayoutAttributes

UICollectionViewLayoutAttributes是一個非常重要的類,先來看看property列表:

  • @property (nonatomic) CGRect frame
  • @property (nonatomic) CGPoint center
  • @property (nonatomic) CGSize size
  • @property (nonatomic) CATransform3D transform3D
  • @property (nonatomic) CGFloat alpha
  • @property (nonatomic) NSInteger zIndex
  • @property (nonatomic, getter=isHidden) BOOL hidden

可以看到,UICollectionViewLayoutAttributes的實例中包含了諸如邊框,中心點,大小,形狀,透明度,層次關系和是否隱藏等信息。和DataSource的行為十分類似,當UICollectionView在獲取布局時將針對每一個indexPath的部件(包括cell,追加視圖和裝飾視圖),向其上的UICollectionViewLayout實例詢問該部件的布局信息(在這個層面上說的話,實現一個UICollectionViewLayout的時候,其實很像是zap一個delegate,之后的例子中會很明顯地看出),這個布局信息,就以UICollectionViewLayoutAttributes的實例的方式給出。


自定義的UICollectionViewLayout

UICollectionViewLayout的功能為向UICollectionView提供布局信息,不僅包括cell的布局信息,也包括追加視圖和裝飾視圖的布局信息。實現一個自定義layout的常規做法是繼承UICollectionViewLayout類,然后重載下列方法:

  • -(CGSize)collectionViewContentSize

    • 返回collectionView的內容的尺寸
  • -(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

    • 返回rect中的所有的元素的布局屬性
    • 返回的是包含UICollectionViewLayoutAttributes的NSArray
    • UICollectionViewLayoutAttributes可以是cell,追加視圖或裝飾視圖的信息,通過不同的UICollectionViewLayoutAttributes初始化方法可以得到不同類型的UICollectionViewLayoutAttributes:

      • layoutAttributesForCellWithIndexPath:
      • layoutAttributesForSupplementaryViewOfKind:withIndexPath:
      • layoutAttributesForDecorationViewOfKind:withIndexPath:
  • -(UICollectionViewLayoutAttributes )layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath

    • 返回對應於indexPath的位置的cell的布局屬性
  • -(UICollectionViewLayoutAttributes )layoutAttributesForSupplementaryViewOfKind:(NSString )kind atIndexPath:(NSIndexPath *)indexPath

    • 返回對應於indexPath的位置的追加視圖的布局屬性,如果沒有追加視圖可不重載
  • -(UICollectionViewLayoutAttributes * )layoutAttributesForDecorationViewOfKind:(NSString)decorationViewKind atIndexPath:(NSIndexPath )indexPath

    • 返回對應於indexPath的位置的裝飾視圖的布局屬性,如果沒有裝飾視圖可不重載
  • -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds

    • 當邊界發生改變時,是否應該刷新布局。如果YES則在邊界變化(一般是scroll到其他地方)時,將重新計算需要的布局信息。

另外需要了解的是,在初始化一個UICollectionViewLayout實例后,會有一系列准備方法被自動調用,以保證layout實例的正確。

首先,-(void)prepareLayout將被調用,默認下該方法什么沒做,但是在自己的子類實現中,一般在該方法中設定一些必要的layout的結構和初始需要的參數等。

之后,-(CGSize) collectionViewContentSize將被調用,以確定collection應該占據的尺寸。注意這里的尺寸不是指可視部分的尺寸,而應該是所有內容所占的尺寸。collectionView的本質是一個scrollView,因此需要這個尺寸來配置滾動行為。

接下來-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect被調用,這個沒什么值得多說的。初始的layout的外觀將由該方法返回的UICollectionViewLayoutAttributes來決定。

另外,在需要更新layout時,需要給當前layout發送 -invalidateLayout,該消息會立即返回,並且預約在下一個loop的時候刷新當前layout,這一點和UIView的setNeedsLayout方法十分類似。在-invalidateLayout后的下一個collectionView的刷新loop中,又會從prepareLayout開始,依次再調用-collectionViewContentSize和-layoutAttributesForElementsInRect來生成更新后的布局。


Demo

說了那么多,其實還是Demo最能解決問題。Apple官方給了一個flow layout和一個circle layout的例子,都很經典,需要的同學可以從這里下載

LineLayout——對於個別UICollectionViewLayoutAttributes的調整

先看LineLayout,它繼承了UICollectionViewFlowLayout這個Apple提供的基本的布局。它主要實現了單行布局,自動對齊到網格以及當前網格cell放大三個特性。如圖:

先看LineLayout的init方法:

1
2
3
4
5
6
7
8
9
10
11
-(id)init {  self = [super init];  if (self) {  self.itemSize = CGSizeMake(ITEM_SIZE, ITEM_SIZE);  self.scrollDirection = UICollectionViewScrollDirectionHorizontal;  self.sectionInset = UIEdgeInsetsMake(200, 0.0, 200, 0.0);  self.minimumLineSpacing = 50.0;  }  return self; } 

self.sectionInset = UIEdgeInsetsMake(200, 0.0, 200, 0.0); 確定了縮進,此處為上方和下方各縮進200個point。由於cell的size已經定義了為200x200,因此屏幕上在縮進后就只有一排item的空間了。

self.minimumLineSpacing = 50.0; 這個定義了每個item在水平方向上的最小間距。

UICollectionViewFlowLayout是Apple為我們准備的開袋即食的現成布局,因此之前提到的幾個必須重載的方法中需要我們操心的很少,即使完全不重載它們,現在也可以得到一個不錯的線狀一行的gridview了。而我們的LineLayout通過重載父類方法后,可以實現一些新特性,比如這里的動對齊到網格以及當前網格cell放大。

自動對齊到網格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (CGPoint)targetContentOffsetForProposedContentOffset: (CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {  //proposedContentOffset是沒有對齊到網格時本來應該停下的位置  CGFloat offsetAdjustment = MAXFLOAT;  CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds) / 2.0);  CGRect targetRect = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);  NSArray* array = [super layoutAttributesForElementsInRect:targetRect];  //對當前屏幕中的UICollectionViewLayoutAttributes逐個與屏幕中心進行比較,找出最接近中心的一個  for (UICollectionViewLayoutAttributes* layoutAttributes in array) {  CGFloat itemHorizontalCenter = layoutAttributes.center.x;  if (ABS(itemHorizontalCenter - horizontalCenter) < ABS(offsetAdjustment)) {  offsetAdjustment = itemHorizontalCenter - horizontalCenter;  }  }  return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y); } 

當前item放大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {  NSArray *array = [super layoutAttributesForElementsInRect:rect];  CGRect visibleRect;  visibleRect.origin = self.collectionView.contentOffset;  visibleRect.size = self.collectionView.bounds.size;  for (UICollectionViewLayoutAttributes* attributes in array) {  if (CGRectIntersectsRect(attributes.frame, rect)) {  CGFloat distance = CGRectGetMidX(visibleRect) - attributes.center.x;  CGFloat normalizedDistance = distance / ACTIVE_DISTANCE;  if (ABS(distance) < ACTIVE_DISTANCE) {  CGFloat zoom = 1 + ZOOM_FACTOR*(1 - ABS(normalizedDistance));  attributes.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0);  attributes.zIndex = 1;  }  }  }  return array; } 

對於個別UICollectionViewLayoutAttributes進行調整,以達到滿足設計需求是UICollectionView使用中的一種思路。在根據位置提供不同layout屬性的時候,需要記得讓-shouldInvalidateLayoutForBoundsChange:返回YES,這樣當邊界改變的時候,-invalidateLayout會自動被發送,才能讓layout得到刷新。

CircleLayout——完全自定義的Layout,添加刪除item,以及手勢識別

CircleLayout的例子稍微復雜一些,cell分布在圓周上,點擊cell的話會將其從collectionView中移出,點擊空白處會加入一個cell,加入和移出都有動畫效果。

這放在以前的話估計夠寫一陣子了,而得益於UICollectionView,基本只需要100來行代碼就可以搞定這一切,非常cheap。通過CircleLayout的實現,可以完整地看到自定義的layout的編寫流程,非常具有學習和借鑒的意義。

CircleLayoutCircleLayout

首先,布局准備中定義了一些之后計算所需要用到的參數。

1
2
3
4
5
6
7
8
9
-(void)prepareLayout { //和init相似,必須call super的prepareLayout以保證初始化正確  [super prepareLayout];  CGSize size = self.collectionView.frame.size;  _cellCount = [[self collectionView] numberOfItemsInSection:0];  _center = CGPointMake(size.width / 2.0, size.height / 2.0);  _radius = MIN(size.width, size.height) / 2.5; } 

其實對於一個size不變的collectionView來說,除了_cellCount之外的中心和半徑的定義也可以扔到init里去做,但是顯然在prepareLayout里做的話具有更大的靈活性。因為每次重新給出layout時都會調用prepareLayout,這樣在以后如果有collectionView大小變化的需求時也可以自動適應變化。

然后,按照UICollectionViewLayout子類的要求,重載了所需要的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//整個collectionView的內容大小就是collectionView的大小(沒有滾動) -(CGSize)collectionViewContentSize {  return [self collectionView].frame.size; } //通過所在的indexPath確定位置。 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path {  UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path]; //生成空白的attributes對象,其中只記錄了類型是cell以及對應的位置是indexPath  //配置attributes到圓周上  attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);  attributes.center = CGPointMake(_center.x + _radius * cosf(2 * path.item * M_PI / _cellCount), _center.y + _radius * sinf(2 * path.item * M_PI / _cellCount));  return attributes; } //用來在一開始給出一套UICollectionViewLayoutAttributes -(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {  NSMutableArray* attributes = [NSMutableArray array];  for (NSInteger i=0 ; i < self.cellCount; i++) {  //這里利用了-layoutAttributesForItemAtIndexPath:來獲取attributes  NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];  [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];  }  return attributes; } 

現在已經得到了一個circle layout。為了實現cell的添加和刪除,需要為collectionView加上手勢識別,這個很簡單,在ViewController中:

1
2
UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; [self.collectionView addGestureRecognizer:tapRecognizer]; 

對應的處理方法handleTapGesture:為

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)handleTapGesture:(UITapGestureRecognizer *)sender {  if (sender.state == UIGestureRecognizerStateEnded) {  CGPoint initialPinchPoint = [sender locationInView:self.collectionView];  NSIndexPath* tappedCellPath = [self.collectionView indexPathForItemAtPoint:initialPinchPoint]; //獲取點擊處的cell的indexPath  if (tappedCellPath!=nil) { //點擊處沒有cell  self.cellCount = self.cellCount - 1;  [self.collectionView performBatchUpdates:^{  [self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:tappedCellPath]];  } completion:nil];  } else {  self.cellCount = self.cellCount + 1;  [self.collectionView performBatchUpdates:^{  [self.collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForItem:0 inSection:0]]];  } completion:nil];  }  } } 

performBatchUpdates:completion: 再次展示了block的強大的一面..這個方法可以用來對collectionView中的元素進行批量的插入,刪除,移動等操作,同時將觸發collectionView所對應的layout的對應的動畫。相應的動畫由layout中的下列四個方法來定義:

  • initialLayoutAttributesForAppearingItemAtIndexPath:
  • initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:
  • finalLayoutAttributesForDisappearingItemAtIndexPath:
  • finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:

更正:正式版中API發生了變化(而且不止一次變化 initialLayoutAttributesForInsertedItemAtIndexPath:在正式版中已經被廢除。現在在insert或者delete之前,prepareForCollectionViewUpdates:會被調用,可以使用這個方法來完成添加/刪除的布局。關於更多這方面的內容以及新的示例demo,可以參看這篇博文(需要翻牆)。新的示例demo在Github上也有,鏈接

在CircleLayout中,實現了cell的動畫。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//插入前,cell在圓心位置,全透明 - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForInsertedItemAtIndexPath:(NSIndexPath *)itemIndexPath {  UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];  attributes.alpha = 0.0;  attributes.center = CGPointMake(_center.x, _center.y);  return attributes; } //刪除時,cell在圓心位置,全透明,且只有原來的1/10大 - (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDeletedItemAtIndexPath:(NSIndexPath *)itemIndexPath {  UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];  attributes.alpha = 0.0;  attributes.center = CGPointMake(_center.x, _center.y);  attributes.transform3D = CATransform3DMakeScale(0.1, 0.1, 1.0);  return attributes; } 

在插入或刪除時,將分別以插入前和刪除后的attributes和普通狀態下的attributes為基准,進行UIView的動畫過渡。而這一切並沒有很多代碼要寫,幾乎是free的,感謝蘋果…


布局之間的切換

有時候可能需要不同的布局,Apple也提供了方便的布局間切換的方法。直接更改collectionView的collectionViewLayout屬性可以立即切換布局。而如果通過setCollectionViewLayout:animated:,則可以在切換布局的同時,使用動畫來過渡。對於每一個cell,都將有對應的UIView動畫進行對應,又是一個接近free的特性。

對於我自己來說,UICollectionView可能是我轉向iOS 6 SDK的最具有吸引力的特性之一,因為UIKit團隊的努力和CoreAnimation的成熟,使得創建一個漂亮優雅的UI變的越來越簡單了。可以斷言說UICollectionView在今后的iOS開發中,一定會成為和UITableView一樣的強大和最常用的類之一。在iOS 6還未正式上市前,先對其特性進行一些學習,以期盡快能使用新特性來簡化開發流程,可以說是非常值得的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM