自定义UIViewController转场切换动画

好久没搞UI这一块。今天系统学习下UIViewController的转场切换自定义实现。常见的系统转场效果:presentdismisspushpop

自定义转场动画也是很有必要的。在增强App的交互性和体验方面,都可以给用户耳目一新的感觉。要实现转场切换动画,需要ViewController实现下面的协议:

UIKit库中的代码方法:


先说UINavigationControllerDelegate代理,最主要的方法是:

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

这个代理方法的返回值是UIViewControllerAnimatedTransitioning协议类型的对象,也就是即将写自定义转场的那个类需要实现的协议。

再看operation参数的具体定义:

typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {
    UINavigationControllerOperationNone,
    UINavigationControllerOperationPush,
    UINavigationControllerOperationPop,
};

这里operation的含义就是Navigation控制器的切入切出操作的枚举变量。

所以可以在这个方法里判断切换的类型,然后返回自定义的转场对象:

if (operation == UINavigationControllerOperationPush) {
    return _pushAnimation;
}else if (operation == UINavigationControllerOperationPop) {
    return _popTransition;
} else{
    return nil;
}

接下来看那个需要遵从UIViewControllerAnimatedTransitioning协议,自定义的转场动画对象:

@interface VKPushTransition : NSObject <UIViewControllerAnimatedTransitioning>

这里细看下UIViewControllerAnimatedTransitioning协议里面重要的两个方法:

//自定义动画切换时间
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;

//自定义切换的实现函数
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

这个实现函数里,需要添加以下关键代码用来获取跳转前的VC和跳转后的VC,以及获取containerView:

UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIView *containerView = [transitionContext containerView];

如果要对视图做转场动画,视图就必须要加入containerView中才能进行,可以理解containerView管理着所有做转场动画的视图。

这里给一个完整的函数实现:


- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    return 2.0;  //切换时间
}

- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    
    //目的ViewController
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    //起始ViewController
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    
    UIView *containerView = [transitionContext containerView];

    [containerView addSubview:fromViewController.view];
    [containerView addSubview:toViewController.view];
    
    //自定义动画
    toViewController.view.transform = CGAffineTransformMakeTranslation(0, -screenHeight);
    
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromViewController.view.transform = CGAffineTransformMakeTranslation(0, screenHeight);
        toViewController.view.transform = CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
        fromViewController.view.transform = CGAffineTransformIdentity;
        // 声明过渡切换结束时调用completeTransition:这个方法
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

实现了协议的方法,下一步就是连接起来。

在前一个导航控制器Navigation的连接是这样的:self.navigationController = self;

这样就绑定了Navigation控制器切换时调用实现的UINavigationControllerDelegate协议方法,协议方法里,返回自定义的切换动画效果。

然后运行切换ViewController应该就可以看到效果了。


还有一种是present/dismiss的方式开始转场动画。其实与上面的pop/push类似的代码布局(实现协议,绑定协议,实现自定义动画类)。

也就是实现UIViewControllerTransitioningDelegate协议方法。

- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    _presentTransition.animationType = AnimationTypePresent;
    
    return _presentTransition;
}

- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    _presentTransition.animationType = AnimationTypeDismiss;
    
    return _presentTransition;
}

在方法里返回自定义的UIViewControllerAnimatedTransitioning类型的切换动画对象。

上面的代码使用了自己声明的枚举来判断是present还是dismiss的操作,方便在- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;方法里区别分开处理。

同样,需要把协议与对象连接起来:

ThirdViewController *third = [[ThirdViewController alloc] init];
third.transitioningDelegate = self;
[self presentViewController:third animated:YES completion:nil];

这里绑定的transitioningDelegate需要注意,就是即将跳转的ThirdViewController的绑定。跳转目标VC与实现UIViewControllerTransitioningDelegate协议的对象绑定即可。所以可以在跳转目标的vc里面实现这个协议,就可以不像上面那样在前一个VC中绑定协议,实现协议了,而是在目标VC中绑定协议,实现协议,一样是完全可以的。在目标ThirdViewController的init方法里面加上绑定协议的代码也是可以的,像这样:

- (instancetype)init{
    self = [super init];
    if (self) {
        self.transitioningDelegate = self;
    }
    
    return self;
}

前提是你的self实现了UIViewControllerTransitioningDelegate协议。

自定义切换动画还支持交互,用户在滑动手指时,可以让切换动画跟随手指的移动发生改变。可以在UINavigationControllerDelegateUIViewControllerTransitioningDelegate中查看与interaction相关的方法。

上述总结,均可以在这个示例项目中有体现。下载链接:戳我。


参考:ViewController的自定义转场动画(UIViewControllerAnimatedTransitioning)

文章来自 http://skymonkey.cn/

高能广告区

暂无广告哦=^^=。继续看看其它文章吧!