歡迎來到支線任務,返回主線任務點這里,移動 cell 的動畫效果:
Github 地址:CollectionViewAnimation
如果你移動 cell 的時候錄下屏幕慢放就會發現系統的實現是,所有相對位置發生變化的 cell 都直接移動到目標位置。我們需要取消要移動的 cell 的默認動畫效果以免它產生干擾,很簡單,隱藏就好了。
回到布局更新流程,finalLayoutAttributesForDisappearingItemAtIndexPath:
方法返回的值決定了 cell 開始移動前的布局信息,我們在這里修改布局信息,但是布局系統會針對所有位置的 cell 都調用該方法,所以我們要過濾信息,只隱藏真正移動的 cell。
在布局子類中添加以下屬性來收集移動操作的信息:
var movedItemsToAnimate: Set<UICollectionViewUpdateItem> = []
override func prepareForCollectionViewUpdates(updateItems: [UICollectionViewUpdateItem]) {
super.prepareForCollectionViewUpdates(updateItems)
for updateItem in updateItems{
switch updateItem.updateAction{
....
case .Move:
movedItemsToAnimate.insert(updateItem)
default: break
}
}
}
然后在finalLayoutAttributesForDisappearingItemAtIndexPath:
將真正移動的 cell 的 alpha 值改為0:
override func finalLayoutAttributesForDisappearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
let attr = self.layoutAttributesForItemAtIndexPath(itemIndexPath)
if movedItemsToAnimate.count > 0 && inBeforeMoveSet(movedItemsToAnimate, withIndexPath: itemIndexPath){
attr?.alpha = 0
}
return attr
}
//判斷 indexPath 是否是真正移動的 cell 的 indexPath
func inBeforeMoveSet(moveSet: Set<UICollectionViewUpdateItem>, withIndexPath indexPath: NSIndexPath) -> Bool
這樣在 cell 開始移動前就隱藏了,不會影響我們的小動作了。
接下來布局系統會調用initialLayoutAttributesForAppearingItemAtIndexPath:
來返回移動的 cell 出現在目標位置時的初始布局信息,這里有個小問題,雖然這里的參數itemIndexPath
是要移動的 cell 的目標位置,但是此時通過itemIndexPath
獲取的 cell 并不是移動到該位置的 cell,在此刻 cells 依然使用原來位置的 indexPath,可以利用在prepareForCollectionViewUpdates
收集的布局信息來獲取原來的索引位置從而獲取我們需要的 cell。
func getOldIndexPathInMoveSet(moveSet: Set<UICollectionViewUpdateItem>, withIndexPath indexPath: NSIndexPath) -> NSIndexPath{
let filteredResult = moveSet.filter({
element in
let newIndexPath = element.indexPathAfterUpdate
return newIndexPath.section == indexPath.section && newIndexPath.item == indexPath.item
})
return filteredResult.first!.indexPathBeforeUpdate
}
override func initialLayoutAttributesForAppearingItemAtIndexPath(itemIndexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
let attr = self.layoutAttributesForItemAtIndexPath(itemIndexPath)
if movedItemsToAnimate.count > 0 && inAfterMoveSet(movedItemsToAnimate, withIndexPath: itemIndexPath){
let oldIndexPath = getOldIndexPathInMoveSet(movedItemsToAnimate, withIndexPath: itemIndexPath)
let cell = collectionView?.cellForItemAtIndexPath(oldIndexPath)
let assemebleRect = attr?.frame
let oldAttr = self.layoutAttributesForItemAtIndexPath(oldIndexPath)
//移動 cell 需要的信息多一些,插入和刪除時都是在原地重組,移動時就要換位置了,而且要指定操作類型
cell?.refactorWithPiecesRegion(oldAttr?.frame, assembleRect: assemebleRect, shiningColor: nil, cellAction: .Move)
}
return attr
}
//判斷 indexPath 是否真正移動的 cell 的新的 indexPath
func inAfterMoveSet(moveSet: Set<UICollectionViewUpdateItem>, withIndexPath indexPath: NSIndexPath) -> Bool
這樣一切就結束了,不過實際上,在這里,重組動畫的實現被修改了以適應這里的需求,不過你不用擔心這部分。
我依然覺得默認的移動動畫效果比較好,安安靜靜,看著舒服,好吧,主要是移動動畫和插入動畫一樣被布局過程中斷了,不然應該會好看點的。不過,現在我們知道了怎么定制這個過程,這個更有成就感不是嗎。
Github 地址:CollectionViewAnimation