美文网首页IOSios开发ios学习
自定义转场-模仿简书的效果

自定义转场-模仿简书的效果

作者: Little_Dragon | 来源:发表于2015-10-14 01:40 被阅读751次

    当看到了简书的转场效果, 觉得很有意思就试着进行做一个. 自定义转场, 有一种后转的效果出现. 立体效果比较明显.

    简单的做一个demo

    简书的转场特效.gif

    在上一篇文章中我已经指出, 苹果在WDCC2014年中提供了新的API给我们实现转场效果.
    与上一篇文章做一个效果的对比, 首先它多的地方就是需要进行三维的偏转, 加有一定阴影.

    1. 三维效果需要有一种即视的立体感.

    demo的代码

    Custom3D中代码实现,转场的代理和动画的执行方式
    有一点需要了解的就是:(这个是 图层3D旋转的 属性.其中M34很重要)
    CATransform3D transform = CATransform3DIdentity; // 如何增加立体感,近大远小 // 400.0 可以理解为,人的眼睛离手机屏幕的垂直距离,越小,近大远小效果越明显 transform.m34 = -1 / 400.0; transform = CATransform3DRotate(transform, _angel, 1, 0, 0);

    // .h文件
    #import <UIKit/UIKit.h>
    // 遵守协议, 实现动画协议
    @interface Custom3D : NSObject<UIViewControllerTransitioningDelegate,UIViewControllerAnimatedTransitioning>
    // 需要接收外界传入一个角度,来进行3D旋转角度
    @property (assign, nonatomic) CGFloat angel;
    // 需要外界提供需要modal出来的尺寸.
    @property (assign, nonatomic) CGRect rect;
    @end
    
    // .m文件
    #import "Custom3D.h"
    #import "PresentationController.h"
    
    @interface Custom3D()
    // 设置3D的旋转, 即视感 修改M34属性
    @property (assign, nonatomic) CATransform3D transform;
    // 用来判断 是否处于 展示 状态
    @property (assign,nonatomic) BOOL isPop;
    
    @end
    @implementation Custom3D
    
    
    
    #pragma mark -- UIViewControllerTransitioningDelegate代理方法
    
    // 当外界传入angel的时候, 进行设置transform的相关属性
    - (void)setAngel:(CGFloat)angel
    {
        _angel = angel;
        
        CATransform3D transform = CATransform3DIdentity;
        
        // 如何增加立体感,近大远小
        // 400.0 可以理解为,人的眼睛离手机屏幕的垂直距离,越小,近大远小效果越明显
        transform.m34 = -1 / 400.0;
        transform = CATransform3DRotate(transform, _angel, 1, 0, 0);
        self.transform = transform;
    }
    
    // 返回一个UIPresentationController的对象,该对象创建需要  展示控制器, 和 换出展示的控制器. 我们可以更改该对象,进行设置展示控制器大小什么的.
    - (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
    {
        PresentationController *presentVC = [[PresentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
        
        presentVC.rect = self.rect;
        return presentVC;
    }
    
    // 当展示出控制器, 需要谁来控制动画的执行
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
    {
        self.isPop = YES;
        return self;
    }
    // 但退出控制器的时候,需要谁来控制动画的执行
    - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    {
        self.isPop = NO;
        return self;
    }
    
    // 设置动画事件
    - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        return 0.5;
    }
    // 当展示和退出都是利用这个方法来做动画的, 怎么做呢, 需要注意的是: 弹出时,和退出时, 上下文中的transitionContext 的from和to 进行了反转. 开始是你modal我, 后来是我modal你
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        // 当展示的时候
        if (self.isPop) {
            // 取出`to`控制器, modal出来的控制器
            UIViewController *toPresentVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
            // 取出 换出展示的控制器.
            UIViewController *fromPrenestVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
            // 设置锚点, 让其从下面出来
            toPresentVC.view.layer.anchorPoint = CGPointMake(0.5, 1);
            toPresentVC.view.layer.shadowColor = [UIColor grayColor].CGColor;
            toPresentVC.view.layer.shadowOffset = CGSizeMake(3, 3);
            
            toPresentVC.view.layer.shadowOpacity = 1.0;
            // 需要将modal出来的控制器,加到 上下文的 containerView中, 需要添加,否则不会展示出来view .
            [[transitionContext containerView] addSubview:toPresentVC.view];
            // 当没有被展示的时候 需要隐藏
            toPresentVC.view.hidden = YES;
            // 呼出展示控制器的控制器,的时候要有一定的阴影效果
            fromPrenestVC.view.layer.shadowOffset = CGSizeMake(10, 10);
            fromPrenestVC.view.layer.shadowOpacity = 1.0;
            fromPrenestVC.view.layer.shadowColor = [UIColor grayColor].CGColor;
            // 此控制器,需要将锚点 进行更正, 如果从中心位置进行反转的话,会出现一半载里面,一半在外面,会将蒙版挡住.  所以锚点必须设置在底部中心处.
            fromPrenestVC.view.layer.anchorPoint = CGPointMake(0.5, 1);
            // 当锚点设置时候, 会将原layer进行了一定的改变, 锚点--找到定位点, 似的layer向上移, 所以需要layer向下移动
            fromPrenestVC.view.layer.frame = [UIScreen mainScreen].bounds;
            // 设置开始时 展示控制器的 形变
            toPresentVC.view.transform = CGAffineTransformMakeTranslation(0, self.rect.size.height);
           
            // 设置展示动画
            [UIView animateWithDuration:0.5 animations:^{
                
                // 呼出展示控制器的控制器 的3D动画
                fromPrenestVC.view.layer.transform = self.transform;
          
    //
                
            } completion:^(BOOL finished) {
                // 完成后在执行 展示控制器 弹出
                [UIView animateWithDuration:0.5 animations:^{
                    
                    
                    //首先观察到, 系统会先3D 后又在 整体frame减小, 自己再次更改,让动画显得流畅
                    fromPrenestVC.view.transform =     CGAffineTransformMakeScale(0.93, 0.93);
                    // 展示控制器 弹出, 去掉原来设置的 形变值就行了
                    toPresentVC.view.transform = CGAffineTransformIdentity;
                    // 展示控制器显示出来
                  toPresentVC.view.hidden = NO;
                } completion:^(BOOL finished) {
                    // 完成动画后,记得将上下文 关闭, 否则不能完成以后的动画
                    [transitionContext completeTransition:YES];
                }];
                
                
            }];
            
        
        }
        // 退出的时候  dismiss的时候, 上下文中的结构就改变了.  我们需要将后面的控制器 modal出来
        else{
            // 取出展示的控制器
            UIViewController *toPresentVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
            // 取出呼出展示控制器的控制器
            UIViewController *fromPrenestVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
            // 设置结束的动画
            [UIView animateWithDuration:0.5 animations:^{
                // 展示控制器的图层 3D形变,需要再次后翻的动画
                toPresentVC.view.layer.transform = self.transform;
                // 呼出展示控制器的控制器,的形变进行设置,让整个控制器 向下移动 设置的值.
                fromPrenestVC.view.transform = CGAffineTransformMakeTranslation(0, self.rect.size.height);
                
            } completion:^(BOOL finished) {
                
                // 完成后执行第二段动画
                [UIView animateWithDuration:0.5 animations:^{
                    // 展示控制器的 形变恢复
                    toPresentVC.view.transform = CGAffineTransformIdentity;
                } completion:^(BOOL finished) {
                    // 动画完成后 需要关闭上下文,否则不能执行下一代动画
                    [transitionContext completeTransition:YES];
                }];
                
            }];
       
        }
    }
    @end
    

    PresentationController,专门为自己modal的控制器的尺寸设置的, 自己可以进行尺寸的设置.

    // .h文件
    #import <UIKit/UIKit.h>
    
    @interface PresentationController : UIPresentationController
    // 需要外界传入尺寸, 来设置modal出来的控制器的大小
    @property (assign, nonatomic) CGRect rect;
    @end
    
    // .m文件
    
    #import "PresentationController.h"
    @interface PresentationController()
    // 设置蒙版层
    @property(nonatomic,strong) UIView *cover;
    
    @end
    @implementation PresentationController
    // 对象中有containerView 中包含 展示的内容(展示-- 呼出展示的 view ,控制器等相关的配置都需要一下的方法进行设置)
    - (void)containerViewWillLayoutSubviews
    {
        [super containerViewWillLayoutSubviews];
            // 取出展示控制器的frame
            UIViewController *presentVC = self.presentedViewController;
        // 将外界传入的rect 付给展示的控制器, 也就是设置展示控制器的frame
        presentVC.view.frame = self.rect;
            // 设置蒙版
            UIView *cover = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds];
            cover.backgroundColor = [[UIColor alloc]initWithWhite:0.5 alpha:0.5];
            //给蒙版添加手势
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(click)];
            [cover addGestureRecognizer:tap];
        
            self.cover = cover;
            // 加蒙版插到中间.
        
            [self.containerView insertSubview:cover atIndex:0];;
            
    
    }
    // 点击蒙版 退出modal状态
    - (void)click
    {
    
        [self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
    }
    @end
    

    viewcontoller文件

    
    #import "ViewController.h"
    
    #import "Custom3D.h"
    @interface ViewController ()
    // 点击按钮
    @property (weak, nonatomic) IBOutlet UIButton *modal;
    // 点击触发
    - (IBAction)clickModal;
    // 设置展示控制器
    @property (strong, nonatomic)UIViewController *modalVC;
    // 设置自定义转场的代理, 用于转场3D动画
    @property (strong, nonatomic) Custom3D *custom;
    
    @end
    
    @implementation ViewController
    
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.modalVC = [[UIViewController alloc]init];
        self.modalVC.view.backgroundColor = [UIColor redColor];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    - (IBAction)clickModal {
        
        self.custom = [[Custom3D alloc]init];
        self.modalVC.transitioningDelegate = self.custom;
        self.custom.angel = M_1_PI * 0.1;
        
        self.custom.rect =  CGRectMake(0, [UIScreen mainScreen].bounds.size.height-200, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
        
        self.modalVC.modalPresentationStyle = UIModalPresentationCustom;
        
        [self presentViewController:self.modalVC animated:YES completion:nil];
    }
    @end
    

    将这3段代码,写在文件中,便可以实现上述效果. 下一次给大家,讲解图片浏览器的功能实现.

    相关文章

      网友评论

        本文标题:自定义转场-模仿简书的效果

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