专栏首页Scott_Mr 个人专栏自定义转场详解(一)

自定义转场详解(一)

前言

本文是我学习了onevcat的这篇转场入门做的一点笔记。

今天我们来实现一个简单的自定义转场,我们先来看看这篇文章将要实现的一个效果图吧:

过程详解

热身准备

我们先创建一个工程,首先用storyboard快速的创建两个控制器,一个作为主控制器,叫ViewController,另外一个作为present出来的控制器,叫PresentViewController,并且用autoLayout快速搭建好界面。就像这样:

我们先做好点击ViewController上面的按钮,present出 PresentViewController,点击PresentViewController上面的按钮,dismiss掉PresentViewController的逻辑。这里有两个注意点:

  1. 因为此处我使用了segue,所以在ViewController按钮点击的时候,我们只需要这样调用就行。 #pragma mark - 点我弹出 -(IBAction)presentBtnClick:(UIButton *)sender { [self performSegueWithIdentifier:@"PresentSegue" sender:nil]; }
  2. 我们平时写dismiss的时候,一般都会是在第二个控制器中直接给self发送dismissViewController的相关方法。在现在的SDK中,如果当前的VC是被显示的话,这个消息会被直接转发到显示它的VC去。但是这并不是一个好的实现,违反了程序设计的哲学,也很容易掉到坑里。所以我们用标准的delegate 方式实现 dismiss

首先我们在PresentViewController控制器中申明一个代理方法。

    #import <UIKit/UIKit.h>
    @class PresentViewController;
    @protocol PresentViewControllerDelegate <NSObject>
    - (void)dismissViewController:(PresentViewController *)viewController;
    @end
    @interface PresentViewController : UIViewController
    @property (nonatomic, weak) id<PresentViewControllerDelegate> delegate;
    @end

在button的点击事件中,让代理去完成关闭当前控制器的工作。

    #pragma mark - 点击关闭
    - (IBAction)closeBtnClick:(UIButton *)sender {
        if (self.delegate && [self.delegate respondsToSelector:@selector(dismissViewController:)]) {
            [self.delegate dismissViewController:self];
        }
    }

与此同时,在ViewController中需要设置PresentViewController的代理,并且实现代理方法:

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
        if ([segue.identifier isEqualToString:@"PresentSegue"]) {
            PresentViewController *presetVC = segue.destinationViewController;
            presetVC.delegate = self;
        }
    }
    #pragma mark - PresentViewControllerDelegate
    - (void)dismissViewController:(PresentViewController *)viewController {
        [self dismissViewControllerAnimated:YES completion:nil];
    }

OK,到这里,我们一个基本的转场就完成了(这也是系统自带的一个效果)。like this:

主要内容

接下来,要接触我们今天要讲的主要内容了,我们用iOS7中一个新的类UIViewControllerTransitioning来实现自定义转场。


UIViewControllerAnimatedTransitioning

首先我们需要一个实现了协议名为UIViewControllerAnimatedTransitioning的对象。创建一个类叫做PresentAnimation继承于NSObject并且实现了UIViewControllerAnimatedTransitioning协议。(注意:需要导入UIKit框架)

    @interface PresentAnimation : NSObject<UIViewControllerAnimatedTransitioning>

这个协议负责转场的具体内容。开发者在做自定义切换效果时大部门代码会是用来实现这个协议的,这个协议只有两个方法必须要实现的:

    // 返回动画的时间
    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
    // 在进行切换的时候将调用该方法,我们对于切换时的UIView的设置和动画都在这个方法中完成。
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

实现这两个方法

    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
        return 0.8f;
    }
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
        // 1.我们需要得到参与切换的两个ViewController的信息,使用context的方法拿到它们的参照;
        UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];   
        // 2.对于要呈现的VC,我们希望它从屏幕下方出现,因此将初始位置设置到屏幕下边缘;
        CGRect finaRect = [transitionContext finalFrameForViewController:toVC];
        toVC.view.frame = CGRectOffset(finaRect, 0, [UIScreen mainScreen].bounds.size.height);
        // 3.将view添加到containerView中;
        [[transitionContext containerView] addSubview:toVC.view];
        // 4.开始动画。这里的动画时间长度和切换时间长度一致。usingSpringWithDamping的UIView动画API是iOS7新加入的,描述了一个模拟弹簧动作的动画曲线;
        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 usingSpringWithDamping:0.6 initialSpringVelocity:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
            toVC.view.frame = finaRect;
        } completion:^(BOOL finished) {
            // 5.在动画结束后我们必须向context报告VC切换完成,是否成功。系统在接收到这个消息后,将对VC状态进行维护。
            [transitionContext completeTransition:YES];
        }];
    }

注意点

UITransitionContextToViewControllerKeyUITransitionContextFromViewControllerKey 比如从A present 出B,此时A是FromViewController,B是ToViewController 如果从B dismiss 到A,此时A是ToViewController,B是FromViewController

UIViewControllerTransitioningDelegate

这个接口的作用比较单一,在需要VC切换的时候系统会向实现了这个接口的对象询问是否需要使用自定义转场效果。 所以,一个比较好的地方是直接在主控制器ViewController中实现这个协议。

ViewController中完成如下代码:

    @interface ViewController ()<PresentViewControllerDelegate,UIViewControllerTransitioningDelegate>
    @property (nonatomic, strong) PresentAnimation *presentAnimation;
    @end
    @implementation ViewController
    #pragma mark - 懒加载
    - (PresentAnimation *)presentAnimation {
        if (!_presentAnimation) {
            _presentAnimation = [[PresentAnimation alloc] init];
        }
        return _presentAnimation;
    }
    #pragma mark - UIViewControllerTransitioningDelegate
    - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
        return self.presentAnimation;
    }
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
        if ([segue.identifier isEqualToString:@"PresentSegue"]) {
            PresentViewController *presetVC = segue.destinationViewController;
            presetVC.delegate = self;
            presetVC.transitioningDelegate = self;
        }
    }

现在看下我们的效果:

相对于上面系统自带的效果来说,我们在present出第二个控制器的时候,带有弹簧效果。

手势驱动百分比切换

现在我们增加一个功能,就是用手势滑动来dismiss,通俗的说,就是让present出来的那个控制器使用手势dismiss。

  1. 创建一个类,继承自UIPercentDrivenInteractiveTransition
  • 我们写一个方法提供给外部类调用。让外部类可以看到传入手势dismiss的VC的入口。

2.既然传入了这个需要手势dismiss的VC,我们就需要保存一下,方便当前类在其他地方使用,所以我们新建一个属性来保存这个传入的VC。

3.和创建PresentAnimation一样,我们创建一个一个DismissAnimation

4.最后,我们在主控制器中添加一个手势驱动的对象,一个dismiss转场的对象,然后懒加载。

完善

此时,我们运行程序,会发现以上代码尽管可以手势驱动了,但是点击按钮dismiss的功能无法使用了。这是因为如果只是返回self.paninterTransition,那么点击按钮dismiss的动画就会失效;如果只是返回nil,那么手势滑动的效果将会失效。综上所述,我们就得分情况考虑。 接下来我们就来完善一下。

ok,到此为止,我们的一个自定义转场动画就算了完成了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 自定义转场详解(一)

    Scott_Mr
  • 两个imageView实现图片轮播

    Scott_Mr
  • 两个imageView实现图片轮播

    Scott_Mr
  • 用友营收增长乏力 但股票为何出现七连阳?

    截止2015年1月16日下午3点上证交易闭市,用友软件涨停封在37.52人民币,市值是437亿人民币,在这之前用友股票已经是7连阳。 从上证交易所发布的财报看,...

    人称T客
  • 纠结的用友 优普和超客合并折腾以外还有什么?

    优普和超客合并?刚开始听到这个消息都认为不可思议,好像超客从优普分拆出来十个月,尚不满一周岁就又勿勿合体,很多人用了一个词来形容是“折腾”,的确,如此折腾到底为...

    人称T客
  • 用友:ERP已成过去时 平台+生态+聚合服务成未来

    用友网络执行总裁陈强兵表示:ERP已成过去时,未来用友会将重心放在平台、生态和少量的核心云服务,其他的服务通过生态上的伙伴聚合来完成。在刚刚结束的伙伴大会上用友...

    人称T客
  • 用友Q1亏损加剧 转型良方何在?

    其实关于用友的转型近年来一直是大家讨论的焦点,无论是风风火火的互联网金融,还是去年中旬成立的用友超客,以及担负用友云战略先锋的畅捷通,一直都备受争议,业内普遍认...

    人称T客
  • 用友2017年第三财季亏损2.6亿 增长之路缘何如此艰难?

    前两天有人私信让我评价一下用友刚刚公布的最新财报,说实话关注了用友这么多年,用友一直没有多大变化,从2012年到2016年用友就进入了一个增长瓶颈期,营收一直在...

    人称T客
  • 盘点用友网络的2015

    今年整个ERP市场又一句来评价已是外强中干,看似繁花似锦实际上已是残枝败柳,可能很多人不会认同我的观点,其实这没有什么?连国外的知名厂商SAP和Oracle也同...

    人称T客
  • 60后盘距用友高管层 突破互联网思维何其艰难

    自从昨天写完《用友2014年人事调整之我见 老将回归未见新意》一文,引起业内网友的很大反响,本来今天想盘点一下《盘点SAP这些年的人事地震》,但应众多网友的需求...

    人称T客

扫码关注云+社区

领取腾讯云代金券