(http://blog.devtang.com/2016/03/13/iOS-transition-guide/)之前寫過一篇core Animation的文章里面提到,核心動畫里面有一個很重要的部分就是轉(zhuǎn)場動畫,而轉(zhuǎn)場動畫有包括三個部分:
1.模態(tài)方式的轉(zhuǎn)場
2.導(dǎo)航欄式的轉(zhuǎn)場(push pop)
3.手勢驅(qū)動轉(zhuǎn)場
4.tabber轉(zhuǎn)場動畫
一. 模態(tài)方式的轉(zhuǎn)場
默認(rèn)的模態(tài)專場方式是:頁面從底部彈出來.那么要實現(xiàn)其他的方式應(yīng)該怎么做呢
首先要重寫專長的代理方法
創(chuàng)建一個類 繼承與nsobject 并且準(zhǔn)守代理方法UIViewControllerAnimatedTransitioning
//.h文件的內(nèi)容
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface Transtion : NSObject<UIViewControllerAnimatedTransitioning>
@property (assign, nonatomic) BOOL reverse;//用來標(biāo)記是跳轉(zhuǎn)還是返回
- (instancetype)init;//初始話方法
@end
//.m文件中的部分代碼
#import "Transtion.h"
@interface Transtion()
@property (assign, nonatomic) id context;
@property (strong, nonatomic) UIView* containerView;
@end
@implementation Transtion
-(instancetype)init{
if (self = [super init]) {
}
return self;
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
return 5;//過渡時間代理方法
}
//.m文件中重要的重寫代理方法
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
_context = transitionContext;
self.containerView = [transitionContext containerView];
if (!self.reverse) {
NSLog(@"跳轉(zhuǎn)");
UIViewController* toVC = [_context viewControllerForKey:UITransitionContextToViewControllerKey];//獲取將要得到的第二個界面
CGSize screenSize = [UIScreen mainScreen].bounds.size;
toVC.view.frame = CGRectMake(0, 0, screenSize.width, screenSize.height);//secondVC 給頁面賦值
[UIView animateWithDuration:2 animations:^{
[self.containerView.layer setTransform:[self firstTransform:M_PI/2.0]];
//self.containerView 是轉(zhuǎn)場的過渡視圖容器, 將他旋轉(zhuǎn)90度
} completion:^(BOOL finished) {
[self.containerView addSubview:toVC.view];//旋轉(zhuǎn)90度以后 將即將出現(xiàn)個的控制器的view添加
[UIView animateWithDuration:3 animations:^{
[self.containerView.layer setTransform:[self firstTransform:M_PI*0]];//繼續(xù)旋轉(zhuǎn)(歸位也可以)
} completion:^(BOOL finished) {
[_context completeTransition:YES];
//結(jié)束轉(zhuǎn)場時間
}];
}];
}else{
NSLog(@"返回");//如果是返回的話就走這個界面 這應(yīng)該就不用解釋啦把
UIViewController* toVC = [_context viewControllerForKey:UITransitionContextToViewControllerKey];
CGSize screenSize = [UIScreen mainScreen].bounds.size;
toVC.view.frame = CGRectMake(0, 0, screenSize.width, screenSize.height);
[UIView animateWithDuration:2 animations:^{
[self.containerView.layer setTransform:[self firstTransform:M_PI*1.5]];
} completion:^(BOOL finished) {
[self.containerView addSubview:toVC.view];
[UIView animateWithDuration:3 animations:^{
[self.containerView.layer setTransform:[self firstTransform:M_PI*0]];
}completion:^(BOOL finished) {
[_context completeTransition:YES];
}];
}];
}
}
控制器中的使用
首先遵守該協(xié)議<UIViewControllerTransitioningDelegate>
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
secondViewController * second = [[secondViewController alloc]init];
second.transitioningDelegate =self;//建立代理鏈接
[self presentViewController:second animated:YES completion:nil];
// [self.navigationController pushViewController:second animated:YES];
}
- (Transtion*)trastion{
if (!_trastion) {
_trastion = [[Transtion alloc]init];
}
return _trastion;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
self.trastion.reverse = YES;//返回
return self.trastion;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
self.trastion.reverse = NO;//跳轉(zhuǎn)
return self.trastion;
}
好了 模態(tài)的就搞定啦
二,導(dǎo)航欄(push pop)
這個很見到其實和模態(tài)差不多
Transtion.h 文件里面的東西不用該
我們要修改的是控制器里面的
準(zhǔn)守協(xié)議
@interface ViewController ()<UIViewControllerTransitioningDelegate,UINavigationControllerDelegate>
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.delegate =self;//建立代理連接很重要哦
self.view.backgroundColor = [UIColor greenColor];
self.navigationItem.title = @"第一頁";
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
secondViewController * second = [[secondViewController alloc]init];
second.transitioningDelegate =self;
//[self presentViewController:second animated:YES completion:nil];
[self.navigationController pushViewController:second animated:YES];
}
- (Transtion*)trastion{
if (!_trastion) {
_trastion = [[Transtion alloc]init];
}
return _trastion;
}
//push 用到的代理方法啊
-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
NSLog(@"push代理");
if (operation == UINavigationControllerOperationPush) {
self.trastion.reverse = NO;
return self.trastion;
}else{
self.trastion.reverse = YES;
return self.trastion;
}
}
ok push 搞定
補充: 我們有時后或做一些復(fù)雜的轉(zhuǎn)場動畫這時候會用到連個常用的工具函數(shù)
//.m文件的全部內(nèi)容
- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates NS_AVAILABLE_IOS(7_0);//對某個view進(jìn)行截圖
- (UIView *)resizableSnapshotViewFromRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates withCapInsets:(UIEdgeInsets)capInsets NS_AVAILABLE_IOS(7_0); //對某個view的某個部位進(jìn)行截圖
場景模擬:我們可以在點擊轉(zhuǎn)場以后 對第一個頁面的某一個部分截圖 然后添加到過渡視圖容器上讓著個截圖在轉(zhuǎn)場的整個過程中都可以看到
三. 手勢驅(qū)動
等待更新吧 因為建鍋還沒來得及搞~~~~~~~~~ 等我后續(xù)
接下來我們來搞手勢驅(qū)動(手勢驅(qū)動個人遇到了一些問題可困惑一會可以分享)
首先手勢驅(qū)動需要重寫另一個代理方法:創(chuàng)建類繼承與UIPercentDrivenInteractiveTransition
//.h文件
#import <UIKit/UIKit.h>
@interface MyPercentDrivenInteractiveTransition : UIPercentDrivenInteractiveTransition
@property (nonatomic, assign) BOOL interactionInProgress;
- (void)writeFromoViewController:(UIViewController*)fromVC AndToViewController:(UIViewController*)toVC;//一個添加手勢的方法
@end
接下來就是識別手勢并且更具滑動的時百分比驅(qū)動頁面的轉(zhuǎn)動
#import "MyPercentDrivenInteractiveTransition.h"
@interface MyPercentDrivenInteractiveTransition(){
BOOL _shouldCompleteTransition;
}
@property(nonatomic,strong)UIViewController * FromVC;
@property(nonatomic,strong)UIViewController * ToVC;
@end
@implementation MyPercentDrivenInteractiveTransition
static float fraction = 0;
- (void)writeFromoViewController:(UIViewController*)fromVC AndToViewController:(UIViewController*)toVC{
self.FromVC = fromVC;
self.ToVC = toVC;
[self addGestureRecognizerInFromView:toVC.view];
}
- (void)addGestureRecognizerInFromView:(UIView*)fromView{
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(geatureRecognizer:)];
[fromView addGestureRecognizer:pan];
}
-(CGFloat)completionSpeed
{
return 1-self.percentComplete ;
}
- (void)geatureRecognizer:(UIPanGestureRecognizer*)GestureRecognizer{
CGPoint translation = [GestureRecognizer translationInView:GestureRecognizer.view.superview];
NSLog(@"==== %f",translation.x);
switch (GestureRecognizer.state) {
case UIGestureRecognizerStateBegan:
self.interactionInProgress = YES;
[self.ToVC.navigationController popViewControllerAnimated:YES];
break;
case UIGestureRecognizerStateChanged:{
fraction = -(translation.x / 200);//200是我自己給的 可以給平款用來計算百分比
fraction = fmin(fmaxf(fraction, 0.0), 1.0);
NSLog(@"fraction === %f",translation.x);
_shouldCompleteTransition = fraction > 0.5;
// _shouldCompleteTransition = 1;
[self updateInteractiveTransition:fraction];
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
/**
* 需要判斷是結(jié)束了還是取消了
*/
self.interactionInProgress = NO;
if (!_shouldCompleteTransition || GestureRecognizer.state == UIGestureRecognizerStateCancelled)
{
//只要有一個不成立 那么就是取消了
[self cancelInteractiveTransition];//取消
}
else
{
[self finishInteractiveTransition];//結(jié)束
}
}
default:
break;
}
}
@end
控制器里面要初始化這個類
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPush) {
self.trastionAnimstion.pushOrPop = YES;
[_myPercentDrivenInteractiveTransition writeFromoViewController:fromVC AndToViewController:toVC];//push時用的不是手勢是按鈕 這時候給toview 添加手勢
return self.trastionAnimstion;
}else{
self.trastionAnimstion.pushOrPop = NO;
return self.trastionAnimstion;
}
}
//這是交互的代理實現(xiàn)
-(id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
return _myPercentDrivenInteractiveTransition.interactionInProgress ? _myPercentDrivenInteractiveTransition : nil;
}
到離這里按說改完美結(jié)束了 但是結(jié)果總是出人意外:在實現(xiàn)動畫是
我第一次用了前面的實現(xiàn)方式
CGSize screenSize = [UIScreen mainScreen].bounds.size;
_toVC.view.frame = CGRectMake(0, 0, screenSize.width, screenSize.height);
[self.ContainerView addSubview:self.toVC.view];
self.toVC.view.alpha = 0;
[UIView animateWithDuration:TrasitonTime/2.0 animations:^{
[self.ContainerView.layer setTransform:[self trasfrom:M_PI_2 * directFlage]];
} completion:^(BOOL finished) {
self.toVC.view.alpha = 1;
[UIView animateWithDuration:TrasitonTime/2.0 animations:^{
[self.ContainerView.layer setTransform:[self trasfrom:0]];
} completion:^(BOOL finished) {
[TransitionContext completeTransition:![TransitionContext transitionWasCancelled]];
}];
}];
//沒看錯這就是我前面實現(xiàn)
沒看錯這就是我前面實現(xiàn)旋轉(zhuǎn)時 用的方法 但是手勢驅(qū)動時不好用
在執(zhí)行下面這一段時 translation.x 打印出來為 NAN(意思就是不是一個數(shù)字),這里我搞了好久然后看了一些成功案例 進(jìn)行了模仿
case UIGestureRecognizerStateChanged:{
fraction = -(translation.x / 200);
fraction = fmin(fmaxf(fraction, 0.0), 1.0);
NSLog(@"fraction === %f",translation.x);
_shouldCompleteTransition = fraction > 0.5;
// _shouldCompleteTransition = 1;
[self updateInteractiveTransition:fraction];
}
break;
采用了第二種方法(但是依然有問題)
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
self.fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
self.toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
self.ContainerView = [transitionContext containerView];
TrasitonTime = [self transitionDuration:transitionContext];
TransitionContext = transitionContext;
self.fromVC.view.frame = [UIScreen mainScreen].bounds;
self.toVC.view.frame = [UIScreen mainScreen].bounds;
[self.ContainerView addSubview:self.toVC.view];
if (self.pushOrPop == YES) {
[self pushOrPresent];
}else{
[self popOrDismiss];
}
}
- (void)pushOrPresent{
// [_ContainerView addSubview:_toVC.view];
// _toVC.view.alpha = 0;
[self animationTrasFromDirect:-1.0];
}
- (void)popOrDismiss{
[self animationTrasFromDirect:1.0];
}
- (void)animationTrasFromDirect:(CGFloat)directFlage{
//這一段如果把時間改長也會出現(xiàn)一系列問題
[self.toVC.view.layer setTransform:[self trasfrom: - M_PI_2 * directFlage ]];
// _toVC.view.layer.transform = [self yRotation:directFlage * -M_PI_2];
[UIView animateKeyframesWithDuration:TrasitonTime delay:0 options:0 animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:TrasitonTime/2.0 animations:^{
[self.fromVC.view.layer setTransform:[self trasfrom:M_PI_2 * directFlage]];
}];
[UIView addKeyframeWithRelativeStartTime:TrasitonTime/2.0 relativeDuration:TrasitonTime/2.0 animations:^{
[self.toVC.view.layer setTransform:CATransform3DIdentity];
}];
} completion:^(BOOL finished) {
[TransitionContext completeTransition:![TransitionContext transitionWasCancelled]];
}];
}
這個方法呢解決了手勢驅(qū)動問題 但是又有新麻煩:當(dāng)設(shè)置轉(zhuǎn)場時間1.0s是沒問題效果如下:
但是如果改成3.0s 就有問題啦(當(dāng)頁面旋轉(zhuǎn)到一半時,切換第二個頁面時,沒有了過渡的動畫效果) 效果如圖:
當(dāng)然在最后也找了一個完全沒問題的而寫法(這個寫法是我借鑒網(wǎng)文寫的但是我感覺和我的第二種方法差不多 但是效果就是沒問題)
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
// zhe一段是完全沒問題的
UIView * containerView = [transitionContext containerView];
UIViewController * fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController * toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView * fromView = fromViewController.view;
UIView * toView = toViewController.view;
[containerView addSubview:toView];
// CATransform3D transform = CATransform3DIdentity;
//
// transform.m34 = -0.0001;
//
// [containerView.layer setSublayerTransform:transform];
// CGRect initialFrame = [transitionContext initialFrameForViewController:fromViewController];
//
//
// //設(shè)置視圖大小
// toView.frame = initialFrame;
// fromView.frame = initialFrame;
//是否是反面
float factor = self.pushOrPop ? -1.0 : 1.0;
//push操作 y軸方向順時針旋轉(zhuǎn)90度 這樣第一時間是看不到toView的
toView.layer.transform = [self yRotation:factor * -M_PI_2];
//animate
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateKeyframesWithDuration:duration delay:0 options:0 animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
//fromView-----
// fromView.layer.transform = [self yRotation:factor * M_PI_2];
[fromView.layer setTransform:[self trasfrom: factor * M_PI_2]];
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
[toView.layer setTransform: [self trasfrom: 0]];
//toVIew復(fù)原 在先前順時針的基礎(chǔ)上 逆時針旋轉(zhuǎn)90度 復(fù)原
//toView.layer.transform = [self yRotation:0.0];
}];
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
效果我就不上了但是保證效果嘎嘎好
在這我想知道 各位道友知道 問題出在哪里 (我實在是找不到了這一篇斷斷續(xù)續(xù)寫了好幾天)代碼地址:https://git.oschina.net/GAOZEJIAN/transitionAnimation.git
四 , tabber轉(zhuǎn)場動畫
好吧到這里我不想寫啦 我感覺用上面的思路就可以解決 如果還是不會自行百度學(xué)習(xí)