你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> UICollectionView動畫

UICollectionView動畫

編輯:IOS開發基礎

70.jpg

作者:LiuChunGui

一、簡單使用

UICollectionView更新事件有四種分別是插入、刪除、刷新、移動, api使用起來和UITableView類似,具體可以自己在代碼中找,如果需要執行多個更新事件,可以放到performBatchUpdates中的updates閉包中作為一組動畫,然後全部執行完之後通過completion調回。

collectionView.performBatchUpdates({ () -> Void in
            collectionView.insertItemsAtIndexPaths(insertIndexPaths)
            collectionView.moveItemAtIndexPath(currentIndexPath, toIndexPath: toIndexPath)
            }, completion: { (isFinish) -> Void in
        })

二、UICollectionView動畫

四種不同的更新事件,系統默認會帶有動畫,不過是比較簡單的。我們可以自定義layout或者繼承flowLayout,在內部實現我們自己想要的動畫。下面,我們來說說動畫的流程,以及系統默認的四種動畫內部是如何的,並且通過代碼來修改達到自己想要的動畫。

CollectionView動畫流程

當我們在外部調用CollectionView相關的api去插入、刪除、刷新、移動cell時,首先會通過layout中的layoutAttributesForElementsInRect方法獲取更新以後的布局信息,然後通過prepareForCollectionViewUpdates方法來通知layout哪些內容將會發生改變。之後,通過調用layout中的initialLayoutAttributesForAppearingItemAtIndexPath、finalLayoutAttributesForDisappearingItemAtIndexPath方法獲取對應indexPath的剛出現時最初布局屬性和消失時最終布局屬性。而後形成兩個動畫過程分別是剛出現時最初布局->更新後布局的出現動畫和更新前布局->消失時最終布局的消失動畫,而collectionView中'插入'、'刪除'、'刷新'和'移動'動畫都是基於這兩個動畫組合形成的。最後,等這一系列動畫執行完之後,最後會調用layout中finalizeCollectionViewUpdates方法,這個方法仍然放在動畫塊中,我們可以在這個方法當中添加額外的動畫。

從上面流程可以看出,在更新的時候,由於更新前布局和更新後布局都是在更新動畫前已經設置好了,我們不能去胡亂更改布局,所以我們只能通過initialLayoutAttributesForAppearingItemAtIndexPath和finalLayoutAttributesForDisappearingItemAtIndexPath兩個方法來更改剛出現時最初布局屬性和消失時最終布局屬性,即我們只能更改出現動畫的起點和消失動畫的終點。

為了更方面的下面說明,引申出兩個名詞:

  • 出現動畫:initialLayoutAttributesForAppearingItemAtIndexPath獲取對應indexPath的剛出現時最初布局->更新後布局變化過程

  • 消失動畫:更新之前的布局->finalLayoutAttributesForDisappearingItemAtIndexPath方法獲取對應indexPath的消失時最終布局的變化過程

注意,出現動畫和消失動畫針對的是一個cell單元。

下面我們通過代碼示例來實現插入、刪除、刷新、移動動畫。 代碼示例工程:UICollectionViewAnimationDemo

在這個Demo工程中有一個BGSelectImageLayout,它是CollectionView的layout,它的布局方式是水平橫向滑動,並且只有一組,每一個普通的cell大小都是itemSize,而選中的cell則寬度是itemSize*2。

插入動畫:

在當前的布局下,每插入一個cell時,都會影響它後面所有cell布局變化。 
例如CollectionView有一行三個cell,為了更好的說明將indexPath是(0,0),(0,1),(0,2)標記為0,1,2。當在第1個位置插入一個cell時,如下圖:

128.png

而在這個插入過程中,視覺上會有三個動畫過程。new插入到位置1為過程1,1移動一個單位到2為過程2,2移動一個單位到一個新的位置3為過程3,如下圖:

127.png

雖然視覺上只有三個動畫過程,但其實有五個動畫。其中,過程1是1位置的出現動畫;過程2是1位置的消失動畫和2位置的出現動畫重合而成;過程3是2位置的消失動畫和3位置的出現動畫。

其中值得注意的三點,一是除了最後一個,前面的cell消失動畫與它後面cell出現動畫重合,這樣看起來就是當前位置的cell向後平移了一個位置;二是最後一個cell只有出現動畫,沒有消失動畫,整個過程出現動畫會多一個;三是插入的cell的出現動畫是默認是alpha從0到1的淡入效果。

在代碼中,想獲得一個插入的cell從小變大的出現效果和其它cell整體向後移動一個位置的動畫效果,可以如下實現:

override func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
    let attributes = super.initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath)?.copy() as? UICollectionViewLayoutAttributes
    if self.insertIndexPathArr.contains(itemIndexPath) {
        attributes?.transform = CGAffineTransformMakeScale(0.0, 0.0)
        attributes?.alpha = 0
    }
    else {
        //設置為前一個item的frame
        attributes?.frame = self.currentFrameWithIndexPath(NSIndexPath(forRow: itemIndexPath.row-1, inSection: itemIndexPath.section))
    }
    return attributes
}
override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
   let attributes = super.finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath)?.copy() as? UICollectionViewLayoutAttributes
     attributes?.frame = self.currentFrameWithIndexPath(NSIndexPath(forRow: itemIndexPath.row+1, inSection: itemIndexPath.section))
     return attributes
}

 

這裡為了看到效果,我在模擬器的Debug模式下勾選了Slow Animations調慢了動畫: 

126.gif

刪除動畫:

在上面的位置1插入一個cell後,cell的數量變成了4個,分別是0、1、2、3,它們對應的indexPath為(0,0)、(0,1)、(0,2)、(0,3)。當要刪除位置1的cell時,與插入類似,系統默認也會有三個動畫過程,如下圖:

125.png

其中,動畫過程1是在位置1執行一個消失動畫;過程2是位置1的出現動畫和位置2的消失動畫重合而成;過程3是位置2的出現動畫和位置3的消失動畫重合而成。

需要注意的是,一是與插入不同,重合後的效果是cell向前平移了一個位置;二是最後一個位置只有消失動畫沒有出現動畫,整個過程消失動畫數會多一個;三是刪除的cell的出現動畫默認是從1到0的淡出效果。

在代碼中,實現一個與插入相對應的動畫,即刪除的cell從大到小的淡出效果和其它cell整體向前移動一個位置的效果,可以如下實現:

override func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
    let attributes = super.initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath)?.copy() as? UICollectionViewLayoutAttributes
    attributes?.frame = self.currentFrameWithIndexPath(NSIndexPath(forRow: itemIndexPath.row+1, inSection: itemIndexPath.section))
    return attributes
}
override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
        let attributes = super.finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath)?.copy() as? UICollectionViewLayoutAttributes
    if self.deleteIndexPathArr.contains(itemIndexPath) {
        //這裡寫成縮放成(0,0)直接就不見了
        attributes?.transform = CGAffineTransformMakeScale(0.1, 0.1)
        attributes?.alpha = 0.0
    }
    else {
        attributes?.frame = self.currentFrameWithIndexPath(NSIndexPath(forRow: itemIndexPath.row-1, inSection: itemIndexPath.section))
    }
    return attributes
}

效果如下:

124.gif

刷新動畫:

在官方的解釋中,刷新是先刪除然後插入。其實它就是先執行所有cell的消失動畫;在此之後,它又會執行所有cell的出現動畫。 在系統當中,需要注意的是默認出現動畫是一個alpha從0到1的淡入效果,而消失動畫則是alpha從1到0的淡入效果;與插入動畫和刪除動畫不同的是,刷新動畫會成對存在,即消失動畫與出現動畫數量相等。

在這裡,實現一個點擊某個cell時,當前選中的cell變大的效果,而它旁邊的cell被推開的動畫效果。在這裡我不需要淡入和淡出效果,所以修改了消失時alpha為1.0,代碼如下:

override func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
    let attributes = super.initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath)?.copy() as? UICollectionViewLayoutAttributes
    attributes?.frame = self.lastFrameWithIndexPath(itemIndexPath)
    return attributes
}
override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
    let attributes = super.finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath)?.copy() as? UICollectionViewLayoutAttributes
    //注意,這裡alpha設置為不透明,系統默認返回是0,即一個淡出的效果
    attributes?.alpha = 1.0
    attributes?.frame = self.currentFrameWithIndexPath(itemIndexPath)
    return attributes
}

效果如下:

123.gif

移動動畫:

移動一個cell到另一個位置時,會引起當前cell到目標位置之間所有cell布局發生變化,從而形成一系列的動畫。在這個動畫過程中,每個indexPath都會有一個出現動畫和一個消失動畫。

例如,在系統默認情況下,0位置cell移動到2位置cell的時候,我們會看到三個動畫過程,如下圖:

QQ截圖20160205142443.png

但是,其實它內部執行了六個動畫,只是其中兩兩之間動畫重合了而已。其中動畫過程1是1位置的消失動畫和0位置出現動畫重合;動畫過程2是0位置的消失動畫和2位置的出現動畫重合;動畫過程3是2位置的消失動畫和1位置的出現動畫重合。

其中值得注意的有兩點: 
1、消失動畫和出現動畫數量相等 
2、動畫的重合與刷新動畫不同,與插入和刪除動畫類似,它們不同位置之間的消失動畫與出現動畫重合。

在這裡,實現一個移動cell時旋轉180°到目標位置效果,實現如下:

override func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
   let attributes = super.initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath)?.copy() as? UICollectionViewLayoutAttributes
   if itemIndexPath == self.afterMoveIndexPath {
       //afterMoveIndexPath的消失動畫和beforeMoveIndexPath的出現動畫重合
       //init是設置起點,而final設置終點,理論是不重合的
       attributes?.transform3D = CATransform3DMakeRotation(-1*CGFloat(M_PI), 0, 0, -1)
   }
   return attributes
}
override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
   let attributes = super.finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath)?.copy() as? UICollectionViewLayoutAttributes
   if self.beforeMoveIndexPath == itemIndexPath {
       //afterMoveIndexPath的消失動畫和beforeMoveIndexPath的出現動畫重合,設置他們旋轉的角度一樣,方向相反
       attributes?.transform3D = CATransform3DMakeRotation(-1*CGFloat(M_PI), 0, 0, -1)
   }
   return attributes
}

效果如下:

7746cd07jw1eyb0375fz7g208j0g9go4.gif

上面都是純顏色,在示例工程UICollectionViewAnimationDemo中,我還添加了一個圖片的BGSimpleImageSelectCollectionViewDemo2。布局基本上相同,唯一不同的是圖片因為上下不可以倒轉,沒辦法做到統一的旋轉180°。

效果如下:

7746cd07jw1eybewu3qyxg208j0g9k2r.gif

總結:

  1. CollectionView更新時,執行動畫的時候會訪問layout中哪些api,整個流程是如何形成的 

  2. 修改CollectionView動畫就是修改出現動畫的起點和消失動畫的終點,即layout當中的initialLayoutAttributesForAppearingItemAtIndexPath和finalLayoutAttributesForDisappearingItemAtIndexPath方法進行修改。 

  3. 插入、刪除、刷新、移動內部執行哪些動畫,我們如何去修改。

參考:

Collection View 動畫

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved