這個topic其實(shí)起源于看到了我們別的組的一個小效果句惯,就是首頁有一個小標(biāo)簽秕衙,標(biāo)簽點(diǎn)開會以圓形擴(kuò)散的效果打開一個付費(fèi)頁~ 類似醬紫:
頁面的切換效果搀玖,其實(shí)就是轉(zhuǎn)場動畫藏斩。
官方支持以下幾種方式的自定義轉(zhuǎn)場:
- UINavigationController 中 push 和 pop
- UITabBarController 中切換 Tab
- presentViewController以及dismiss(modal模態(tài)轉(zhuǎn)場)
- UICollectionViewController 的布局轉(zhuǎn)場:UICollectionViewController 與 UINavigationController 結(jié)合的轉(zhuǎn)場方式 (https://github.com/seedante/iOS-Note/wiki/View-Controller-Transition-PartIII#Chapter4)
我之前做過一個效果就是切換tab的時候from和to有一個漸隱和漸現(xiàn)的效果驯嘱,并且如果切換方向不同灶壶,from和to移動方向也不一樣,其實(shí)就是利用了轉(zhuǎn)場動畫蝶柿。
1. 自定義轉(zhuǎn)場動畫
自定義轉(zhuǎn)場其實(shí)就是通過UIViewControllerAnimatedTransitioning
協(xié)議實(shí)現(xiàn)的,所以我們需要先建一個實(shí)現(xiàn)了UIViewControllerAnimatedTransitioning
的NSObject:
#import "TransAnimation.h"
@import UIKit;
@interface TransAnimation ()<UIViewControllerAnimatedTransitioning>
@end
@implementation TransAnimation
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
}
- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
return 0.5;
}
這里的transitionDuration
就是把轉(zhuǎn)場動畫的時間返回非驮,然后animateTransition
就是真的做動畫交汤。
如果像上面那樣把animateTransition
寫成一個空block,那么當(dāng)你點(diǎn)擊跳轉(zhuǎn)另一個VC的時候會發(fā)現(xiàn)木有反應(yīng)哦劫笙,因?yàn)槟悴⒛居袑?shí)現(xiàn)轉(zhuǎn)場所以VC不會顯示
那么要怎么轉(zhuǎn)呢芙扎,例如醬紫:
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
// 獲取fromVc和toVc
UIViewController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *fromView = [[UIView alloc] init];;
UIView *toView = [[UIView alloc] init];
if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
// fromVc 的view
fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
// toVc的view
toView = [transitionContext viewForKey:UITransitionContextToViewKey];
} else {
// fromVc 的view
fromView = fromVc.view;
// toVc的view
toView =toVc.view;
}
CGFloat x = [UIScreen mainScreen].bounds.size.width;
// 轉(zhuǎn)場環(huán)境
UIView *containView = [transitionContext containerView];
toView.frame = CGRectMake(-x, 0, containView.frame.size.width, containView.frame.size.height);
[containView addSubview:fromView];
[containView addSubview:toView];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromView.transform = CGAffineTransformTranslate(fromView.transform, x, 0);
toView.transform = CGAffineTransformTranslate(toView.transform, x, 0);
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
那么如果
animateTransition
里實(shí)際做動畫的時長與transitionDuration
返回的時間不一致會怎樣呢?是會按照animateTransition
里面的把動畫乖乖做完的~ 不會到了animateTransition
就把動畫掐掉噠
蘋果大大告訴我們:
UIKit calls this method to obtain the timing information for your animations. The value you provide should be the same value that you use when configuring the animations in your animateTransition
method. UIKit uses the value to synchronize the actions of other objects that might be involved in the transition. For example, a navigation controller uses the value to synchronize changes to the navigation bar.
When determining the value to return, assume there will be no user interaction during the transition—even if you plan to support user interactions at runtime.
來劃重點(diǎn)啦~
-
animateTransition
里實(shí)際做動畫的時長與transitionDuration
返回的時間必須一致填大,否則可能其他參與轉(zhuǎn)場的組件例如navigation bar在動畫沒做完或者已經(jīng)做完很久了才顯示 -
transitionDuration
這個時間不需要考慮交互轉(zhuǎn)場的影響戒洼,按照非交互即可,即使你真的要用交互轉(zhuǎn)場
另外一個關(guān)于animateTransition
的注釋是醬紫的:
// This method can only be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
也就是說
animateTransition
只有在是交互式并且不是百分比交互式的情況下才可以是空哦另外一個注意點(diǎn)是允华,如果你在A push B的時候加了透明度變化的A漸隱圈浇,B漸現(xiàn)的動畫,在動畫結(jié)束需要讓A的透明度置位1靴寂,否則用戶從B pop回A的時候就是黑色沒有頁面啦:
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromView.alpha = 0;
toView.alpha = 1;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
fromView.alpha = 1;
}];
※ 圓形擴(kuò)散轉(zhuǎn)場
很多很炫的轉(zhuǎn)場都是通過截屏實(shí)現(xiàn)的磷蜀,比如開頭的一個圓形擴(kuò)散,就是把toView截屏百炬,然后以圓形的masklayer
逐漸顯示褐隆。劃重點(diǎn),用layer.mask實(shí)現(xiàn)遮罩剖踊,外加CAShapeLayer可以做path動畫
[fromView snapshotViewAfterScreenUpdates:NO];
[toView snapshotViewAfterScreenUpdates:YES];
注意snapshotViewAfterScreenUpdates
的參數(shù)就是是不是要立刻截屏庶弃,如果yes一般是給fromView的因?yàn)檗D(zhuǎn)場的時候它就顯示著衫贬,而對toView而言需要先add然后截屏,所以都選NO歇攻。
我們有些開門的動效固惯,就是利用把fromView截圖,然后生成兩個UIView半屏掉伏,把全屏截圖放進(jìn)去作為子view缝呕,分別clipToBounds,就得到了兩邊兩個view斧散,然后做位移動畫即可~
而有些放大縮小的例如點(diǎn)一個圖片全屏就是把toView截屏做放大縮小動畫供常,和圓形擴(kuò)散類似只是換為了位移大小變化動畫。
這里提供舉個定點(diǎn)圓形擴(kuò)散的轉(zhuǎn)場animator:
// .h
typedef NS_ENUM(NSUInteger, TransitionType) {
TransitionTypePresent = 0, //管理present動畫
TransitionTypeDissmis,
};
@interface TransAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@property(nonatomic) TransitionType transitionType;
@end
// .m
#import "TransAnimation.h"
@interface TransAnimation ()<CAAnimationDelegate>
@property (nonatomic) id<UIViewControllerContextTransitioning> transitionContext;
@end
@implementation TransAnimation
- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
return 3;
}
- (void)animateTransition:
(id<UIViewControllerContextTransitioning>)transitionContext {
if (self.transitionType == TransitionTypePresent) {
[self presentAnimation:transitionContext];
} else if (self.transitionType == TransitionTypeDissmis) {
[self dismissAnimation:transitionContext];
}
}
- (void)presentAnimation:
(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *tempView = [toVC.view snapshotViewAfterScreenUpdates:YES];
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
[containerView addSubview:fromVC.view];
[containerView addSubview:tempView];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGRect rect = CGRectMake(50, [UIScreen mainScreen].bounds.size.height - 50, 2, 2);
UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:rect];
UIBezierPath *endPath = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:sqrt(screenHeight * screenHeight + screenWidth * screenWidth) startAngle:0 endAngle:M_PI*2 clockwise:YES];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = endPath.CGPath;
tempView.layer.mask = maskLayer;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.delegate = self;
animation.fromValue = (__bridge id)(startPath.CGPath);
animation.toValue = (__bridge id)((endPath.CGPath));
animation.duration = [self transitionDuration:transitionContext];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[maskLayer addAnimation:animation forKey:@"PointNextPath"];
self.transitionContext = transitionContext;
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
UIView *containerView = [self.transitionContext containerView];
[containerView.subviews.lastObject removeFromSuperview];
[self.transitionContext completeTransition:YES];
}
- (void)dismissAnimation:
(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *tempView = [fromVC.view snapshotViewAfterScreenUpdates:YES];
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
[containerView addSubview:tempView];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGRect rect = CGRectMake(50, [UIScreen mainScreen].bounds.size.height - 50, 2, 2);
UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:rect];
UIBezierPath *startPath = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:sqrt(screenHeight * screenHeight + screenWidth * screenWidth) startAngle:0 endAngle:M_PI*2 clockwise:YES];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = endPath.CGPath;
tempView.layer.mask = maskLayer;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.delegate = self;
animation.fromValue = (__bridge id)(startPath.CGPath);
animation.toValue = (__bridge id)((endPath.CGPath));
animation.duration = [self transitionDuration:transitionContext];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[maskLayer addAnimation:animation forKey:@"PointNextPath"];
self.transitionContext = transitionContext;
}
@end
效果就是醬紫的:
2. 設(shè)置動畫
現(xiàn)在我們有了動畫鸡捐,要怎么設(shè)置給navigation或者present作為效果呢栈暇?先看navigation的~
2.1 UINavigationController 中 push 和 pop
可參考:https://blog.csdn.net/dolacmeng/article/details/51873395?utm_source=blogxgwz0
- 首先我們需要設(shè)置
navigationController
的delegate,這個delegate可以指定動畫是啥
@interface MainViewController ()<UINavigationControllerDelegate>
@end
@implementation MainViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.navigationController.delegate = self;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if (self.navigationController.delegate == self) {
self.navigationController.delegate = nil;
}
}
@end
- 指定動畫是啥的方式箍镜,這里指定為
[[TransAnimation alloc] init]
// MARK: - UINavigationControllerDelegate
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (fromVC == self && [toVC isKindOfClass:[MasViewController class]]) {
return [[TransAnimation alloc] init];
}
return nil;
}
注意哦源祈,這里其實(shí)可以通過
fromVC
和toVC
判斷是push還是pop哈
- 最后就是只要你讓
TransAnimation
實(shí)現(xiàn)了UIViewControllerAnimatedTransitioning
即可啦~
2.2 UITabBarController 中切換 Tab
可參考:https://blog.csdn.net/qq_25639809/article/details/61198894
實(shí)現(xiàn)UITabBarControllerDelegate
的animationControllerForTransitionFromViewController
把動畫返回回去就好啦~~
@interface DMMainViewController ()<UITabBarControllerDelegate>
@end
@implementation DMMainViewController
// 實(shí)現(xiàn)協(xié)議
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
return [[AnimationManager alloc] init];
}
@end
這里一樣的可以通過fromVC和toVC判斷方向~
2.3 presentViewController以及dismiss
可參考:http://www.reibang.com/p/15355cc8e133
和nav以及tab非常類似,也是通過delegate來設(shè)置動畫色迂,區(qū)別大概只是delegate里面會分是present還是dismiss香缺,不用你自己判斷。
例如你需要從A跳到B歇僧,那么你需要做這樣的事情:
// VC A
ViewControllerB * bVC = [[ViewControllerB alloc] init];
bVC.transitioningDelegate = self;
[self presentViewController: bVC animated:YES completion:nil];
注意這里把B的transitioningDelegate
設(shè)為了A了哦
這里A需要實(shí)現(xiàn)的delegate就是UIViewControllerTransitioningDelegate
:
//指定present動畫
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//指定dismiss動畫
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
//指定交互式present動畫的控制類
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
//指定交互式dismiss動畫的控制類
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;
所以這里舉例如果我們想讓A跳到B的時候有動畫只要醬紫就好啦:
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
return [[TransAnimation alloc] init];
}
注意如果是present的動畫图张,就在present的時候改delegate,然后實(shí)現(xiàn)animationControllerForPresentedController
诈悍,如果想改dismiss的動畫祸轮,就確認(rèn)當(dāng)前VC的delegate的dismiss是實(shí)現(xiàn)了的
例如當(dāng)前頁面顯示的時候?qū)elegate設(shè)為自己:
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
self.transitioningDelegate = self;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
return [[TransAnimation alloc] init];
}
2.4 UICollectionViewController 的布局轉(zhuǎn)場
這個我本來想找個OC的,結(jié)果真的木有侥钳。适袜。就還是swift的參考啦其實(shí)都差不多。舷夺。https://github.com/seedante/iOS-Note/wiki/View-Controller-Transition-PartIII#Chapter4
從iOS7開始苦酱,在collectionViewController中就伴隨著自定義轉(zhuǎn)場的功能產(chǎn)生了一個新的屬性:useLayoutToLayoutNavigationTransitions,這是一個BOOL值,如果設(shè)置該值為YES给猾,如果navigationController push或者pop 一個collectionViewController 到另一個collectionViewController的時候躏啰,其所在的navigationController就可以用collectionView的布局轉(zhuǎn)場動畫來替換標(biāo)準(zhǔn)的轉(zhuǎn)場,這點(diǎn)大家可以自行嘗試一下耙册,但是顯然给僵,這個屬性的致命的局限性就是你得必須滿足都是collectionViewController,對于collectionView就沒辦法了。
這個的效果其實(shí)就類似我們的照片app里面那種帝际,從一個collection到一個新的collection的平滑過渡蔓同,而且只要打開一個熟悉超厲害~
demo引用別人的哈,實(shí)在懶得寫了sorry:https://github.com/seedante/iOS-ViewController-Transition-Demo/tree/master/CollectionViewControllerLayoutTransition
(注意在這個demo有點(diǎn)問題蹲诀,沒有給collectionView register cell斑粱,需要改一下哈要不會在false的時候crash)
注意需要在pop或者push或者present或者dismiss之前設(shè)置delegate哦脯爪,否則是不能觸發(fā)animation噠
3. 可交互轉(zhuǎn)場動畫
什么叫可交互轉(zhuǎn)場動畫
呢则北?就是根據(jù)你的手指位置來控制轉(zhuǎn)場動畫進(jìn)度,類似醬紫:
所以要怎么實(shí)現(xiàn)呢痕慢?
① 首先得準(zhǔn)備一個animation尚揣,和Part1一樣,需要實(shí)現(xiàn)UIViewControllerAnimatedTransitioning
作為轉(zhuǎn)場動畫
// .h
@interface TransAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end
// .m
@interface TransAnimation ()
@end
@implementation TransAnimation
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
// 獲取fromVc和toVc
UIViewController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *fromView = [[UIView alloc] init];;
UIView *toView = [[UIView alloc] init];
if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
// fromVc 的view
fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
// toVc的view
toView = [transitionContext viewForKey:UITransitionContextToViewKey];
} else {
// fromVc 的view
fromView = fromVc.view;
// toVc的view
toView =toVc.view;
}
// 轉(zhuǎn)場環(huán)境
UIView *containView = [transitionContext containerView];
toView.alpha = 0;
[containView addSubview:fromView];
[containView addSubview:toView];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromView.alpha = 0;
toView.alpha = 1;
} completion:^(BOOL finished) {
// 由于是交互的掖举,所以completeTransition不一定會success
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
if ([transitionContext transitionWasCancelled]) {
//手勢取消了
}else{
//手勢成功
}
fromView.alpha = 1;
}];
}
- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
return 0.5;
}
@end
交互式轉(zhuǎn)場的原理就是快骗,更新轉(zhuǎn)場百分比(UIPercentDrivenInteractiveTransition
對象),會自動根據(jù)百分比算出animateTransition
里面的動畫進(jìn)行到什么狀態(tài)比如alpha塔次、frame之類的然后顯示出來方篮。
也就是交互式的轉(zhuǎn)場也是依賴這個animator,只不過附加的百分比計算動畫進(jìn)度并更新励负,不是讓動畫自己動藕溅。(自己動就是定時器模式啦)
這里例子里是一個漸變的animator,需要注意的點(diǎn)是之前非交互的animator是動畫做完轉(zhuǎn)場就一定完成了继榆,但是交互式是不一樣的巾表。交互式的松手的時候無論是成功轉(zhuǎn)場還是失敗,都會執(zhí)行動畫的completion
裕照,所以在里面需要判斷[transitionContext transitionWasCancelled]
是不是轉(zhuǎn)場取消了
一個疑惑點(diǎn)木有想明白,我們在
animateTransition
里面寫什么動畫或者不寫動畫都是未知的调塌,系統(tǒng)是怎么檢測到我們寫的動畫并在傳入百分比的時候按照百分比計算顯示的呢晋南?但畢竟正常做動畫也是系統(tǒng)算的,只是加一個百分比也很容易對于系統(tǒng)而言
② 設(shè)置interactionController
可交互轉(zhuǎn)場動畫依賴于一個百分比管理器羔砾,也就是interactionController负间,這個管理器需要實(shí)現(xiàn)UIViewControllerInteractiveTransitioning
協(xié)議,官方給我們提供了一個現(xiàn)成的UIPercentDrivenInteractiveTransition
類姜凄,你也可以繼承UIPercentDrivenInteractiveTransition
來使用政溃。
UIViewControllerInteractiveTransitioning
協(xié)議的功能主要是控制轉(zhuǎn)場動畫的狀態(tài),即動畫完成的百分比态秧,所以只有在轉(zhuǎn)場中才有用董虱。
比如我們通過[self.navigationController popViewControllerAnimated:YES]
觸發(fā)pop轉(zhuǎn)場動畫,然后在轉(zhuǎn)場動畫結(jié)束之前通過- (void)updateInteractiveTransition:(CGFloat)percentComplete
更改轉(zhuǎn)場動畫的完成的百分比,那么轉(zhuǎn)場動畫將由實(shí)現(xiàn)UIViewControllerInteractiveTransitioning
的類接管愤诱,而不是由定時器管理云头,之后就可以隨意設(shè)置動畫狀態(tài)了。
交互動畫往往配合手勢操作淫半,手勢操作產(chǎn)生一序列百分比數(shù)通過updateInteractiveTransition
方法實(shí)時更新轉(zhuǎn)場動畫狀態(tài)溃槐。
- 這一步要注意的是,對于navigation的push/pop或者是presentVC和dismiss科吭,設(shè)置interactionController的方式都是不一樣的哦昏滴!也要記得把
navigationController.delegate
設(shè)置上哦,可以在push的時候設(shè)置
舉個例子:
// dismiss
-(id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator;
// pop push
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController;
這些方法對應(yīng)不同的觸發(fā)條件对人,[self dismissViewControllerAnimated:YES completion:nil]
觸發(fā)的是上面的谣殊,[self.navigationController popViewControllerAnimated:YES];
觸發(fā)的是下面的~
這類方法返回的都是一個id<UIViewControllerInteractiveTransitioning>
對象,也就是我們的百分比控制器规伐。
例如:
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactiveTransition;
self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
// MARK: - UINavigationControllerDelegate
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
return self.interactiveTransition;
}
即使是可交互轉(zhuǎn)場動畫也是動畫蟹倾,需要在delegate返回轉(zhuǎn)場動畫的方法里面返回我們①里面創(chuàng)建的animator哦
例如navigation的:
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPop) {
return [[TransAnimation alloc] init];
}
return nil;
}
③ 添加手勢更新百分比
現(xiàn)在有了百分比控制器,那么怎么更新轉(zhuǎn)場進(jìn)行到了百分之幾呢猖闪,這就依賴于手勢啦~
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] init];
[pan addTarget:self action:@selector(panGestureRecognizerAction:)];
[self.view addGestureRecognizer:pan];
- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)pan{
//產(chǎn)生百分比
CGFloat process = [pan translationInView:self.view].x / ([UIScreen mainScreen].bounds.size.width);
process = MIN(1.0,(MAX(0.0, process)));
if (pan.state == UIGestureRecognizerStateBegan) {
self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
// 注意這里一定要用pop才能觸發(fā)鲜棠,因?yàn)閐elegate實(shí)現(xiàn)的是navigation的`interactionControllerForAnimationController`
[self.navigationController popViewControllerAnimated:YES];
}else if (pan.state == UIGestureRecognizerStateChanged){
[self.interactiveTransition updateInteractiveTransition:process];
}else if (pan.state == UIGestureRecognizerStateEnded
|| pan.state == UIGestureRecognizerStateCancelled){
if (process > 0.5) {
[ self.interactiveTransition finishInteractiveTransition];
}else{
[ self.interactiveTransition cancelInteractiveTransition];
}
self.interactiveTransition = nil;
}
}
在UIGestureRecognizerStateBegan
的時候需要觸發(fā)動畫,所以需要用popViewControllerAnimated
培慌,并創(chuàng)建UIPercentDrivenInteractiveTransition
然后在UIGestureRecognizerStateChanged
的時候更新當(dāng)前百分比控制器的百分比updateInteractiveTransition
在UIGestureRecognizerStateEnded
的時候選擇是finish還是cancelinteractiveTransition
豁陆,如果cancel的話就說明轉(zhuǎn)場失敗~ finish就是成功哦
到這里就可以實(shí)現(xiàn)漸變的交互轉(zhuǎn)場啦撒花花~~~
引用的別人的圖說明一下轉(zhuǎn)場的詢問順序:
也就是如果有自定義動畫并沒有交互動畫就會把動畫交給定時器,按照自定義動畫執(zhí)行~ 如果有就會按照百分比執(zhí)行吵护。
這里也說明了一定要先實(shí)現(xiàn)轉(zhuǎn)場animationControllerForOperation
的delegate才會有可交互的轉(zhuǎn)場一說盒音,畢竟可交互的轉(zhuǎn)場只是在原來轉(zhuǎn)場的基礎(chǔ)上增加了百分比控制器interactiveTransition
補(bǔ)充一個坑:https://stackoverflow.com/questions/25488267/custom-transition-animation-not-calling-vc-lifecycle-methods-on-dismiss
Reference:
- 超全的一個文~ 很喜歡的:http://www.reibang.com/p/ec08f43808aa
- http://www.reibang.com/p/a9b1307b305b
- http://www.reibang.com/p/cca1dcb79ddf
- http://www.reibang.com/p/29b0165de712?from=groupmessage
- 動畫合集:http://www.reibang.com/p/fd3154946919
- tableview push collectionViewController:http://www.reibang.com/p/c609ebc6a433