iOS动効-利用POP动画实现卡片切换动画
pbfp3369
8年前
<p style="text-align:center"><img src="https://simg.open-open.com/show/8e2d51a2d96ccb5f074614a821501413.gif"></p> <p>Pop is an extensible animation engine for iOS and OS X. In addition to basic static animations, it supports spring and decay dynamic animations, making it useful for building realistic, physics-based interactions. The API allows quick integration with existing Objective-C codebases and enables the animation of any property on any object. It's a mature and well-tested framework that drives all the animations and transitions in Paper.</p> <p>以上是pop动画在github上的官方解释,大体上的意思是pop是一个extensible(可扩展)的动画引擎,提供基础的Basic 静态动画以及支持弹簧和衰减动画,用来构建高可用性的真实、物理特性的交互体验,使用oc作为基础,可用户扩展到任何的oc的Object的属性,是一个非常易于测试的框架,并且在非死book自家的Paper上应用。</p> <p>pop首先是一个动画引擎,那本质上不再是基于apple的CoreAnimation框架的,是自己实现的一套跟CoreAnimation一样的动画引擎框架,内部使用了CADisplayer的每秒60帧高素质的渲染技术,充分使用了GPU的能力,性能表现很乐观,而能用作任何基于NSObject上,又增加了扩展性,比如说UIView背景颜色的动态切换,比如说声音的渐隐渐显等等,这是对CA不足的充分补充,而引出的physics特性,更引发了apple后来推出了UIDynamic(UIKit动力学)来弥补iOS平台上的物理特性动画的不足,也大大简化了动画的开发难度。POP 使用 Objective-C++ 编写,Objective-C++ 是对 C++ 的扩展,就像 Objective-C 是 C 的扩展。而至于为什么他们用 Objective-C++ 而不是纯粹的 Objective-C,原因是他们更喜欢 Objective-C++ 的语法特性所提供的便利。那我们弄清楚了pop的本质,就开始介绍一下这个框架。</p> <p>基本的pop动画的使用我就不再赘述,很多文章都有介绍,我在刚接触的pop的时候有很多疑问,主要是在实际运用的时候出现的代码实践的问题,因为之前一直是用CA的,总会用CA的用法来寻找pop的使用技巧,那么就有以下几个问题要搞清楚:</p> <p>1.具有多个动画的联合动画如何实现,CA中有CAAnimationGroup的概念,能实现动画组,pop中该怎么办?</p> <p>2.有没有重复播放动画的概念,因为有些动画是必须重复播放的。</p> <p>3.在现在大量使用autolayout的布局的时候,如何使用pop动画</p> <p>结合以上的问题,我自己做了一个卡片切换动画的Demo来说明一下。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/81bc2c2e77f63a08a175442828d6e49b.gif"></p> <p>卡片切换的交互动画</p> <p>卡片设计比较粗糙,没有设置卡片上明确的细小的分栏和文字切换,只是简单的模仿了卡片切换和卡片上圆环的切换。</p> <p>动画组的概念,这个问题在做的时候不纠结了,因为pop提供pop_addAnimation这样的API,实际上要想实现动画组概念,在实践中就将生产Animation的代码单独封装成函数,然后多次调用pop_addAnimation就好了。比方说,我就会这么干</p> <p>-(void)setCenter:(CGPoint)center Duration:(CGFloat)duration Card:(cardView *)card Index:(NSUInteger)index{</p> <p>POPBasicAnimation * bAni = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter];</p> <p>bAni.toValue = [NSValue valueWithCGPoint:center];</p> <p>bAni.duration = duration;</p> <p>[bAni setCompletionBlock:^(POPAnimation *ani, BOOL is) {</p> <p>if (is) {</p> <p>card.hidden = NO;</p> <p>}</p> <p>}];</p> <p>[card pop_addAnimation:bAni forKey:@"center"];</p> <p>}</p> <p>-(void)setScaleWithScalePercent:(CGFloat) percent Duration:(CGFloat)duration Card:(cardView *)card{</p> <p>POPBasicAnimation * bAni = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY];</p> <p>bAni.toValue = [NSValue valueWithCGSize:CGSizeMake(percent, percent)];</p> <p>bAni.duration = duration;</p> <p>[card.layer pop_addAnimation:bAni forKey:@"123"];</p> <p>}</p> <p>-(void)setRorationWithAngle:(CGFloat)angele Duration:(CGFloat)duration Card:(cardView *)card{</p> <p>POPBasicAnimation * bAni = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerRotation];</p> <p>bAni.duration = duration;</p> <p>bAni.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];</p> <p>bAni.toValue = [NSNumber numberWithFloat:angele];</p> <p>[card.layer pop_addAnimation:bAni forKey:@"213"];</p> <p>}</p> <p>将上述对cardView增加动画的方法写好,然后多次调用</p> <p>[self setScaleWithScalePercent:scalePercent Duration:0.0001f Card:card];</p> <p>[self setRorationWithAngle:rotation Duration:0.001f Card:card];</p> <p>对于重复的动画使用,可以利用如下:</p> <p>-(void)performAnimation</p> <p>{</p> <p>[self setAnimationWithBounciness:self.bouncinessSlider.value andSpeed:self.speedSlider.value];</p> <p>}</p> <p>定义好执行方法,在completionBlock中重复调用执行动画的方法就Ok,当然,你也可以对pop动画对象再封装,实现重复次数。</p> <p>anim.completionBlock = ^(POPAnimation *anim, BOOL finished)</p> <p>{</p> <p>if (finished) {</p> <p>[self performAnimation];</p> <p>}</p> <p>};</p> <p>对于autolayout的动画</p> <p>有人写过这样的例子,以下是代码,pop是支持更新layout的约束的,这对更新具体某个约束是比较有效的,而如果你在做某个类似翻转动画的时候,必定要用到layer层动画,那单单靠更新约束就不太能满足要求,但是恰好你在使用Pop的layer层动画的时候,该layer属于的View上有用约束建立的控件,就比较难以处理,我个人的意见是,做layer层的动画,那上面的其他控件干脆也用frame布局。</p> <p>POPAnimatableProperty *constantProperty = [POPAnimatableProperty propertyWithName:@"constant" initializer:^(POPMutableAnimatableProperty *prop){</p> <p>prop.readBlock = ^(NSLayoutConstraint *layoutConstraint, CGFloat values[]) {</p> <p>values[0] = [layoutConstraint constant];</p> <p>};</p> <p>prop.writeBlock = ^(NSLayoutConstraint *layoutConstraint, const CGFloat values[]) {</p> <p>[layoutConstraint setConstant:values[0]];</p> <p>};</p> <p>}];</p> <p>POPSpringAnimation *constantAnimation = [POPSpringAnimation animation];</p> <p>constantAnimation.property = constantProperty;</p> <p>constantAnimation.fromValue = @(_layoutConstraint.constant);</p> <p>constantAnimation.toValue = @(200);</p> <p>[_layoutConstraint pop_addAnimation:constantAnimation forKey:@"constantAnimation"];</p> <p>以上就是一切使用问题的总结,详细的使用可以参考源码。</p> <p> </p> <p> </p>