iOS7之转场动画

iOS7添加了转场动画,让我们更加方便的切换控制器。

iOS7 SDK支持两种自定义过渡:自定义视图控制器过渡交互式视图控制器过渡

怎么来区分自定义视图控制器过渡和交互式试图控制器过渡呢?

当过渡是时间的函数式,它通常是一个自定义视图控制器过渡,如果他是一个手势识别器或者其他类似事件的函数,它通常是一个交互式视图控制器过渡

举个例子:
UINavigationController过渡使我们最常见的自定义视图控制器过渡。UIPageViewController过渡不是随着时间进行,它需要跟随我们的手指滑动,所以它是一种交互式视图控制器过渡。

0.自定义视图控制器过渡

0.使用Storyboard来实现自定义视图控制器过渡

我们可以使用连线(segue)的方式,来实现自定义视图控制器过渡,segue有好几种类型,
选择某个segue,我们可以看到下图:

segue1

我们选择自定义这种方式可以看到:

segue2

我们可以指定segue Class 它是一个UIStoryboardSegue子类,我们可以创建一个UIStoryboardSegue子类IYMCustomSegue,填写在这里。

在UIStoryboardSegue里有个perform方法,我们在IYMCustomSegue重写一下:

- (void) perfrom{
//这里实现动画
...
}

下面实现一个自定义push动画:

- (void) perform
{
    UIViewController *sourceViewController =              self.sourceViewController;
    UIViewController *desinationViewController =      self.destinationViewController;

    CGRect sourceRect = sourceViewController.view.frame;
    CGRect desinationViewFromRect =     CGRectMake(sourceRect.origin.x + sourceRect.size.width, sourceRect.origin.y, sourceRect.size.width, sourceRect.size.height);
     desinationViewController.view.frame = desinationViewFromRect;
     desinationViewController.view.alpha = 0;
     [sourceViewController.view.superview addSubview:desinationViewController.view];

     [UIView animateWithDuration:0.3 animations:^{
         sourceViewController.view.alpha = 0.5;
         desinationViewController.view.alpha = 1;
         desinationViewController.view.frame = sourceRect;
     }completion:^(BOOL finished) {
         sourceViewController.view.alpha = 1;
         [desinationViewController.view removeFromSuperview];

         [sourceViewController.navigationController  pushViewController:desinationViewController animated:NO];
    }];
}

1.代码方式实现

自定义试图控制器过渡是由两个协议实现的,即UIViewControllerTransitioningDelegate 和 UIViewControllerAnimatedTransitioning。

我们从A控制器到B控制器,通常有两种方式:一种是presentViewController方式,另一种是pushViewController。

0.使用presentViewController方式:从A控制器present到B控制器

设置B控制器的transitionDelegate为A控制器
在A控制器中实现UIViewControllerTransitioningDelegate协议
实现UIViewControllerAnimatedTransitioning

实现UIViewControllerTransitioningDelegate中的方法:

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;

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

这两个方法返回的是一个实现了UIViewControllerAnimatedTransitioning协议的类实例。通过方法名字我们就可以看到它们适用于presentViewController方式。如果自定义过渡效果只有一个视图控制器使用,视图控制器本身就可以实现UIViewControllerAnimatedTransitioning协议,否则,就需要一个单独的继承自NSObject一个单独的类来实现UIViewControllerAnimatedTransitioning协议。

这里我们就实现一个B视图控制器过渡动画,就直接在A控制器中实现UIViewControllerAnimatedTransitioning。

实现UIViewControllerTransitioningDelegate中的方法:

// This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to
// synchronize with the main animation. 
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext{
    //返回动画时间
    return 1.0f;
}

// This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
    //动画代码在这里写
    ...

    [transitionContext completeTransition:YES];//必须实现这个方法
}

``- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext```中的transitionContext,我们可以获得:

源控制器即来自哪个视图控制器:

UIViewController *src = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

目标控制器,这里是B控制器:

UIViewController *dest = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

容器视图:

UIView *containerView = [transitionContext containerView];

1.使用pushViewController方式:从A控制器push到B控制器

因为是从A控制器push到B控制器,我们不需要实现UIViewControllerTransitioningDelegate中的方法,我们需要:

0.在A控制器设置UINavigationDelegate 为 self
1.实现UINavigationDelegate
2.实现UIViewControllerAnimatedTransitioning协议

实现UINavigationDelegate中的方法:

- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC;

返回的是一个实现了UIViewControllerAnimatedTransitioning协议的类实例。这个和presentViewController方式一样。

1.交互式视图控制器过渡

要实现一个交互式视图控制器过渡,除了要是上面的动画外,还要告诉交互控制器动画完成了多少,我们只要知道完成了多少百分比,告诉系统,系统就直接给咱们处理了。

通过计算手势/动作事件或其他驱动过渡的事件,可以得出百分比。例如通过UIScreenEdgePanGestureRecognizer边缘返回手势,我们可以通过平移量来查看返回完成了多少。

还是要分成两种,一种是presentViewController方式,另一种是pushViewController。

0.使用presentViewController方式

除了上面presentViewController方式实现的协议方法外,我们还需要实现UIViewControllerTransitioningDelegate中的另外两个方法:

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;

交互控制器实现了UIViewControllerInteractiveTransitioning协议。交互式过渡的交互控制器都必须是UIPercentDrivenInteractiveTransition实例或者UIPercentDrivenInteractiveTransition子类实例。

UIPercentDrivenInteractiveTransition中以下方法可以完成交互过渡:

- (void)updateInteractiveTransition:(CGFloat)percentComplete;//完成了多少,percentComplete是百分比
- (void)cancelInteractiveTransition;//取消
- (void)finishInteractiveTransition;//完成

例如我们给B控制器(A控制器 present to B控制器),B控制器通过缩放到达A控制器,我们给B控制器视图添加了一个缩放手势UIPinchGestureRecognizer。

上面的UIViewControllerTransitioningDelegate我们返回一个 [[UIPercentDrivenInteractiveTransition alloc] init]实例percentDrivenInteractiveTransition。

- (void) pinchGestureAction:(UIPinchGestureRecognizer*) gestureRecognizer {
    CGFloat scale = gestureRecognizer.scale;
    if(gestureRecognizer.state == UIGestureRecognizerStateBegan) {
         self.percentDrivenInteractiveTransition =[[UIPercentDrivenInteractiveTransition alloc] init];
         self.startScale = scale;
         [self.controller dismissViewControllerAnimated:YES completion:nil];
     }
    if(gestureRecognizer.state == UIGestureRecognizerStateChanged) {
          CGFloat completePercent = 1.0 - (scale/self.startScale);
          [self updateInteractiveTransition:completePercent];//完成了多少百分比
    }
    if(gestureRecognizer.state == UIGestureRecognizerStateEnded) {
        if(gestureRecognizer.velocity >= 0){
            [self cancelInteractiveTransition];//取消
         }else{
             [self finishInteractiveTransition];//完成
         }

         self.percentDrivenInteractiveTransition = nil;
    }

     if(gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
        [self cancelInteractiveTransition];//取消
        self.percentDrivenInteractiveTransition = nil;
    }
}

1.使用pushViewController方式

和present方式不同的一点是,我们使用UINavigationDelegate中的:

- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController

来代替:

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;

- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;

其他的都一样。

2.实现一个完整的例子

单击图片展示大图,返回慢慢缩放到小图,效果如下:

效果

将用到:
0.UINavigationDelegate
1.返回手势
2.交互式过渡效果

0.新建两个控制器

首页:IYMFirstViewController
喵星人:IYMSecondViewController

1.使用Storyboard创建两个视图

Storyboard

0.设置UINavigationDelegate

IYMFirstViewController.m IYMSecondViewController.m分别重写以下方法:

- (void)viewDidAppear:(BOOL)animated {
     [super viewDidAppear:animated];

    // Set outself as the navigation controller's delegate so we're asked for a transitioning object
    self.navigationController.delegate = self;
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    // Stop being the navigation controller's delegate
    if (self.navigationController.delegate == self) {
        self.navigationController.delegate = nil;
    }
}

1.实现UINavigationDelegate

IYMFirstViewController.m中:

- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC{
    if (fromVC == self && [toVC isKindOfClass:[IYMSecondViewController class]]) {
        return self;
    }else {
        return nil;
    }
}

IYMSecondViewController.m

- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController{
    return _percentDrivenInteractiveTransition;
}

- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC{
    if (fromVC == self && [toVC isKindOfClass:[IYMFirstViewController class]]) {
        return self;
    }else {
        return nil;
    }
}

其中_percentDrivenInteractiveTransition是一个UIPercentDrivenInteractiveTransition实例,在边缘手势中实例化。

2.实现UIViewControllerAnimatedTransitioning

IYMFirstViewController.m

- (NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.3f;//动画时间
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    IYMFirstViewController *fromViewContrroller = (IYMFirstViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    IYMSecondViewController *toViewController = (IYMSecondViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *containerView = [transitionContext containerView];

    UIView *snapshotView = [fromViewContrroller.bottomImageView snapshotViewAfterScreenUpdates:NO];//截图
    CGRect snapshotViewFromRect = [containerView convertRect:fromViewContrroller.bottomImageView.frame fromView:fromViewContrroller.view];//获取在ContainerView中的frame
    snapshotView.frame = snapshotViewFromRect; 
    toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
    toViewController.view.alpha = 0;
    [containerView addSubview:toViewController.view];
    [containerView addSubview:snapshotView];
    fromViewContrroller.bottomImageView.hidden = YES;
    toViewController.topImageView.hidden = YES;

    CGRect snapshotViewToRect = [containerView convertRect:toViewController.topImageView.frame fromView:toViewController.view];
    [UIView animateWithDuration:0.3 animations:^{
        toViewController.view.alpha = 1;
        snapshotView.frame = snapshotViewToRect;
    } completion:^(BOOL finished) {
        if (finished) {
            [snapshotView removeFromSuperview];
            fromViewContrroller.bottomImageView.hidden = NO;
            toViewController.topImageView.hidden = NO;

            [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
        }
    }];
}

IYMSecondViewController.m

- (NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.3f;
}


- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
    IYMSecondViewController *fromViewController = (IYMSecondViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    IYMFirstViewController *toViewController = (IYMFirstViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *containerView = [transitionContext containerView];

    UIView *snapshotView = [fromViewController.topImageView snapshotViewAfterScreenUpdates:NO];
    CGRect snapshotViewFromRect = [containerView convertRect:fromViewController.topImageView.frame fromView:fromViewController.view];
    snapshotView.frame = snapshotViewFromRect; // Setup the initial view states
    toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
    toViewController.view.alpha = 0;
    [containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
    [containerView addSubview:snapshotView];
    fromViewController.topImageView.hidden = YES;
    toViewController.bottomImageView.hidden = YES;

    CGRect snapshotViewToRect = [containerView convertRect:toViewController.bottomImageView.frame fromView:toViewController.view];
    [UIView animateWithDuration:0.3 animations:^{
        toViewController.view.alpha = 1;
        snapshotView.frame = snapshotViewToRect;
    } completion:^(BOOL finished) {
        if (finished) {
            [snapshotView removeFromSuperview];
            fromViewController.topImageView.hidden = NO;
            toViewController.bottomImageView.hidden = NO;
        
            [transitionContext   completeTransition:!transitionContext.transitionWasCancelled];
        }
    }];
}

3.为IYMSecondViewController添加边缘返回手势

UIScreenEdgePanGestureRecognizer *screenEdgePanGestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleScreenEdgePanGesture:)];
screenEdgePanGestureRecognizer.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:screenEdgePanGestureRecognizer];
- (void) handleScreenEdgePanGesture:(UIScreenEdgePanGestureRecognizer *) recognizer
{
    CGFloat progress = [recognizer translationInView:self.view].x / (self.view.bounds.size.width * 1.0);
    progress = MIN(1.0, MAX(0.0, progress));

    if (recognizer.state == UIGestureRecognizerStateBegan) {
        _percentDrivenInteractiveTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
       [self.navigationController popViewControllerAnimated:YES];
    }else if (recognizer.state == UIGestureRecognizerStateChanged) {
        [_percentDrivenInteractiveTransition updateInteractiveTransition:progress];
    }else if (recognizer.state == UIGestureRecognizerStateEnded ||     recognizer.state == UIGestureRecognizerStateCancelled) {
        if (progress > 0.5f) {
            [_percentDrivenInteractiveTransition finishInteractiveTransition];
        }else{
            [_percentDrivenInteractiveTransition cancelInteractiveTransition];
        }

        _percentDrivenInteractiveTransition = nil;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容