iOS自定義轉場動畫

自定義轉場動畫主用到3個類

  • 實現UIViewControllerAnimatedTransitioning協議的動畫類
  • 繼承UIPercentDrivenInteractiveTransition類的手勢類
  • 實現UIViewControllerContextTransitioning協議的transitionContext

先實現present和dismiss動畫

PresentedViewController *vc = [[PresentedViewController alloc] init];
//設置動畫的代理,vc需要實現UIViewControllerTransitioningDelegate協議
vc.transitioningDelegate = vc;
[self presentViewController:vc animated:YES completion:NULL];

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;

這裏主要介紹dismiss的動畫和手勢
我們建立了一個動畫類SunAimator,實現了UIViewControllerAnimatedTransitioning協議,下面是協議兩個方法的實現

- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    //動畫時間
    return 1.5;
}
- (void)dismissAnimatorTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    //將要消失的視圖
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    //將要顯示的視圖
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    //動畫過程的背景視圖
    UIView *containerView = [transitionContext containerView];
    //動畫時間
    NSTimeInterval duration = [self transitionDuration:transitionContext];

    //應爲要在fromView上做動畫,爲了一些不必要的麻煩這裏採用截圖處理
    UIView *tempView = [self snapWithView:fromView];
    //將參與動畫的視圖添加到動畫背景視圖上
    [containerView addSubview:toView];
    [containerView addSubview:tempView];

    //動畫的具體內容,也可自自己使用CABasicAnimation、CAKeyframeAnimation和CAKeyframeAnimation實現更復雜的動畫
    [UIView animateWithDuration:duration animations:^{
        CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.1, 0.1);
        tempView.transform = transform;
    } completion:^(BOOL finished) {

        //判斷手勢是否失敗
        if([transitionContext transitionWasCancelled]){
            //發送一個失敗的消息
            [transitionContext completeTransition:NO];
            //失敗的話需要將目標視圖移除,還要將原先的頁面顯示出來(顯示很重要)
            [toView removeFromSuperview];
            [containerView addSubview:fromView];
        }else{
            [transitionContext completeTransition:YES];
        }
        //臨時視圖不管動畫失敗還是成功都要移除
        [tempView removeFromSuperview];
    }];

}

下面是手勢類SunDrivenInteractive,手勢需要繼承UIPercentDrivenInteractiveTransition類

SunDrivenInteractive.h
@interface SunDrivenInteractive : UIPercentDrivenInteractiveTransition

@property (nonatomic,assign) BOOL isIneractive;
- (void)addGestureToViewCOntroller:(UIViewController *)fromVC;

@end
SunDrivenInteractive.m
#import "SunDrivenInteractive.h"

@interface SunDrivenInteractive ()

@property (nonatomic,weak) UIViewController *fromVC;
@property (nonatomic,assign) CGFloat percent;

@end
@implementation SunDrivenInteractive

- (void)addGestureToViewCOntroller:(UIViewController *)fromVC {

    UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveAction:)];
    [fromVC.view addGestureRecognizer:panGesture];
    _fromVC = fromVC;
}

- (void)moveAction:(UIPanGestureRecognizer *)panGesture {

    if(panGesture.state == UIGestureRecognizerStateBegan){

        _isIneractive = YES;
        [_fromVC dismissViewControllerAnimated:YES completion:NULL];
    }else if (panGesture.state == UIGestureRecognizerStateChanged){

        CGFloat translationX = [panGesture translationInView:_fromVC.view].x;
        if(translationX > 0){
            _percent = translationX/300.0;
            //更新進度
            [self updateInteractiveTransition:_percent];
        }
    }else if (panGesture.state == UIGestureRecognizerStateEnded){

        _isIneractive = NO;
        if(_percent > 0.5){
            [self finishInteractiveTransition];
        }else{
            [self cancelInteractiveTransition];
        }
    }
}

動畫類和手勢類都已經準備好了,接下里是PresentedViewController.m裏的實現

@interface PresentedViewController ()

@property (nonatomic,strong) SunDrivenInteractive *interactive;

@end

@implementation PresentedViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor yellowColor];

    _interactive = [[SunDrivenInteractive alloc] init];
    [_interactive addGestureToViewCOntroller:self];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    [self dismissViewControllerAnimated:YES completion:NULL];
}


#pragma mark - UIViewControllerTransitioningDelegate
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {

    //present動畫
    return [[SunAimator alloc] initWithAnimatorType:(SunAimatorTypePresent)];
}

- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {

    //dismiss動畫
    return [[SunAimator alloc] initWithAnimatorType:(SunAimatorTypeDismiss)];
}

- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator {

    //present手勢
    return nil;
}

- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator {

    //dismiss手勢
    return (_interactive.isIneractive?_interactive:nil);
}

簡單的轉場動畫就這樣實現了
Demo地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章