自定義Pop轉場動畫
繼續使用上個程序,把push改為pop只需要做很少的工作就能完成
1、復制PushTransition.h和PushTransition.m。命名為PopTransition.h和PopTransition.m
2、在PopTransition.m中把
ViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
SecondViewController * toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
改為:
SecondViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ViewController * toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
兩個類名交換位置即可
3、把所有的avatarImageView改為sourceImageView,把所有的sourceImageView改為avatarImageView,如下:
// 轉場動畫的具體內容
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
// 獲取動畫的源控制器和目標控制器
SecondViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ViewController * toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView * container = transitionContext.containerView;
// 創建一個imageView的截圖,并把原本imageView隱藏,造成以為移動的就是imageView的假象
UIView * snapshotView = [fromVC.avatarImageView snapshotViewAfterScreenUpdates:NO];
snapshotView.frame = [container convertRect:fromVC.avatarImageView.frame fromView:fromVC.view];
fromVC.avatarImageView.hidden = YES;
// 設置目標控制器的位置,并把透明度設為0,在后面的動畫中慢慢顯示出來變為1
toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
toVC.sourceImageView.hidden = YES;
// 都添加到container中。注意順序
// [container addSubview:toVC.view];
[container insertSubview:toVC.view belowSubview:fromVC.view];
[container addSubview:snapshotView];
// 執行動畫
[UIView animateKeyframesWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
snapshotView.frame = toVC.sourceImageView.frame;
fromVC.view.alpha = 0;
} completion:^(BOOL finished) {
toVC.sourceImageView.hidden = NO;
[snapshotView removeFromSuperview];
fromVC.avatarImageView.hidden = NO;
//一定要記得動畫完成后執行此方法,讓系統管理 navigation
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]]; // 如果參數寫成yes,當用戶取消pop時,會繼續執行動畫,也就是讓detailVC消失,設置成這個參數,會避免這樣的錯誤
}];
}
4、修改ViewController中的判斷動畫類型的方法,加入pop的判斷:
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPush){ // 就是在這里判斷是哪種動畫類型
return [[PushTransition alloc] init]; // 返回push動畫的類
}else if (operation == UINavigationControllerOperationPop){ // 就是在這里判斷是哪種動畫類型
return [[PopTransition alloc] init]; // 返回pop動畫的類{
}else{
return nil;
}
}
系統默認的 Push 和 Pop 動畫都支持手勢驅動,并且可以根據手勢移動距離改變動畫完成度。幸運的是,Cocoa 已經集成了相關方法,我們只用告訴它百分比就可以了。所以下一步就是手勢驅動。
1、在 SecondViewController 的 viewDidLoad() 方法中,加入滑動手勢。
// 加入左側邊界手勢
UIScreenEdgePanGestureRecognizer * edgePan = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgePanGesture:)];
edgePan.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:edgePan];
2、遵循UINavigationControllerDelegate協議,因為navigationController的動畫需要在這里執行,所以需要設置代理為自己
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
self.navigationController.delegate = self;
}
3、在手勢監聽方法中,創建 UIPercentDrivenInteractiveTransition 屬性,并實現手勢百分比更新。
// 在手勢監聽方法中,創建UIPercentDrivenInteractiveTransition屬性,并實現手勢百分比更新
- (void)edgePanGesture:(UIScreenEdgePanGestureRecognizer *)edgePan{
// 進度值,這是左側邊界的算法,如果要改為右側邊界,改為self.view.bounds.size.width / [edgePan translationInView:self.view].x;
CGFloat progress = [edgePan translationInView:self.view].x / self.view.bounds.size.width;
if (edgePan.state == UIGestureRecognizerStateBegan) {
self.percentDrivenTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
[self.navigationController popViewControllerAnimated:YES];
}else if(edgePan.state == UIGestureRecognizerStateChanged){
[self.percentDrivenTransition updateInteractiveTransition:progress];
}else if(edgePan.state == UIGestureRecognizerStateCancelled || edgePan.state == UIGestureRecognizerStateEnded){
if(progress > 0.5){
[self.percentDrivenTransition finishInteractiveTransition];
}else{
[self.percentDrivenTransition cancelInteractiveTransition];
}
self.percentDrivenTransition = nil;
}
}
4、實現返回 UIViewControllerInteractiveTransitioning 的方法并返回剛剛創建的 UIPercentDrivenInteractiveTransition屬性。
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController{
if([animationController isKindOfClass:[PopTransition class]]){
return self.percentDrivenTransition;
}else{
return nil;
}
}
5、還需要設置一下返回動畫,否則手勢驅動不會生效
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPop){
return [[PopTransition alloc] init]; // 返回pop動畫的類
}else{
return nil;
}
}
完成