歡迎來(lái)到支線任務(wù)橘洞,返回主線任務(wù)點(diǎn)這里,移動(dòng) cell 的動(dòng)畫(huà)效果:
Github 地址:CollectionViewAnimation
如果你移動(dòng) cell 的時(shí)候錄下屏幕慢放就會(huì)發(fā)現(xiàn)系統(tǒng)的實(shí)現(xiàn)是办陷,所有相對(duì)位置發(fā)生變化的 cell 都直接移動(dòng)到目標(biāo)位置探入。我們需要取消要移動(dòng)的 cell 的默認(rèn)動(dòng)畫(huà)效果以免它產(chǎn)生干擾,很簡(jiǎn)單懂诗,隱藏就好了蜂嗽。
回到布局更新流程,finalLayoutAttributesForDisappearingItemAtIndexPath:
方法返回的值決定了 cell 開(kāi)始移動(dòng)前的布局信息殃恒,我們?cè)谶@里修改布局信息植旧,但是布局系統(tǒng)會(huì)針對(duì)所有位置的 cell 都調(diào)用該方法,所以我們要過(guò)濾信息离唐,只隱藏真正移動(dòng)的 cell病附。
在布局子類(lèi)中添加以下屬性來(lái)收集移動(dòng)操作的信息:
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:
將真正移動(dòng)的 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 是否是真正移動(dòng)的 cell 的 indexPath
func inBeforeMoveSet(moveSet: Set<UICollectionViewUpdateItem>, withIndexPath indexPath: NSIndexPath) -> Bool
這樣在 cell 開(kāi)始移動(dòng)前就隱藏了,不會(huì)影響我們的小動(dòng)作了亥鬓。
接下來(lái)布局系統(tǒng)會(huì)調(diào)用initialLayoutAttributesForAppearingItemAtIndexPath:
來(lái)返回移動(dòng)的 cell 出現(xiàn)在目標(biāo)位置時(shí)的初始布局信息完沪,這里有個(gè)小問(wèn)題,雖然這里的參數(shù)itemIndexPath
是要移動(dòng)的 cell 的目標(biāo)位置嵌戈,但是此時(shí)通過(guò)itemIndexPath
獲取的 cell 并不是移動(dòng)到該位置的 cell覆积,在此刻 cells 依然使用原來(lái)位置的 indexPath,可以利用在prepareForCollectionViewUpdates
收集的布局信息來(lái)獲取原來(lái)的索引位置從而獲取我們需要的 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)
//移動(dòng) cell 需要的信息多一些宽档,插入和刪除時(shí)都是在原地重組,移動(dòng)時(shí)就要換位置了庵朝,而且要指定操作類(lèi)型
cell?.refactorWithPiecesRegion(oldAttr?.frame, assembleRect: assemebleRect, shiningColor: nil, cellAction: .Move)
}
return attr
}
//判斷 indexPath 是否真正移動(dòng)的 cell 的新的 indexPath
func inAfterMoveSet(moveSet: Set<UICollectionViewUpdateItem>, withIndexPath indexPath: NSIndexPath) -> Bool
這樣一切就結(jié)束了吗冤,不過(guò)實(shí)際上又厉,在這里,重組動(dòng)畫(huà)的實(shí)現(xiàn)被修改了以適應(yīng)這里的需求椎瘟,不過(guò)你不用擔(dān)心這部分覆致。
我依然覺(jué)得默認(rèn)的移動(dòng)動(dòng)畫(huà)效果比較好,安安靜靜肺蔚,看著舒服篷朵,好吧,主要是移動(dòng)動(dòng)畫(huà)和插入動(dòng)畫(huà)一樣被布局過(guò)程中斷了婆排,不然應(yīng)該會(huì)好看點(diǎn)的声旺。不過(guò),現(xiàn)在我們知道了怎么定制這個(gè)過(guò)程段只,這個(gè)更有成就感不是嗎腮猖。
Github 地址:CollectionViewAnimation