[iOS] 轉(zhuǎn)場動畫

這個topic其實(shí)起源于看到了我們別的組的一個小效果句惯,就是首頁有一個小標(biāo)簽秕衙,標(biāo)簽點(diǎn)開會以圓形擴(kuò)散的效果打開一個付費(fèi)頁~ 類似醬紫:

轉(zhuǎn)場動畫.gif (圖片借鑒View-Controller-Transition-PartIII里的)

頁面的切換效果搀玖,其實(shí)就是轉(zhuǎn)場動畫藏斩。

官方支持以下幾種方式的自定義轉(zhuǎn)場:

  1. UINavigationController 中 push 和 pop
  2. UITabBarController 中切換 Tab
  3. presentViewController以及dismiss(modal模態(tài)轉(zhuǎn)場)
  4. 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

效果就是醬紫的:


圓形擴(kuò)散收縮

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í)可以通過fromVCtoVC判斷是push還是pop哈

  • 最后就是只要你讓TransAnimation實(shí)現(xiàn)了UIViewControllerAnimatedTransitioning即可啦~

2.2 UITabBarController 中切換 Tab

可參考:https://blog.csdn.net/qq_25639809/article/details/61198894

實(shí)現(xiàn)UITabBarControllerDelegateanimationControllerForTransitionFromViewController把動畫返回回去就好啦~~

@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)

左側(cè)是useLayoutToLayoutNavigationTransitions為true,右側(cè)是false.gif

注意需要在pop或者push或者present或者dismiss之前設(shè)置delegate哦脯爪,否則是不能觸發(fā)animation噠


3. 可交互轉(zhuǎn)場動畫

什么叫可交互轉(zhuǎn)場動畫呢则北?就是根據(jù)你的手指位置來控制轉(zhuǎn)場動畫進(jìn)度,類似醬紫:

可交互轉(zhuǎ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)場的詢問順序:

pop push的時候的動畫詢問流程

也就是如果有自定義動畫并沒有交互動畫就會把動畫交給定時器,按照自定義動畫執(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:

  1. 超全的一個文~ 很喜歡的:http://www.reibang.com/p/ec08f43808aa
  2. http://www.reibang.com/p/a9b1307b305b
  3. http://www.reibang.com/p/cca1dcb79ddf
  4. http://www.reibang.com/p/29b0165de712?from=groupmessage
  5. 動畫合集:http://www.reibang.com/p/fd3154946919
  6. tableview push collectionViewController:http://www.reibang.com/p/c609ebc6a433
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市馅而,隨后出現(xiàn)的幾起案子祥诽,更是在濱河造成了極大的恐慌,老刑警劉巖瓮恭,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雄坪,死亡現(xiàn)場離奇詭異,居然都是意外死亡屯蹦,警方通過查閱死者的電腦和手機(jī)维哈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來登澜,“玉大人阔挠,你說我怎么就攤上這事∧匀洌” “怎么了购撼?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我份招,道長切揭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任锁摔,我火速辦了婚禮廓旬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谐腰。我一直安慰自己孕豹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布十气。 她就那樣靜靜地躺著励背,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砸西。 梳的紋絲不亂的頭發(fā)上叶眉,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機(jī)與錄音芹枷,去河邊找鬼衅疙。 笑死,一個胖子當(dāng)著我的面吹牛鸳慈,可吹牛的內(nèi)容都是我干的饱溢。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼走芋,長吁一口氣:“原來是場噩夢啊……” “哼绩郎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起翁逞,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤肋杖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后挖函,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體状植,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年挪圾,在試婚紗的時候發(fā)現(xiàn)自己被綠了浅萧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逐沙。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡哲思,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吩案,到底是詐尸還是另有隱情棚赔,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站靠益,受9級特大地震影響丧肴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胧后,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一芋浮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧壳快,春花似錦纸巷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至竖伯,卻和暖如春存哲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背七婴。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工祟偷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人本姥。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓肩袍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親婚惫。 傳聞我的和親對象是個殘疾皇子氛赐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 直接看轉(zhuǎn)場動畫的協(xié)議時有點(diǎn)迷糊,做了一個簡單的demo記錄下理解過程 一先舷、如何更改動畫方式 站在自己的角度艰管,最簡單...
    10m每秒滑行閱讀 338評論 0 0
  • 轉(zhuǎn)場動畫,就是Vc切換過程中的過渡動畫蒋川。官方支持以下幾種方式的自定義轉(zhuǎn)場:1牲芋、我們最常見的在 UINavigati...
    炎成閱讀 13,024評論 0 33
  • 首先看一個簡單的 效果圖模擬器不知道怎么了 變得這么慢,就將就著看吧 關(guān)于轉(zhuǎn)場動畫我們主要考慮兩個捺球,一個是導(dǎo)航條中...
    LeeDev閱讀 1,468評論 1 5
  • 轉(zhuǎn)場動畫學(xué)習(xí)中...TransitionDemo代碼 實(shí)現(xiàn)自定義的轉(zhuǎn)場動畫(只涉及自定義動畫缸浦,不管手勢驅(qū)動) 涉及...
    YaoYaoX閱讀 685評論 0 3
  • 概述 這篇文章泣栈,我將講述幾種轉(zhuǎn)場動畫的自定義方式卜高,并且每種方式附上一個示例弥姻,畢竟代碼才是我們的語言,這樣比較容易上...
    伯恩的遺產(chǎn)閱讀 53,792評論 37 379