美文网首页Swift开发iOS技术交流
iOS转场动画的三种方式

iOS转场动画的三种方式

作者: 雪山飞狐_91ae | 来源:发表于2018-04-03 21:47 被阅读188次

    1.CATransition

    CATransition是CAAnimation的子类,用于过渡动画或转场动画。为视图层移入移除屏幕提供转场动画。首先来看一下简单的Demo:

       CATransition *animation = [CATransition animation];
        animation.type = kCATransitionFade;
        animation.subtype = kCATransitionFromRight;
        animation.duration = 1.0;
        // 在window上执行CATransition, 即可在ViewController转场时执行动画
        [self.view.window.layer addAnimation:animation forKey:@"kTransitionAnimation"];
        
        AViewController *vc = [[AViewController alloc] init];
        [self presentViewController:vc animated:NO completion:nil];
    

    将该动画添加到window.layer上,则会present或push时使用指定的转场动画。
    其中最主要的两个属性就是type和subtype。

    • type:转场动画的类型。
      官方SDK只提供了四种转场动画的类型,即:
    CA_EXTERN NSString * const kCATransitionFade;
    CA_EXTERN NSString * const kCATransitionMoveIn;
    CA_EXTERN NSString * const kCATransitionPush;
    CA_EXTERN NSString * const kCATransitionReveal;
    

    私有的type:

    NSString *const kCATransitionCube = @"cube"; 
    NSString *const kCATransitionSuckEffect = @"suckEffect"; 
    NSString *const kCATransitionOglFlip = @"oglFlip"; 
    NSString *const kCATransitionRippleEffect = @"rippleEffect"; 
    NSString *const kCATransitionPageCurl = @"pageCurl"; 
    NSString *const kCATransitionPageUnCurl = @"pageUnCurl"; 
    NSString *const kCATransitionCameraIrisHollowOpen = @"cameraIrisHollowOpen";
    NSString *const kCATransitionCameraIrisHollowClose = @"cameraIrisHollowClose";
    
    • subtype:动画类型的方向
    CA_EXTERN NSString * const kCATransitionFromRight;
    CA_EXTERN NSString * const kCATransitionFromLeft;
    CA_EXTERN NSString * const kCATransitionFromTop;
    CA_EXTERN NSString * const kCATransitionFromBottom;
    

    上面讲的是给window.layer添加transition,这样使得在present或push时使用指定的转场动画。
    既然讲到这里了,就看一下把transition加在layer上。
    看一下示例代码:

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 50)];
        
        [button setTitle:@"进入" forState:UIControlStateNormal];
        
        button.backgroundColor = [UIColor redColor];
        
        [button addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];
        
        [self.view addSubview:button];
        
        _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 300, 150, 150)];
        [self.view addSubview:_imageView];
        _imageView.backgroundColor = [UIColor redColor];
        _imageArray = @[[UIImage imageNamed:@"成果秀1"],[UIImage imageNamed:@"点赞他人1"],[UIImage imageNamed:@"偷师学艺1"],[UIImage imageNamed:@"学会欣赏3"]];
        
        _imageView.image = [UIImage imageNamed:@"成果秀1"];
    }
    
    
    
    - (void)buttonClicked{
      
        CATransition *animation = [CATransition animation];
        animation.type = @"cube";
        animation.subtype = kCATransitionFromRight;
        animation.duration = 1.0;
        //换图片的时候使用转场动画
        [self.imageView.layer addAnimation:animation forKey:nil];
        //cycle to next image
        UIImage *currentImage = self.imageView.image;
        NSUInteger index = [self.imageArray indexOfObject:currentImage];
        index = (index + 1) % [self.imageArray count];
        self.imageView.image = self.imageArray[index];
       
    }
    

    2.transitionFromViewController

    • UIViewController自带的方法:
      transitionFromViewController:toViewController:duration:options:animations:completion:这个转场动画是用在当一个父视图控制器中有几个childViewController,当要在这几个子视图控制器之间切换时就可以用这个方法。
    AViewController *a = self.childViewControllers[0];
    BViewController *b = self.childViewControllers[1];
    CViewController *c = self.childViewControllers[2];
    
    // Curl 翻页效果
    // UIViewAnimationOptionTransitionCurlUp, UIViewAnimationOptionTransitionCurlDown
    // Flip 翻转效果
    // UIViewAnimationOptionTransitionFlipFromLeft, UIViewAnimationOptionTransitionFlipFromRight
    // UIViewAnimationOptionTransitionFlipFromTop, UIViewAnimationOptionTransitionFlipFromDown
    
    [self transitionFromViewController:_currentViewController
                      toViewController:b
                              duration:0.5
                               options:UIViewAnimationOptionTransitionFlipFromRight
                            animations:^{
    
    } completion:^(BOOL finished) {
    
    }];
    

    3.Transition Animation

    1 UINavigationControllerDelegate + UIViewControllerAnimatedTransitioning

    在UINavigationController的转场动画中,要指定UINavigationControllerDelegate对象:

    self.navigationController.delegate = self;
    [self.navigationController pushViewController:itemVC animated:YES];
    

    UINavigationControllerDelegate主要有以下两个协议方法:

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

    首先创建一个基类PDAnimatorBaseTransition实现UIViewControllerAnimatedTransitioning协议的方法。
    UIViewControllerAnimatedTransitioning协议的方法有:

    //动画持续时间
    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
    //转场动画实现细节
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
    动画结束时调用
    - (void)animationEnded:(BOOL) transitionCompleted;
    
    • PDAnimatorBaseTransition.h
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    typedef NS_ENUM(NSInteger, PDAnimationType){
        
        animationTypePresent = 0,
        animationTypeDismiss ,
        animationTypePush ,
        animationTypePop,
    };
    
    @interface PDAnimatorBaseTransition : NSObject <UIViewControllerAnimatedTransitioning>
    
    @property (nonatomic, assign)PDAnimationType animationType;
    
    @property (nonatomic, strong)UIView *containerView;
    
    @property (nonatomic, strong)UIViewController *from;
    
    @property (nonatomic, strong)UIViewController *to;
    
    @property (nonatomic, strong)UIView *fromView;
    
    @property (nonatomic, strong)UIView *toView;
    
    @property (nonatomic, weak)id <UIViewControllerContextTransitioning> transitionContext;
    
    
    
    @end
    
    • PDAnimatorBaseTransition.m
    #import "PDAnimatorBaseTransition.h"
    @interface PDAnimatorBaseTransition() 
    @end
    @implementation PDAnimatorBaseTransition
    
    #pragma mark -required
    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{
        
        return 1.f;
    }
    
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
        
        _transitionContext = transitionContext;
        
        _containerView = [transitionContext containerView];
        
        _from = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        
        _to = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        
        if([transitionContext respondsToSelector:@selector(viewForKey:)]){
            
            _fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
            _toView = [transitionContext viewForKey:UITransitionContextToViewKey];
        }else{
            
            _fromView = _from.view;
            _toView = _to.view;
        }
        
        if(self.animationType == animationTypePresent){
            
            [self animationPresent];
        }else if (self.animationType == animationTypeDismiss){
            
            [self animationDismiss];
        }else if (self.animationType == animationTypePop){
            
            [self animationPop];
        }else{
            
            [self animationPush];
        }
        
    }
    #pragma mark -optional
    
    //动画结束时回调
    - (void)animationEnded:(BOOL) transitionCompleted{   
    }
    - (void)animationPresent{  
    }
    - (void)animationDismiss{
    }
    - (void)animationPop{ 
    }
    - (void)animationPush{
      
    }
    

    然后创建子类PDAnimatorPUshPopTransition继承自PDAnimatorBaseTransition,实现- (void)animationPush,- (void)animationPop方法。

    • PDAnimatorPUshPopTransition.h
    #import "PDAnimatorBaseTransition.h"
    #import <UIKit/UIKit.h>
    
    @interface PDAnimatorPUshPopTransition : PDAnimatorBaseTransition
    @property (nonatomic, assign)CGPoint itemCenter;
    
    @property (nonatomic, assign)CGSize itemSize;
    
    @property (nonatomic, strong)NSString *imageName;
    
    @end
    
    • PDAnimatorPUshPopTransition.m
    #import "PDAnimatorPUshPopTransition.h"
    
    @implementation PDAnimatorPUshPopTransition
    
    - (instancetype)init{
        
        self = [super init];
        if(self){
            
            
        }
        
        return self;
    }
    
    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{
        
        return 5.f;
    }
    
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
        
        [super animateTransition:transitionContext];
    }
    
    - (void)animationPush{
        
        NSTimeInterval duration = [self transitionDuration:self.transitionContext];
        __weak typeof(self) weakSelf = self;
        
        self.containerView.backgroundColor = [UIColor lightGrayColor];
        
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 160)];
        imageView.image = [UIImage imageNamed:self.imageName];
        imageView.center = _itemCenter;
        
        CGFloat initialScale = _itemSize.width / CGRectGetWidth(imageView.frame);
        
        CGAffineTransform transform = CGAffineTransformIdentity;
        
        transform = CGAffineTransformScale(transform, initialScale, initialScale);
        transform = CGAffineTransformRotate(transform, M_PI);
        
        imageView.layer.affineTransform = transform;
        
     //   imageView.transform = CGAffineTransformMakeScale(initialScale, initialScale);
        
        [self.containerView addSubview:imageView];
        
        
        self.toView.frame = [self.transitionContext finalFrameForViewController:self.to];
        CGPoint finalCenter = self.toView.center;
        self.toView.center = finalCenter;
        self.toView.alpha = 0.0;
        //这一句一定要
        [self.containerView addSubview:self.toView];
        
        [UIView animateWithDuration:duration delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{
            
    
            imageView.layer.affineTransform = CGAffineTransformIdentity;
            
            imageView.center = finalCenter;
            
            self.fromView.alpha = 0.0;
            self.containerView.backgroundColor = [UIColor redColor];
        } completion:^(BOOL finished){
            
            [imageView removeFromSuperview];
            
            weakSelf.toView.alpha = 1.0f;
            weakSelf.fromView.alpha = 1.0f;
            
            [weakSelf.transitionContext completeTransition:![weakSelf.transitionContext transitionWasCancelled]];
        }];
        
    }
    
    - (void)animationPop{
        
        NSTimeInterval duration = [self transitionDuration:self.transitionContext];
        __weak typeof(self) weakSelf = self;
        
        self.toView.frame = [self.transitionContext finalFrameForViewController:self.to];
        
        [self.containerView insertSubview:self.toView belowSubview:self.fromView];
        
        self.fromView.alpha = 0.0;
        
        self.fromView.backgroundColor = [UIColor clearColor];
        
        [UIView animateWithDuration:duration delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{
            
            CGFloat initialScale = _itemSize.width / 200;
            weakSelf.fromView.transform = CGAffineTransformMakeScale(initialScale, initialScale);
            weakSelf.fromView.center = weakSelf.itemCenter;
            
            weakSelf.toView.alpha = 1.0f;
        } completion:^(BOOL finished){
            
            weakSelf.fromView.alpha = 0.0f;
            
            [weakSelf.transitionContext completeTransition:![weakSelf.transitionContext transitionWasCancelled]];
        }];
    }
    
    
    @end
    

    然后我们在需要push的地方实现UINavigationControllerDelegate的协议方法:

    - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                                animationControllerForOperation:(UINavigationControllerOperation)operation
                                                             fromViewController:(UIViewController *)fromVC
                                                               toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0){
        
        PDAnimatorPUshPopTransition *animationTransition = [[PDAnimatorPUshPopTransition alloc] init];
        if(operation == UINavigationControllerOperationPush){
            
            animationTransition.animationType = animationTypePush;
        }else if (operation == UINavigationControllerOperationPop){
            
            animationTransition.animationType = animationTypePop;
        }
        
        NSArray *indexPaths = [self.collectionView indexPathsForSelectedItems];
        if (indexPaths.count == 0) {
            return nil;
        }
        
        NSIndexPath *selectedIndexPath = indexPaths[0];
        UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:selectedIndexPath];
        
        // 一定要加上convertPoint:toView:操作
        animationTransition.itemCenter = [self.collectionView convertPoint:cell.center toView:self.view];
        animationTransition.itemSize = cell.frame.size;
        animationTransition.imageName = [NSString stringWithFormat:@"%ld", (long)selectedIndexPath.item];
        
        return animationTransition;
    }
    

    2 UIViewControllerTransitioningDelegate+UIViewControllerAnimatedTransitioning

    首先需要设置被present的Controller的transitionDelegate

    DemoViewControllerTransitionPresentedViewController *presentedVC = [[DemoViewControllerTransitionPresentedViewController alloc] init];
    presentedVC.transitionDelegate = self;
    [self presentViewController:presentedVC animated:YES completion:nil];
    

    UIViewControllerTransitioningDelegate的代理的协议方法有:

    //       prenent       
    - (id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
    //       pop       
    - (id )animationControllerForDismissedController:(UIViewController *)dismissed;
    //       prenent       
    - (id )interactionControllerForPresentation:(id )animator;
    //       pop       
    - (nullable id )interactionControllerForDismissal:(id )animator;
    

    创建子类PDAnimationPresentTransitio继承自基类PDAnimatorBaseTransition,实现- (void)animationPresent,- (void)animationDismiss方法。

    • PDAnimationPresentTransition.h
    #import "PDAnimatorBaseTransition.h"
    
    @interface PDAnimationPresentTransition : PDAnimatorBaseTransition
    
    @end
    
    • PDAnimationPresentTransition.m
    #import "PDAnimationPresentTransition.h"
    
    @implementation PDAnimationPresentTransition
    
    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{
        
        return 1.f;
    }
    
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
        [super animateTransition:transitionContext];
    }
    
    - (void)animationPresent{
        
        NSTimeInterval duration = [self transitionDuration:self.transitionContext];
        __weak typeof(self) weakSelf = self;
        
        self.toView.frame = [self.transitionContext initialFrameForViewController:self.to];
        
        self.fromView.frame = [self.transitionContext initialFrameForViewController:self.from];
        
        CGAffineTransform transform = CGAffineTransformIdentity;
        transform = CGAffineTransformScale(transform, 0.001, 0.001);
        self.toView.layer.affineTransform = transform;
        
        [self.containerView addSubview:self.toView];
        
        self.toView.alpha = 0.0;
        
        [UIView animateWithDuration:duration delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
            
            self.toView.layer.affineTransform = CGAffineTransformIdentity;
            self.toView.frame = [self.transitionContext finalFrameForViewController:self.to];
            self.toView.alpha = 1.0;
        } completion:^(BOOL finished){
            
            BOOL wasCancelled = [weakSelf.transitionContext transitionWasCancelled];
            [weakSelf.transitionContext completeTransition:!wasCancelled];
        }];
        
    }
    

    然后我们在需要present的地方实现UIViewControllerTransitioningDelegate的代理方法。

    - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
        
        PDAnimationPresentTransition *animationTransition = [[PDAnimationPresentTransition alloc] init];
        animationTransition.animationType = animationTypePresent;
        return animationTransition;
    }
    
    - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
        
        PDAnimationPresentTransition *animationTransition = [[PDAnimationPresentTransition alloc] init];
        animationTransition.animationType = animationTypePresent;
        return animationTransition;
    }
    

    相关文章

      网友评论

        本文标题:iOS转场动画的三种方式

        本文链接:https://www.haomeiwen.com/subject/fqyacftx.html