自定义Pop动画

modal界面跳转

在IOS中,相比大家都不陌生,除了导航控制器中的经常使用的push以外,我们也经常使用从下而上跳转的控制器效果,这也就是modal效果。

modal跳转使用

对于使用storyBoard内进行连线的跳转,我们只需要将一个按钮连线到我们需要的控制器,然后选择modal效果,就可以实现从当前控制器跳转到下一个控制器。

而如何通过代码的话,也十分容易,先准备好需要跳转的modalVC控制器,只需要调用这么一句话

<code class="hljs ini has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-title" style="box-sizing: border-box; color: rgb(0, 102, 102);">[self presentViewController:modalVC animated:YES completion:completion]</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

然后当需要回到原先界面的时候,只需要在出发时间的方法内部调用

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">[<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.dismissViewControllerAnimated</span>:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span> completion:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>];</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

modal扩展需求

一般而言,我们对于界面的跳转也就到这里为止了,所以我们可以看到目前大部分的APP在采用modal跳转的时候都是很简单的从下往上跳转,然后回去从下往上。

但是当我们闲着蛋疼看一些酷炫的APP的时候,会发现有些界面的跳转十分的酷炫。

  • 非常规跳转动画,也就是在modal的效果上,转换成其他的一些转场动画,或者自己定义一些效果
  • 让跳出来的界面不是全屏显示,并且可以随意的动画跳出来,也就是类似下面的这样的效果。这是我从GitHub Pod大神封装的Pop动画演示程序截图的, 
    https://github.com/schneiderandre/popping 
    pop效果

modal系统默认情况下的调用原理

  • 对于StoryBoard拖线实现的跳转来说,和其他控制器跳转一样,跳转的前后的视图控制器都是通过Segue对象来进行的。Segue会在跳转前调用这个方法
<code class="hljs erlang has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-pp" style="box-sizing: border-box;">-<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(void)</span>prepareForSegue:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIStoryboardSegue</span> *)</span>segue sender:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(id)</span>sender</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

根据Segue对象的destinationViewController属性可以获得跳转目标控制器,一般我们会该方法做跳转前的一些动作。 
* 而对于用代码跳转的方式,因为是我们自己创建的目标控制器,因此,我们可以自己手动的在执行跳转的presentViewController方法之前做好需要做的准备。 
* 其实不论是StoryBoard还是代码,系统最终都会调用presentViewController进行跳转。

内部系统提供的方法

  • 在modal跳转的时候,其实每个需要被modal的视图控制器都有两个属性: 
    • modalPresentationStyle modal展现的类型。这是一个枚举类型,官方头文件中表示了内部的全部类型:
<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">typedef</span> NS_ENUM(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSInteger</span>, UIModalPresentationStyle) {
        UIModalPresentationFullScreen = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>,
        UIModalPresentationPageSheet NS_ENUM_AVAILABLE_IOS(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>_2),
        UIModalPresentationFormSheet NS_ENUM_AVAILABLE_IOS(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>_2),
        UIModalPresentationCurrentContext NS_ENUM_AVAILABLE_IOS(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3</span>_2),
        UIModalPresentationCustom NS_ENUM_AVAILABLE_IOS(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">7</span>_0),
        UIModalPresentationOverFullScreen NS_ENUM_AVAILABLE_IOS(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>_0),
        UIModalPresentationOverCurrentContext NS_ENUM_AVAILABLE_IOS(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>_0),
        UIModalPresentationPopover NS_ENUM_AVAILABLE_IOS(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>_0),
        UIModalPresentationNone NS_ENUM_AVAILABLE_IOS(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">7</span>_0) = -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>,         
};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

从这些类型中,我们很容易就可以发现,其实系统本身也支持其他的一些展示的效果,不过效果有些局限。但是IOS 7.0之后,我们发现这里多了一个UIModalPresentationCustom。也就是说,我们可以自定义展现类型咯。 
既然可以自定义,系统的其他类型就不管了,只要能实现自定义,那么大千世界就任我为所欲为咯。

  • transitioningDelegate 动画代理。 它是一个遵守了UIViewControllerTransitioningDelegate 协议的任意对象。那么既然是协议,我们就先看看内部有哪些协议方法:
<code class="hljs erlang has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">// 控制展现动画的控制器,该方法要求返回遵守了<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIViewControllerAnimatedTransitioning</span>该协议的对象
<span class="hljs-pp" style="box-sizing: border-box;">- <span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(id <<span class="hljs-variable" style="box-sizing: border-box;">UIViewControllerAnimatedTransitioning</span>>)</span>animationControllerForPresentedController:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIViewController</span> *)</span>presented presentingController:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIViewController</span> *)</span>presenting sourceController:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIViewController</span> *)</span>source;
// 控制消失动画的控制器,该方法要求返回遵守了UIViewControllerAnimatedTransitioning该协议的对象
- <span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(id <<span class="hljs-variable" style="box-sizing: border-box;">UIViewControllerAnimatedTransitioning</span>>)</span>animationControllerForDismissedController:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIViewController</span> *)</span>dismissed;
// 返回控制转场动画的控制器<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIPresentationController</span>)</span>,该方法将正在显示的对象,和需要显示的对象都交给你,然后返回一个控制转场动画的动画
- <span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIPresentationController</span> *)</span>presentationControllerForPresentedViewController:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIViewController</span> *)</span>presented presentingViewController:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIViewController</span> *)</span>presenting sourceViewController:<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-variable" style="box-sizing: border-box;">UIViewController</span> *)</span>source NS_AVAILABLE_IOS<span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">8</span>_0)</span>;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

这个协议中还有另外两个是支持实时交互的方法,这里暂时涉及不到就先不提了。

从代理方法我们可以看出,根据上面的两个方法,我们还需要了解

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">UIViewControllerAnimatedTransitioning这个协议
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 转场动画 (提供了转场上下文,根据上下文处理转场动画)</span>
- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)animateTransition:(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> <UIViewControllerContextTransitioning>)transitionContext;
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 转场动画时长,一般这里都随便填,真正的动画时间看你自定义的动画时长来决定</span>
- (<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSTimeInterval</span>)transitionDuration:(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> <UIViewControllerContextTransitioning>)transitionContext;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>

自定义modal分析

根据以上的代理方法,根据系统的调用顺序,合理的实现代理方法就可以进行自定义动画了。

分析图

根据分析图可知,系统会根据系统内部顺序调用代理方法,因此,我们需要继承以上代理并且实现内部的代理方法。

专门设置一个动画代理

为了防止主控制器的臃肿和方法的解耦,主控制不关心动画的过程,而是我们专门创建一个对象来遵守所有的协议方法并且实现协议的方法。该代理遵守转场代理和转场动画协议,所有的转场动画的协议方法全部通过他实现

每当我们需要进行跳转的时候,我们只需要给需要跳转的控制器的转场代理和转场方式,并且创建一个动画代理实现代理方法,在转场动画的代理方法中实现不同的动画即可根据自定义的方法进行跳转了。

思路扩展

如果一个项目需要比较多的modal跳转,或者需要酷炫的自定义modal跳转。如果每次都要自己去根据上面的思路图实现各种协议方法是一件十分费力的事情。 
由于上面分解出来了一个动画代理,因此该代理就可以进行封装和重用。

封装框架

目标:

控制器提供需要跳转的控制器,提供跳转控制器显示的大小位置(frame),根据提供的显示动画和消失动画即可实现自定义跳转。

目的:

完全不关心内部代理方法的实现,系统调用的方式,只需要提供自定义的内容来完成自定义跳转。

实现

做一个ViewController的分类,提供类似跳转的方法。

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 *  自定义modal的跳转方式
 *
 * <span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> modalVC          需要展示的viewController
 * <span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> presentFrame     展示视图在屏幕的frame
 * <span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> presentAnimation 展示动画代码(返回的时间是转场动画上下文关闭的时间)
 * <span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> dismissAnimation 消失动画代码(返回的时间是转场动画上下文关闭的时间)
 关于转场动画上下文时长说明:
 转场动画上下文关闭的时间决定了改转场动画封锁界面的用户交互能力的时长,如果返回0表示立马接受用户交互,
 那么可能存在在动画过程中用户交互而导致动画达不到预期效果。
 一般建议返回动画的时间长度,正好动画结束,然后开启用户交互能力。
 特殊需求可以填写特殊时长
 * <span class="hljs-javadoctag" style="color: rgb(102, 0, 102); box-sizing: border-box;"> @param</span> completion       完成回调
 */</span>
-(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)mk_presentViewController:(UIViewController *)modalVC
               withPresentFrame:(CGRect)presentFrame
           withPresentAnimation:(NSTimeInterval (^)(UIView *view))presentAnimation
           withDismissAnimation:(NSTimeInterval (^)(UIView *view))dismissAnimation
                 withCompletion:(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> (^)(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>))completion;
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li></ul>

再通过运行时动态添加一个属性用于存储执行动画的代理对象

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> *animationDelegateKey = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"animationDelegate"</span>;
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 *  runtime动态加载执行动画的代理属性的set方法
 */</span>
- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)setAnimationDelegate:(ZJAnimationDelegate *)animationDelegate {
    objc_setAssociatedObject(self, animationDelegateKey, animationDelegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 *  runtime动态加载执行动画的代理属性的get方法
 */</span>
- (ZJAnimationDelegate *)animationDelegate {
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> objc_getAssociatedObject(self, animationDelegateKey);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>

代理对象内部存储了跳转需要的动画block,在动画方法内部在恰当的时间调用动画。

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)animateTransition:(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> <UIViewControllerContextTransitioning>)transitionContext
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.isPresenting</span>) {
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIView</span> *view = [transitionContext viewForKey:UITransitionContextToViewKey];
        [[transitionContext containerView] addSubview:view];
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSTimeInterval</span> time = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.presentAnimation</span>(view);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [transitionContext completeTransition:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span>];
        });
    }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">UIView</span> *view = [transitionContext viewForKey:UITransitionContextFromViewKey];
        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSTimeInterval</span> time = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.dismissAnimation</span>(view);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [transitionContext completeTransition:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span>];
            [view removeFromSuperview];
        });
    }
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li></ul>

注意事项

[transitionContext completeTransition:YES]; 
一定要在动画结束之后调用,不然系统任务转场动画没结束,会一直封锁整个界面和用户教育的能力。

所以这句话我是采用用户返回的时间来延迟加载。这句话只要在动画开始之后执行都不影响动画的执行效果,但是如果在动画之前执行会提前开启用户交互能力,因此可能动画会被用户的其他操作而打断。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter提供了一个很好的动画框架,可以轻松地自定义任何动画效果。要自定义pop动画,可以使用`PageRouteBuilder`类来创建自定义路由。 下面是一个示例: ```dart class CustomPageRoute<T> extends PageRoute<T> { CustomPageRoute({ @required this.builder, this.transitionDuration = const Duration(milliseconds: 500), this.opaque = true, this.barrierDismissible = false, this.barrierColor, this.barrierLabel, this.maintainState = true, }); final WidgetBuilder builder; @override final Duration transitionDuration; @override final bool opaque; @override final bool barrierDismissible; @override final Color barrierColor; @override final String barrierLabel; @override final bool maintainState; @override Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { return builder(context); } @override Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) { return SlideTransition( position: Tween<Offset>( begin: Offset(0.0, 1.0), end: Offset.zero, ).animate(animation), child: child, ); } } ``` 这里我们创建了一个`CustomPageRoute`类,它继承自`PageRoute`,并重写了`buildTransitions`方法来定义我们的自定义动画效果。在这个例子中,我们使用`SlideTransition`来实现一个从底部向上滑动的动画效果。你可以根据需要替换为其他动画效果。 使用自定义动画时,可以使用`Navigator.push`来打开路由: ```dart Navigator.push(context, CustomPageRoute(builder: (context) => NextPage())); ``` 这将打开一个名为`NextPage`的路由,并使用我们定义的自定义动画效果进行过渡。同样,你可以使用`Navigator.pop`来关闭当前路由: ```dart Navigator.pop(context); ``` 希望这个例子能帮到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值