objc系列譯文(12.5):Collection View 動(dòng)畫
2014/05/28 ·iOS,開發(fā)·iOS,動(dòng)畫
分享到:8
原文出處:Engin Kurutepe譯文出處:吳迪(@唯木念)歡迎分享原創(chuàng)到伯樂頭條
UICollectionView和相關(guān)類的設(shè)置非常靈活和強(qiáng)大。但是靈活性一旦增強(qiáng),某種程度上也增加了其復(fù)雜性:UICollectionView比老式的UITableView更有深度哗总,適用性也更強(qiáng)誊役。
Collection View 深入太多了,事實(shí)上逗爹,Ole Begeman和Ash Furrow之前曾在 objc.io 上發(fā)表過自定義 Collection View 布局和UICollectionView + UIKit 力學(xué)亡嫌,但是我依然有一些他們沒有提及的內(nèi)容可以寫。在這篇文章中掘而,我假設(shè)你已經(jīng)非常熟悉UICollectionView的基本布局挟冠,并且至少閱讀了蘋果精彩的編程指南以及 Ole 之前的文章。
本文的第一部分將集中討論并舉例說明如何用不同的類和方法來共同幫助實(shí)現(xiàn)一些常見的UICollectionView動(dòng)畫袍睡。在第二部分知染,我們將看一下帶有 collection views 的 view controller 轉(zhuǎn)場(chǎng)動(dòng)畫以及在useLayoutToLayoutNavigationTransitions可用時(shí)使用其進(jìn)行轉(zhuǎn)場(chǎng),如果不可用時(shí)斑胜,我們會(huì)實(shí)現(xiàn)一個(gè)自定義轉(zhuǎn)場(chǎng)動(dòng)畫控淡。
你可以在 GitHub 中找到本文提到的兩個(gè)示例工程:
自定義 collection view 轉(zhuǎn)場(chǎng)動(dòng)畫
Collection View 布局動(dòng)畫
標(biāo)準(zhǔn)UICollectionViewFlowLayout除了動(dòng)畫是非常容易自定義的,蘋果選擇了一種安全的途徑去實(shí)現(xiàn)一個(gè)簡(jiǎn)單的淡入淡出動(dòng)畫作為所有布局的默認(rèn)動(dòng)畫止潘。如果你想實(shí)現(xiàn)自定義動(dòng)畫掺炭,最好的辦法是子類化UICollectionViewFlowLayout并且在適當(dāng)?shù)牡胤綄?shí)現(xiàn)你的動(dòng)畫。讓我們通過一些例子來了解UICollectionViewFlowLayout子類中的一些方法如何協(xié)助完成自定義動(dòng)畫凭戴。
插入刪除元素
一般來說涧狮,我們對(duì)布局屬性從初始狀態(tài)到結(jié)束狀態(tài)進(jìn)行線性插值來計(jì)算 collection view 的動(dòng)畫參數(shù)。然而么夫,新插入或者刪除的元素并沒有最初或最終狀態(tài)來進(jìn)行插值勋篓。要計(jì)算這樣的 cells 的動(dòng)畫,collection view 將通過initialLayoutAttributesForAppearingItemAtIndexPath:以及finalLayoutAttributesForAppearingItemAtIndexPath:方法來詢問其布局對(duì)象魏割,以獲取最初的和最后的屬性譬嚣。蘋果默認(rèn)的實(shí)現(xiàn)中,對(duì)于特定的某個(gè) indexPath钞它,返回的是它的通常的位置拜银,但alpha值為 0.0殊鞭,這就產(chǎn)生了一個(gè)淡入或淡出動(dòng)畫。如果你想要更漂亮的效果尼桶,比如你的新的 cells 從屏幕底部發(fā)射并且旋轉(zhuǎn)飛到對(duì)應(yīng)位置操灿,你可以如下實(shí)現(xiàn)這樣的布局子類:
-(UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
attr.transform = CGAffineTransformRotate(CGAffineTransformMakeScale(0.2, 0.2), M_PI);
attr.center = CGPointMake(CGRectGetMidX(self.collectionView.bounds), CGRectGetMaxY(self.collectionView.bounds));
return attr;
}
結(jié)果如下:
對(duì)應(yīng)的finalLayoutAttributesForAppearingItemAtIndexPath:方法中,除了設(shè)定了不同的 transform 以外泵督,其他都很相似趾盐。
響應(yīng)設(shè)備旋轉(zhuǎn)
設(shè)備方向變化通常會(huì)導(dǎo)致 collection view 的 bounds 變化。如果通過shouldInvalidateLayoutForBoundsChange:判定為布局需要被無效化并重新計(jì)算的時(shí)候小腊,布局對(duì)象會(huì)被詢問以提供新的布局救鲤。UICollectionViewFlowLayout的默認(rèn)實(shí)現(xiàn)正確地處理了這個(gè)情況,但是如果你子類化UICollectionViewLayout的話秩冈,你需要在邊界變化時(shí)返回YES:
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
CGRect oldBounds = self.collectionView.bounds;
if (!CGSizeEqualToSize(oldBounds.size, newBounds.size)) {
return YES;
}
return NO;
}
在 bounds 變化的動(dòng)畫中本缠,collection view 表現(xiàn)得像當(dāng)前顯示的元素被移除然后又在新的 bounds 中被被重新插入,這會(huì)對(duì)每個(gè) IndexPath 產(chǎn)生一系列的finalLayoutAttributesForAppearingItemAtIndexPath:和initialLayoutAttributesForAppearingItemAtIndexPath:的調(diào)用入问。
如果你在插入和刪除的時(shí)候加入了非常炫的動(dòng)畫丹锹,現(xiàn)在你應(yīng)該看看為何蘋果明智的使用簡(jiǎn)單的淡入淡出動(dòng)畫作為默認(rèn)效果:
啊哦…
為了防止這種不想要的動(dòng)畫,初始化位置 -> 刪除動(dòng)畫 -> 插入動(dòng)畫 -> 最終位置的順序必須完全匹配 collection view 的每一項(xiàng)芬失,以便最終呈現(xiàn)出一個(gè)平滑動(dòng)畫楣黍。換句話說,finalLayoutAttributesForAppearingItemAtIndexPath:以及initialLayoutAttributesForAppearingItemAtIndexPath:應(yīng)該針對(duì)元素到底是真的在顯示或者消失棱烂,還是 collection view 正在經(jīng)歷的邊界改變動(dòng)畫的不同情況锡凝,做出不同反應(yīng),并返回不同的布局屬性垢啼。
幸運(yùn)的是窜锯,collection view 會(huì)告知布局對(duì)象哪一種動(dòng)畫將被執(zhí)行。它分別通過調(diào)用prepareForAnimatedBoundsChange:和prepareForCollectionViewUpdates:來對(duì)應(yīng) bounds 變化以及元素更新芭析。出于本實(shí)例的說明目的锚扎,我們可以使用prepareForCollectionViewUpdates:來跟蹤更新對(duì)象:
- (void)prepareForCollectionViewUpdates:(NSArray *)updateItems
{
[super prepareForCollectionViewUpdates:updateItems];
NSMutableArray *indexPaths = [NSMutableArray array];
for (UICollectionViewUpdateItem *updateItem in updateItems) {
switch (updateItem.updateAction) {
case UICollectionUpdateActionInsert:
[indexPaths addObject:updateItem.indexPathAfterUpdate];
break;
case UICollectionUpdateActionDelete:
[indexPaths addObject:updateItem.indexPathBeforeUpdate];
break;
case UICollectionUpdateActionMove:
[indexPaths addObject:updateItem.indexPathBeforeUpdate];
[indexPaths addObject:updateItem.indexPathAfterUpdate];
break;
default:
NSLog(@"unhandled case: %@", updateItem);
break;
}
}
self.indexPathsToAnimate = indexPaths;
}
以及修改我們?cè)氐牟迦雱?dòng)畫,讓元素只在其正在被插入 collection view 時(shí)進(jìn)行發(fā)射:
- (UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
{
UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
if ([_indexPathsToAnimate containsObject:itemIndexPath]) {
attr.transform = CGAffineTransformRotate(CGAffineTransformMakeScale(0.2, 0.2), M_PI);
attr.center = CGPointMake(CGRectGetMidX(self.collectionView.bounds), CGRectGetMaxY(self.collectionView.bounds));
[_indexPathsToAnimate removeObject:itemIndexPath];
}
return attr;
}
如果這個(gè)元素沒有正在被插入馁启,那么將通過layoutAttributesForItemAtIndexPath來返回一個(gè)普通的屬性驾孔,以此取消特殊的外觀動(dòng)畫。結(jié)合finalLayoutAttributesForAppearingItemAtIndexPath:中相應(yīng)的邏輯惯疙,最終將會(huì)使元素能夠在 bounds 變化時(shí)翠勉,從初始位置到最終位置以很流暢的動(dòng)畫形式實(shí)現(xiàn),從而建立一個(gè)簡(jiǎn)單但很酷的動(dòng)畫效果:
交互式布局動(dòng)畫
Collection views 讓用戶通過手勢(shì)實(shí)現(xiàn)與布局交互這件事變得很容易霉颠。如蘋果建議的那樣对碌,為 collection view 布局添加交互的途徑一般會(huì)遵循以下步驟:
創(chuàng)建手勢(shì)識(shí)別
將手勢(shì)識(shí)別添加給 collection view
通過手勢(shì)來驅(qū)動(dòng)布局動(dòng)畫
讓我們來看看我們?nèi)绾慰梢越⒁恍┯脩艨煽s放捏合的元素,以及一旦用戶釋放他們的捏合手勢(shì)元素返回到原始大小蒿偎。
我們的處理方式可能會(huì)是這樣:
- (void)handlePinch:(UIPinchGestureRecognizer *)sender {
if ([sender numberOfTouches] != 2)
return;
if (sender.state == UIGestureRecognizerStateBegan ||
sender.state == UIGestureRecognizerStateChanged) {
// 獲取捏合的點(diǎn)
CGPoint p1 = [sender locationOfTouch:0 inView:[self collectionView]];
CGPoint p2 = [sender locationOfTouch:1 inView:[self collectionView]];
// 計(jì)算擴(kuò)展距離
CGFloat xd = p1.x - p2.x;
CGFloat yd = p1.y - p2.y;
CGFloat distance = sqrt(xd*xd + yd*yd);
// 更新自定義布局參數(shù)以及無效化
FJAnimatedFlowLayout* layout = (FJAnimatedFlowLayout*)[[self collectionView] collectionViewLayout];
NSIndexPath *pinchedItem = [self.collectionView indexPathForItemAtPoint:CGPointMake(0.5*(p1.x+p2.x), 0.5*(p1.y+p2.y))];
[layout resizeItemAtIndexPath:pinchedItem withPinchDistance:distance];
[layout invalidateLayout];
}
else if (sender.state == UIGestureRecognizerStateCancelled ||
sender.state == UIGestureRecognizerStateEnded){
FJAnimatedFlowLayout* layout = (FJAnimatedFlowLayout*)[[self collectionView] collectionViewLayout];
[self.collectionView
performBatchUpdates:^{
[layout resetPinchedItem];
}
completion:nil];
}
}
這個(gè)捏合操作需要計(jì)算捏合距離并找出被捏合的元素朽们,并且在用戶捏合的時(shí)候通知布局以實(shí)現(xiàn)自身更新怀读。當(dāng)捏合手勢(shì)結(jié)束的時(shí)候,布局會(huì)做一個(gè)批量更新動(dòng)畫返回原始尺寸骑脱。
另一方面菜枷,我們的布局始終在跟蹤捏合的元素以及期望尺寸,并在需要的時(shí)候提供正確的屬性:
- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attrs = [super layoutAttributesForElementsInRect:rect];
if (_pinchedItem) {
UICollectionViewLayoutAttributes *attr = [[attrs filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"indexPath == %@", _pinchedItem]] firstObject];
attr.size = _pinchedItemSize;
attr.zIndex = 100;
}
return attrs;
}
小結(jié)
我們通過一些例子來說明了如何在 collection view 布局中創(chuàng)建自定義動(dòng)畫叁丧。雖然UICollectionViewFlowLayout并不直接允許定制動(dòng)畫啤誊,但是蘋果工程師提供了清晰的架構(gòu)讓你可以子類化并實(shí)現(xiàn)各種自定義行為。從本質(zhì)來說拥娄,在你的UICollectionViewLayout子類中正確地響應(yīng)以下信號(hào)蚊锹,并對(duì)那些要求返回UICollectionViewLayoutAttributes的方法返回合適的屬性,那么實(shí)現(xiàn)自定義布局和動(dòng)畫的唯一約束就是你的想象力:
prepareLayout
prepareForCollectionViewUpdates:
finalizeCollectionViewUpdates
prepareForAnimatedBoundsChange:
finalizeAnimatedBoundsChange
shouldInvalidateLayoutForBoundsChange:
更引人入勝的動(dòng)畫可以結(jié)合像在 objc.io話題 #5中 UIKit 力學(xué)這樣的技術(shù)來實(shí)現(xiàn)条舔。
帶有 Collection views 的 View controller 轉(zhuǎn)場(chǎng)
就如Chris之前在 objc.io 的文章中所說的那樣,iOS 7 中的一個(gè)重大更新是自定義 view controller 轉(zhuǎn)場(chǎng)動(dòng)畫乏矾。與自定義轉(zhuǎn)場(chǎng)動(dòng)畫相呼應(yīng)孟抗,蘋果也在UICollectionViewController添加了useLayoutToLayoutNavigationTransitions標(biāo)記來在可復(fù)用的單個(gè) collection view 間啟用導(dǎo)航轉(zhuǎn)場(chǎng)。蘋果自己的照片和日歷應(yīng)用就是這類轉(zhuǎn)場(chǎng)動(dòng)畫的非常好的代表作钻心。
UICollectionViewController 實(shí)例之間的轉(zhuǎn)場(chǎng)動(dòng)畫
讓我們來看看我們?nèi)绾文軌蚶蒙弦还?jié)相同的示例項(xiàng)目達(dá)到類似的效果:
為了使布局到布局的轉(zhuǎn)場(chǎng)動(dòng)畫工作凄硼,navigation controller 的 root view controller 必須是一個(gè)useLayoutToLayoutNavigationTransitions設(shè)置為NO的 collection view controller。當(dāng)另一個(gè)useLayoutToLayoutNavigationTransitions設(shè)置為YES的UICollectionViewController實(shí)例被 push 到根視圖控制器之上時(shí)捷沸,navigation controller 會(huì)用布局轉(zhuǎn)場(chǎng)動(dòng)畫來代替標(biāo)準(zhǔn)的 push 轉(zhuǎn)場(chǎng)動(dòng)畫摊沉。這里要注意一個(gè)重要的細(xì)節(jié),根視圖控制器的 collection view 實(shí)例被回收用于在導(dǎo)航棧上 push 進(jìn)來的 collection 控制器中痒给,如果你試圖在viewDidLoad之類的方法中中設(shè)置 collection view 屬性说墨, 它們將不會(huì)有任何反應(yīng),你也不會(huì)收到任何警告苍柏。
這個(gè)行為可能最常見的陷阱是期望回收的 collection view 根據(jù)頂層的 collection 視圖控制器來更新數(shù)據(jù)源和委托尼斧。它當(dāng)然不會(huì)這樣:根 collection 視圖控制器會(huì)保持?jǐn)?shù)據(jù)源和委托,除非我們做點(diǎn)什么试吁。
解決此問題的方法是實(shí)現(xiàn) navigation controller 的委托方法棺棵,并根據(jù)導(dǎo)航堆棧頂部的當(dāng)前視圖控制器的需要正確設(shè)置 collection view 的數(shù)據(jù)源和委托。在我們簡(jiǎn)單的例子中熄捍,這可以通過以下方式實(shí)現(xiàn):
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([viewController isKindOfClass:[FJDetailViewController class]]) {
FJDetailViewController *dvc = (FJDetailViewController*)viewController;
dvc.collectionView.dataSource = dvc;
dvc.collectionView.delegate = dvc;
[dvc.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_selectedItem inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
}
else if (viewController == self){
self.collectionView.dataSource = self;
self.collectionView.delegate = self;
}
}
當(dāng)詳細(xì)頁面的 collection view 被推入導(dǎo)航棧時(shí)烛恤,我們重新設(shè)置 collection view 的數(shù)據(jù)源到詳細(xì)視圖控制器,確保只有被選擇的 cell 顏色顯示在詳細(xì)頁面的 collection view 中余耽。如果我們不打算這樣做缚柏,布局依然可以正確過渡,但是collection 將顯示所有的 cells碟贾。在實(shí)際應(yīng)用中船惨,detail 的數(shù)據(jù)源通常負(fù)責(zé)在轉(zhuǎn)場(chǎng)動(dòng)畫過程中顯示更詳細(xì)的數(shù)據(jù)柜裸。
用于常規(guī)轉(zhuǎn)換的 Collection View 布局動(dòng)畫
使用了useLayoutToLayoutNavigationTransitions的布局和布局間導(dǎo)航轉(zhuǎn)換是很有用的,但卻局限于僅在 兩個(gè) view controller 都是UICollectionViewController的實(shí)例粱锐,并且轉(zhuǎn)場(chǎng)的必須發(fā)生在頂級(jí) collection views 之間疙挺。為了達(dá)到在任意視圖控制器的任意 collection view 之間都能實(shí)現(xiàn)相似的過渡,我們需要自定義一個(gè) view collection 的轉(zhuǎn)場(chǎng)動(dòng)畫怜浅。
針對(duì)此類自定義過渡的動(dòng)畫控制器铐然,需要遵循以下步驟進(jìn)行設(shè)計(jì):
對(duì)初始的 collection view 中的所有可見元素制作截圖
將截圖添加到轉(zhuǎn)場(chǎng)上下文的 container view 中
運(yùn)用目標(biāo) collection view 的布局計(jì)算最終位置
制作動(dòng)畫使快照到正確的位置
當(dāng)目標(biāo) collection view 可見時(shí)刪除截圖
一個(gè)這樣的動(dòng)畫設(shè)計(jì)有兩重缺陷:它只能對(duì)初始的 collection view 的可見元素制作動(dòng)畫,因?yàn)?a target="_blank" rel="nofollow">快照 APIs只能工作于屏幕上可見的 view恶座,另外搀暑,依賴于可見的元素?cái)?shù)量,可能會(huì)有很多的 views 需要進(jìn)行正確的跟蹤并為其制作動(dòng)畫跨琳。但另一方面自点,這種設(shè)計(jì)又具有一個(gè)明顯的優(yōu)勢(shì),那就是它可以為所有類型的UICollectionViewLayout組合所使用脉让。這樣一個(gè)系統(tǒng)的實(shí)現(xiàn)就留給讀者們?nèi)ミM(jìn)行練習(xí)吧桂敛。
在附帶的演示項(xiàng)目中我們用另一種途徑進(jìn)行了實(shí)現(xiàn),它依賴于一些UICollectionViewFlowLayout的巧合溅潜。
基本的想法是术唬,因?yàn)樵?collection view 和目標(biāo) collection view 都擁有有效的 flow layouts,因此源 layout 的布局屬性正好可以用作目標(biāo) collection view 的布局中的初始布局屬性滚澜,以此驅(qū)動(dòng)轉(zhuǎn)場(chǎng)動(dòng)畫粗仓。一旦正確建立,就算對(duì)于那些一開始在屏幕上不可見的元素设捐,collection view 的機(jī)制都將為我們追蹤它們并進(jìn)行動(dòng)畫借浊。下面是我們的動(dòng)畫控制器中的animateTransition:的核心代碼:
CGRect initialRect = [inView.window convertRect:_fromCollectionView.frame fromView:_fromCollectionView.superview];
CGRect finalRect?? = [transitionContext finalFrameForViewController:toVC];
UICollectionViewFlowLayout *toLayout = (UICollectionViewFlowLayout*) _toCollectionView.collectionViewLayout;
UICollectionViewFlowLayout *currentLayout = (UICollectionViewFlowLayout*) _fromCollectionView.collectionViewLayout;
//制作原來布局的拷貝
UICollectionViewFlowLayout *currentLayoutCopy = [[UICollectionViewFlowLayout alloc] init];
currentLayoutCopy.itemSize = currentLayout.itemSize;
currentLayoutCopy.sectionInset = currentLayout.sectionInset;
currentLayoutCopy.minimumLineSpacing = currentLayout.minimumLineSpacing;
currentLayoutCopy.minimumInteritemSpacing = currentLayout.minimumInteritemSpacing;
currentLayoutCopy.scrollDirection = currentLayout.scrollDirection;
//將拷貝賦值給源 collection view
[self.fromCollectionView setCollectionViewLayout:currentLayoutCopy animated:NO];
UIEdgeInsets contentInset = _toCollectionView.contentInset;
CGFloat oldBottomInset = contentInset.bottom;
//強(qiáng)制在目標(biāo) collection view 中設(shè)定一個(gè)很大的 bottom inset
contentInset.bottom = CGRectGetHeight(finalRect)-(toLayout.itemSize.height+toLayout.sectionInset.bottom+toLayout.sectionInset.top);
self.toCollectionView.contentInset = contentInset;
//將源布局設(shè)置給目標(biāo) collection view
[self.toCollectionView setCollectionViewLayout:currentLayout animated:NO];
toView.frame = initialRect;
[inView insertSubview:toView aboveSubview:fromView];
[UIView
animateWithDuration:[self transitionDuration:transitionContext]
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
//使用最終 frame 制作動(dòng)畫
toView.frame = finalRect;
//在 performUpdates 中設(shè)定最終的布局
[_toCollectionView
performBatchUpdates:^{
[_toCollectionView setCollectionViewLayout:toLayout animated:NO];
}
completion:^(BOOL finished) {
_toCollectionView.contentInset = UIEdgeInsetsMake(contentInset.top,
contentInset.left,
oldBottomInset,
contentInset.right);
}];
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
首先,動(dòng)畫控制器確保目標(biāo) collection view 以與原來的 collection view 完全相同的框架和布局作為開始萝招。接著巴碗,它將源 collection view 的布局設(shè)定給目標(biāo) collection view,以確保其不會(huì)失效即寒。與此同時(shí)橡淆,該布局已經(jīng)復(fù)制到另一個(gè)新的布局對(duì)象中,而這個(gè)布局對(duì)象則是為防止在導(dǎo)航回原始視圖控制器時(shí)出現(xiàn)奇怪的布局 bug母赵。我們還會(huì)強(qiáng)制在目標(biāo) collection view 的底部設(shè)定一個(gè)很大的 content inset逸爵,來確保布局在動(dòng)畫的初始位置時(shí)保持在一行上。觀察日志的話凹嘲,你會(huì)發(fā)現(xiàn)由于元素的尺寸加上 inset 的尺寸會(huì)比 collection view 的非滾動(dòng)維度要大师倔,因此 collection view 會(huì)在控制臺(tái)警告。在這樣的情況下周蹭,collection view 的行為是沒有定義的趋艘,我們也只是使用這樣一個(gè)不穩(wěn)定的狀態(tài)來作為我們轉(zhuǎn)換動(dòng)畫的初始狀態(tài)疲恢。最后,復(fù)雜的動(dòng)畫 block 將展現(xiàn)它的魅力瓷胧,首先將目標(biāo) collection view 的框架設(shè)定到最終位置显拳,然后在performBatchUpdates:completion:的 update block 中執(zhí)行一個(gè)無動(dòng)畫的布局來改變至最終布局,緊隨其后便是在 completion block 中將 content insets 重置為原始值搓萧。
小結(jié)
我們討論了兩種可以在 collection view 之間實(shí)現(xiàn)布局轉(zhuǎn)場(chǎng)的途徑杂数。一種使用了內(nèi)置的useLayoutToLayoutNavigationTransitions,看起來令人印象深刻并且極其容易實(shí)現(xiàn)瘸洛,缺點(diǎn)就是可以使用的范圍較為局限揍移。由于useLayoutToLayoutNavigationTransitions在一些案例中不能使用,想驅(qū)動(dòng)自定義的過渡動(dòng)畫的話反肋,就需要一個(gè)自定義的 animator那伐。這篇文章中,我們看到了如何實(shí)現(xiàn)這樣一個(gè) animator石蔗,然而罕邀,由于你的應(yīng)用程序大概肯定會(huì)需要在兩個(gè)和本例完全不同的 view 結(jié)構(gòu)中實(shí)現(xiàn)完全不同的動(dòng)畫,所以正如此例中做的那樣抓督,不要吝于嘗試不同的方法來探究其是否能夠工作燃少。