接着上一篇文章,这里整理一下UIMotionEffect的姊妹篇,说说iOS7中的UIDynamics和物理效果。做过游戏开发、用过游戏开发引擎的朋友们可能对Box2D、chipmunk并不陌生,他们可以使你开发出来的游戏具有各种物理效果。在iOS7中,也有类似的工具,不过它不是物理引擎,他是UIKit提供的非常方便的API,UIDynamic。
0. UIDynamic概述
在iOS7的锁屏页,在右下角可以看到拍照的按钮,拖动这个按钮垂直向上,则照相的应用会直接打开。假如我们向上推到一半,松开,页面会沿着屏幕垂直向下做“自由落体”运动。或者,更进一步,我们用力向下回滑已经抬起的锁屏页面,页面会加速滑下并和页面底部边缘撞击反弹。如下图所示:
滑动锁屏页时的Dynamic效果
在这个过程中,实际上至少涉及了两类Dynamic效果,“重力”和“碰撞”。
我们来看一下UIDynamic的几个主要概念:
Behavior 。直接翻译过来就是“行为”,实际上也就是UIDynamic的物理效果。这个对象定义了一类效果,包括效果的具体属性。
Dynamic Item 。动态效果项目,也就是实际中应用了效果的对象,通常是UIView对象,比如上图例子中的锁屏页View。
Animator 。“动画绘制者”,实际上就是动画效果发挥作用的上下文,类似于物理引擎中物理效果的作用空间。这个通常也和一个View对应。
下面单独介绍下UIDynamicBehavior和UIDynamicAnimator,也看看这几个主要对象是怎么结合起来的。
1. UIDynamicBehavior和UIDynamicAnimator
按照上面的介绍,UIDynamicBehavior和UIDynamicAnimator就是UIDynamic中最重要的两个角色,一个代表着具体的物理效果,另一个则是这个效果展示的上下文。是的,没错,UIDynamicAnimator中也刚好有一个我们期待的,最重要的方法,那就是:
- (void)addBehavior:(UIDynamicBehavior *)behavior;
这样,效果和展示的上下文就关联了起来。就是这么简单!
那么问题来了,这两个对象和传统的UIKit中的其它类的对象,比如UIView是什么关系呢?我们回头再来看UIDynamicBehavior和UIDynamicAnimator这两个类的文档或头文件,发现其声明非常简单,都是直接继承自NSObject,方法也不多。
在UIDynamicAnimator的头文件中,给了这样一个初始化方法:
- (instancetype)initWithReferenceView:(UIView*)view;
这个方法的参数是一个UIView,我们叫做ReferenceView。无论是那种物理效果,其运动都是相对于一个上下文的,这个上下文就是我们这里的“参照视图”,ReferenceView。比如锁屏效果的例子,我们可以认为这个ReferenceView就是占据整个屏幕的rootView。
除了这个ReferenceView以外,就是具体发生物理效果的对象,在UIDynamic中我们叫做Item。这个Item的定义有一个protocol,即UIDynamicItem,不过通常情况下它就是一个具体的View,所以我们看到UIView在新版本的API中也有了<UIDynamicItem>这样一个声明。
虽然在UIDynamicBehavior中并没有直接提供类似ReferenceView参数的初始化方法,不过UIDynamicBehavior的各个子类几乎都提供了类似的方法:
- (instancetype)initWithItems:(NSArray *)items;
这样UIKit中传统的View对象就和UIDynamicAnimator、UIDynamicBehavior关联了起来,一起实现了UIDynamic物理效果。
2. Gravity和Collision效果
UIDynamicBehavior有很多子类,或者说iOS7中提供了很多具体的Dynamic效果。我们这里就简单以Gravity和Collision效果为例,介绍一下。
Gravity不管翻译为“重力”还是“万有引力”,效果其实都可以认为是一致的,结果是类似落体的加速运动,而最重要的参数是受力的方向和大小。
animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[itemView]]; CGVector gravityDirection = {0.0, -1.0}; [gravity setGravityDirection:gravityDirection]; [animator addBehavior:gravity];
像这样,牛顿的苹果就向上飞而不是向下落了。
类似的,碰撞反弹的效果也比较简单:
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[ itemView ]]; [collision setTranslatesReferenceBoundsIntoBoundary:YES]; [animator addBehavior:collision]
这是最简单的碰撞效果。如果想做得更漂亮一些,可以考虑用UIDynamicItemBehavior来设置一些通用的物理属性,比如弹性等。
另外,如果在碰撞过程中的各个环节想做定制处理,可以考虑使用UICollisionBehavior的delegate。
3. 其它
除了上面提到的Gravity和Collision外,还有Attach、Push等UIDynamic物理效果。
由于UIDynamic是iOS7的新特性,有很多兼容性考虑比较多的App并没有大范围使用。关于UIDynamic的文档苹果也提供的不是很多。在WWDC 2013的Session中有两篇做了介绍。
在我整理本文时,看到github上 这里 有一个完整的例子不错,大家感兴趣的还可以去看一下。