iOS7自定义视图控制器过渡2-手势交互过渡

24 篇文章 0 订阅

前言:今天会在上一篇的例子的基础上扩充手势交互过渡。手势交互过渡和事件过渡的实现方法差别不大,只需在代理方法中返回一个手势交互过渡动画对象,主要差别就是这个动画对象的实现上,这个动画对象遵守的是UIViewControllerInteractiveTransitioning协议而不再是 UIViewControllerAnimatedTransitioning。原因在于事件过渡的过程是时间的函数,我们只要告诉系统动画时间,系统就会根据这个时间等分过程将画面渲染。但是手势过渡的过程是手势完成度的函数,那么我们就需要向系统汇报这个手势完成度,如何汇报等会再说。


正文:最终效果如下




1. 实现transitionDelegate的代理方法

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

它返回呈现试图控制器时的动画对象,在[self presentViewController:redVC animated:YEScompletion:nil];之后被调用

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

它返回解散视图控制器时的动画对象,在[ self dismissViewControllerAnimated: YES completion: nil];之后被调用

- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator
{
    return self.dimissInteractiveTranstion.interacting ? self.dimissInteractiveTranstion : nil;
}
由于我只需要在解散redViewController时需要过渡动画,所以只需要实现这一个方法。


2. 手势交互动画对象实现

前言中提到手势交互动画对象要向系统汇报手势完成百分比。SDK中提供了UIPercentDrivenInteractiveTransition这个类,它是遵守UIViewControllerInteractiveTransitioning协议帮助我们完成相应的工作。这个类提供了这三个方法:

- (void)updateInteractiveTransition:(CGFloat)percentComplete;

- (void)cancelInteractiveTransition;

- (void)finishInteractiveTransition;

我们的手势交互动画对象只需继承这个类,并且在手势变化时调用第一个方法,在手势取消或未完成时调用第二个方法,在手势完成时调用第三个方法就可以了。

@interface ViewController ()<UIViewControllerTransitioningDelegate, UINavigationControllerDelegate>
@property (nonatomic, strong) PresentAnimater *presentAnimater;
@property (nonatomic, strong) DismissAnimater *dismissAnimater;
@property (nonatomic, strong) DismissInteractiveTranstion *dimissInteractiveTranstion;
@end

@implementation ViewController
- (void)buttonClicked:(UIButton *)btn
{
    RedViewController *redVC = [[RedViewController alloc] init];
    redVC.transitioningDelegate = self;
    self.dimissInteractiveTranstion = [[DismissInteractiveTranstion alloc] initWithController:redVC];
    [self presentViewController:redVC animated:YES completion:nil];
}

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    return self.presentAnimater;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    return self.dismissAnimater;
}
- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator
{
    return self.dimissInteractiveTranstion.interacting ? self.dimissInteractiveTranstion : nil;
}


 
 
 

@implementation DismissAnimater
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.4;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    UIViewController *scVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    CGRect scFrame = scVc.view.frame;
    CGRect toFrame = scFrame;
    toFrame.origin.x = scFrame.size.width;
    
    toVc.view.frame = CGRectInset(scFrame, 20, 20);
    
    UIView *containerView = [transitionContext containerView];
    [containerView addSubview:toVc.view];
    [containerView sendSubviewToBack:toVc.view];
    
    CGFloat duration = [self transitionDuration:transitionContext];
    [UIView animateWithDuration:duration animations:^{
        scVc.view.frame = toFrame;
        toVc.view.frame = scFrame;
        toVc.view.alpha = 1;
    } completion:^(BOOL finished) {
        // NO : 会还原动画前所有View的位置。
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];

}
@end

@interface DismissInteractiveTranstion : UIPercentDrivenInteractiveTransition
- (instancetype)initWithController:(UIViewController *)vc;
@property (nonatomic, assign) BOOL interacting;// 用于判定是否需要手势过渡。
@end
@interface DismissInteractiveTranstion ()
@property (nonatomic, strong) UIViewController *scVc;
@property (nonatomic, assign) BOOL shouldComplated;
@end

@implementation DismissInteractiveTranstion
- (instancetype)initWithController:(UIViewController *)vc
{
    if (self = [super init]) {
        self.scVc = vc;
        UIPanGestureRecognizer *panGR = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGestureRecognizer:)];
        [vc.view addGestureRecognizer:panGR];
    }
    return self;
}
- (void)handleGestureRecognizer:(UIPanGestureRecognizer *)gr
{
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    CGFloat translate = [gr translationInView:gr.view].x;
    CGFloat progress = translate / width;
    
    switch (gr.state) {
        case UIGestureRecognizerStateBegan:
        {
            self.interacting = YES;
            [self.scVc dismissViewControllerAnimated:YES completion:nil];
        }
            break;
        case UIGestureRecognizerStateChanged:
        {
            progress = fminf(fmax(progress, 0.0), 1.0);
            self.shouldComplated = (progress > 0.5);
            NSLog(@"%f", progress);
            [self updateInteractiveTransition:progress];
        }
            break;
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateEnded:
        {
           self.interacting = NO;
            if (!self.shouldComplated || gr.state == UIGestureRecognizerStateCancelled) {
                [self cancelInteractiveTransition];
            } else {
                [self finishInteractiveTransition];
            }
        }
            break;
        default:
            break;
    }
}
@end

 

(1)在这里我们说的是手势交互动画对象,为什么我还要列出事件动画对象呢?这是因为如果交互动画对象没有自己的动画(即没有实现 - ( void )startInteractiveTransition:( id < UIViewControllerContextTransitioning >)transitionContext这个代理方法),系统会自动用事件动画来替代,我们提供的手势完成百分比用来控制该动画的进度。如果交互动画对象实现了自己代理方法,就不会去调用事件动画的动画方法了。(之后几篇中会有手势过渡和事件过渡动画不同的列子)

(2)手势开始后,方法调用过程是这样的:

首先,是手势方法(handleGestureRecognizer:)-- 当dismissViewControllerAnimated:之后 --> 返回事件动画对象 --> 返回交互动画对象

(3)这里还需要注意一点,如果我们只要事件过渡,那么是不能返回手势交互过渡动画对象的,如果返回程序会卡住。那么如果我们既需要事件过渡又需要手势过渡怎么办呢?这就是交互动画对象interacting属性的作用。如果是手势过渡,在手势事件中interacting被置为YES,代理方法中返回交互动画对象。如果是事件过渡,代理方法返回nil。

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

{

    return self.dimissInteractiveTranstion.interacting ? self.dimissInteractiveTranstion : nil;

}

(4)注意动画结束后,不能直接写[transitionContextcompleteTransition:YES];,因为考虑手势有取消的情况,所以要这样写[transitionContextcompleteTransition:![transitionContext transitionWasCancelled]];



Demo地址点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值