源碼下載:源碼
本篇文章是以簡書的自定義轉場為原型。先看一下簡書的自定義轉場動畫如下:
創(chuàng)建項目
項目中創(chuàng)建兩個繼承UIViewController的類蚤蔓。分別為ViewController和SecondViewController赋秀。
從上圖可以看出,是彈出的模態(tài)視圖。本文自定義present轉場的動畫隘梨。實際上就是對
presentViewController
和dismissViewControllerAnimated
的轉場動畫進行自定義蚀腿。
[self presentViewController:secondVC animated:YES completion:NULL];
和
[self dismissViewControllerAnimated:YES completion:NULL];
于是嘴瓤,要創(chuàng)建一個繼承于NSObject的CustomPresentAnimationCotroller自定義動畫控制器。
最終莉钙,項目目錄如下:
分析動畫
-
了解手機屏幕的坐標系廓脆。coordinate.jpeg
解釋旋轉方向:
x軸和y軸都是沿著手機屏幕面的垂直方向旋轉。但是不同的是:x軸是上下方向磁玉,y軸是左右方向停忿。
z軸沿著手機屏幕面平行方向旋轉。
分析present動畫蚊伞。
是由fromView
和toView
兩個視圖的動畫席赂。fromView
暫且說是下面的視圖,toView
暫且說是上面的視圖时迫。
fromView動畫:
先是沿x,y縮放颅停,透明度減少。然后沿x軸旋轉一定角度后向上平移掠拳,再縮放一定比例癞揉。
toView動畫:
從屏幕底部滑入。分析dismiss動畫
fromView
變成上面的視圖溺欧,toView
則是下面的視圖喊熟。
fromView動畫:
從屏幕底部滑出。
toView動畫:
先是沿x,y放大姐刁,透明度增大逊移。然后恢復到原始狀態(tài)。
定義轉場動畫
在iOS7后要遵循UIViewControllerTransitioningDelegate協(xié)議龙填。
在本篇文章中使用了以下協(xié)議方法:
//方法1
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//方法2
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
方法1 是在[self presentViewController:secondVC animated:YES completion:NULL];
后調用胳泉。
方法2是在[self dismissViewControllerAnimated:YES completion:NULL];
后調用拐叉。
在ViewController的按鈕點擊事件后賦予轉場代理持有者。轉場動畫使用我們自定義的動畫控制器CustomPresentAnimationCotroller
實現(xiàn)扇商。
-(void)presentNext:(UIButton *)sender{
SecondViewController *secondVC =[SecondViewController new];
//blow ios 7.0 can use UIModalPresentationCurrentContext
secondVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
secondVC.transitioningDelegate = self;
[self presentViewController:secondVC animated:YES completion:NULL];
}
#pragma mark -UIViewControllerTransitioningDelegate
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
CustomPresentAnimationCotroller *presentAnimation = [CustomPresentAnimationCotroller new];
presentAnimation.dismiss = NO;
return presentAnimation;
}
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
CustomPresentAnimationCotroller *presentAnimation = [CustomPresentAnimationCotroller new];
presentAnimation.dismiss = YES;
return presentAnimation;
}
在自定義的動畫控制器CustomPresentAnimationCotroller
必須要實現(xiàn)下面的兩個轉場動畫協(xié)議UIViewControllerAnimatedTransitioning
凤瘦。
//轉場動畫時間
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 1.0;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = toVC.view;
UIView *fromView = fromVC.view;
if(self.isDismissed){
[self RunDismissAnimation:transitionContext fromVC:fromVC toVC:toVC fromView:fromView toView:toView];
} else {
[self RunPresentAnimation:transitionContext fromVC:fromVC toVC:toVC fromView:fromView toView:toView];
}
}
最后,實現(xiàn)prensent與dismiss的自定義動畫轉場案铺。在下面代碼中小編寫有詳細的注解蔬芥。
present動畫:
-(void)RunPresentAnimation:(id<UIViewControllerContextTransitioning>)transitionContext fromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC fromView:(UIView *)fromView toView:(UIView *)toView {
/*
fromVC UINavigationController 的根視圖控制器---->ViewController
toVC --->SecondViewController
*/
UIView* containerView = [transitionContext containerView];
//獲取fromVC(ViewController)的frame
CGRect frame = [transitionContext initialFrameForViewController:fromVC];
//底部滑進 離屏滑入 即y坐標 從height --->0
CGRect offScreenFrame = frame;
offScreenFrame.origin.y = offScreenFrame.size.height;
toView.frame = offScreenFrame;
[containerView insertSubview:toView aboveSubview:fromView];
//三維變化
CATransform3D t1 = CATransform3DIdentity;
t1.m34 = 1.0/-1000;
//x y方向各縮放比例為0.95
t1 = CATransform3DScale(t1, 0.95, 0.95, 1);
//x方向旋轉15°
t1 = CATransform3DRotate(t1, 15.0f * M_PI/180.0f, 1, 0, 0);
CATransform3D t2 = CATransform3DIdentity;
t2.m34 = 1.0/-1000;
//沿Y方向向上移動
t2 = CATransform3DTranslate(t2, 0, -fromView.frame.size.height*0.08, 0);
//在x y方向各縮放比例為0.8
t2 = CATransform3DScale(t2, 0.8, 0.8, 1);
//UIView關鍵幀過渡動畫 總的持續(xù)時間:1.0
[UIView animateKeyframesWithDuration:1.0 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{
//開始時間:1.0*0.0 持續(xù)時間:1.0*0.4
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.4f animations:^{
//執(zhí)行t1動畫 縮放并旋轉角度
fromView.layer.transform = t1;
//fromView的透明度
fromView.alpha = 0.6;
}];
//開始時間:1.0*0.1 持續(xù)時間:1.0*0.5
[UIView addKeyframeWithRelativeStartTime:0.1f relativeDuration:0.5f animations:^{
//執(zhí)行t2動畫 向上平移和縮放
fromView.layer.transform = t2;
}];
//開始時間:1.0*0.0 持續(xù)時間:1.0*1.0
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:1.0f animations:^{
//toView向上滑入
toView.frame = frame;
}];
} completion:^(BOOL finished) {
//過渡動畫結束
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
dismiss動畫:
-(void)RunDismissAnimation:(id<UIViewControllerContextTransitioning>)transitionContext fromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC fromView:(UIView *)fromView toView:(UIView *)toView {
CGRect frame = [transitionContext initialFrameForViewController:fromVC];
toView.frame = frame;
CGRect frameOffScreen = frame;
frameOffScreen.origin.y = frame.size.height;
CATransform3D t1 = CATransform3DIdentity;
t1.m34 = 1.0/-1000;
t1 = CATransform3DScale(t1, 0.95, 0.95, 1);
t1 = CATransform3DRotate(t1, 15.0f * M_PI/180.0f, 1, 0, 0);
//關鍵幀過渡動畫
[UIView animateKeyframesWithDuration:1.0 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:1.0f animations:^{
//滑出屏幕
fromView.frame = frameOffScreen;
}];
[UIView addKeyframeWithRelativeStartTime:0.35f relativeDuration:0.35f animations:^{
//執(zhí)行t1,沿著x,y放大,沿x旋轉
toView.layer.transform = t1;
//透明度變?yōu)?.0
toView.alpha = 1.0;
}];
[UIView addKeyframeWithRelativeStartTime:0.75f relativeDuration:0.25f animations:^{
//還原3D狀態(tài)
toView.layer.transform = CATransform3DIdentity;
}];
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
最終效果圖:
本篇詳解到此結束控汉。如有疑問請留言給小編笔诵,小編給大家做解答。
問題1:自定義動畫轉場后姑子,調用dismissViewControllerAnimated乎婿,上一級的ViewController不會觸發(fā)viewWillAppear。
解決方法:
第一步在自定義轉場中實現(xiàn)改協(xié)議方法街佑。
-(void)animationEnded:(BOOL)transitionCompleted{
if (!transitionCompleted) {
_toVC.view.transform = CGAffineTransformIdentity;
}
}
第二步下面方法加入一行代碼:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
_toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = _toVC.view;
UIView *fromView = fromVC.view;
//toVC 調用viewWillAppear
[_toVC beginAppearanceTransition:YES animated:YES];
if(self.isDismissed){
[self RunDismissAnimation:transitionContext fromVC:fromVC toVC:_toVC fromView:fromView toView:toView];
} else {
[self RunPresentAnimation:transitionContext fromVC:fromVC toVC:_toVC fromView:fromView toView:toView];
}
// 調用此方法
// fromVC 調用viewDidDisappear
[fromVC beginAppearanceTransition:NO animated:YES];
點擊此處源碼下載:源碼
其實自定義動畫轉場并不是特別難谢翎,需要自己動手做一次。