iOS7 Day-by-Day : : Day 0 : : UIKit Dynamics
原文:http://www.shinobicontrols.com/blog/posts/2013/09/19/ios7-day-by-day-day-0-uikit-dynamics
随着iOS7的推出,苹果已经明确表现出他们正在推进设备与真实世界的交互。新推出的API之一便是UIKit Dynamics,一个位于UIKit层的2维物理引擎。在这一系列的博客的第0天我们将了解一下UIKit Dynamics,并且建造一个牛顿物理学中的单摆模型。
这一系列文章的代码在github的软件仓库中 -github.com/ShinobiControls/iOS7-day-by-day
物理宇宙
为了模型化真实世界的物体,我们使用UIDynamicBehavior的子类来给符合UIDynamicItem协议的对象添加行为。这些行为的例子包括像重力、碰撞、弹力等概念。尽管你可以自己创建符合UIDynamicItem协议的对象,但重要地是UIView已经这样做了。这些UIDynamicBehavior对象可以组合在一起生成一个行为对象,这个对象包含了所给的一个或一系列对象的所有行为。
一旦给那些动态对象定义了行为,就可以将这些行为提供给一个UIDynamicAnimator对象,UIDynamicAnimator就是这个物理引擎。这个UIDynamicAnimator对象便会计算当考虑到他们的行为时不同的对象之间怎样相互作用。下图展示了UIKit Dynamics世界的整体概念。
创建一个钟摆
回想一下高中学校的场景—在牛顿物理学中学到的最简单的物体之一便是钟摆。我们先创建一个视图代表这个钟摆:
UIView *ballBearing = [[UIView alloc] initWithFrame:CGRectMake(0,0,40,40)];
ballBearing.backgroundColor = [UIColor lightGrayColor];
ballBearing.layer.cornerRadius =10;
ballBearing.layer.borderColor = [UIColor grayColor].CGColor;
ballBearing.layer.borderWidth =2;
ballBearing.center = CGPointMake(200,300);
[self.view addSubview:ballBearing];
现在我们给这个钟摆添加一些行为。我们来创建一个组合的行为来将所有的行为放在一起:
UIDynamicBehavior *behavior = [[UIDynamicBehavior alloc] init];
接下来我们开始给模型添加我们想要的行为—第一个先是重力:
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[ballBearing]];
gravity.magnitude =10;
[behavior addChildBehavior:gravity];
UIGravityBehavior代表物体和地球之间的引力。它有允许你设置引力向量(例如大小和方向)的属性。此处我们增加引力的大小,保持沿着y轴的正方向。
我们需要给钟摆添加的另一个行为是附件行为—附件行为表示的是这个物体吊在哪个细绳上。
CGPoint anchor = ballBearing.center;
anchor.y -=200;
UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:ballBearing attachedToAnchor:anchor];
[behavior addChildBehavior:attachment];
UIAttachmentBehavior实例对象使一个物体固定在一个锚点上或另一个物体上。UIAttachmentBehavior对象有能够控制依附着的那个细绳的行为的属性—指定它的频度、减震、长度。这些的默认值能够保证一个刚性的连接,这个正是我们想要给钟摆设置的。
现在已经给钟摆指定了行为,我们可以再创建一个物理引擎来管理这些。这个引擎定义为一个变量UIDynamicAnimator *_animator;
animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
[_animator addBehavior:behavior];
UIDynamicAnimator代表了这个物体引擎,它被用来模型化动态系统。此处我们创建了它,并指定了参考视图(这个视图用来描述空间领域),并且添加我们创建的那个复合行为。
做好以上这些内容实际上就可以创建一个UIKit Dynamics系统了。但此时运行程序什么也不会发生。因为一开始这个系统处于平衡状态—我们要使系统不稳定以便观察一些运动。
手势代表了行为
我们需要给这个钟摆添加一个手势使用户能够摆弄这个它。
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc]initWithTarget:selfaction:@selector(handleBallBearingPan:)];
[ballBearing addGestureRecognizer:gesture];
使用手势的目的是使钟摆持续的获得力量:
(void)handleBallBearingPan:(UIPanGestureRecognizer *)recognizer
{
//开始手势滑动时就会产生拖拽的力量
if (recognizer.state == UIGestureRecognizerStateBegan) {
if(_userDragBehavior) {
[_animator removeBehavior:_userDragBehavior];
}
_userDragBehavior = [[UIPushBehavior alloc] initWithItems:@[recognizer.view] mode:UIPushBehaviorModeContinuous];
[_animator addBehavior:_userDragBehavior];
}
//使力量和手势移动的距离成比例
_userDragBehavior.pushDirection = CGVectorMake([recognizer translationInView:self].x / 10.f,0);
// 当手势滑动结束时取消小球的’let-go’行为
if (recognizer.state == UIGestureRecognizerStateEnded) {
[_animator removeBehavior:_userDragBehavior];
_userDragBehavior =nil;
}
}
UIPushBehavior表示给物体施加一个简单的线性力。我们使用回调将力施加于钟摆,回调使施加力的操作与钟摆分离开。在手势开始时创建一个UIPushBehavior变量,记得要将它添加到动态动画器中(注:前面创建的引擎)。再设置力的大小与水平移动距离成正比。为了让钟摆摆动在手势滑动结束时移除推行为。
结合多个钟摆
一个牛顿支架摆放着相同的钟摆,这样钟摆直接就能碰撞。
为了使用UIKit Dynamics重新创建这个牛顿支架,我们需要先创建多个钟摆—每个都和上面的创建步骤一样。他们直接要有一些空隙,这样才不是总是碰撞(看一下示例代码了解详情)。
还需要增加一个新的行为,它描述了单摆之间怎样碰撞。现在使用一个变量来存储钟摆NSArray *_ballBearings;:
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithObjects:_ballBearings];
[behavior addChildBehavior:collision];
此处用到了碰撞行为和一系列系统被模型化的物体。碰撞行为也能够被用来模型化物体碰撞边界,例如视图边界和贝塞尔曲线边界。
如果现在运行程序并试图移动一个钟摆,你会发现这个支架并没有表现的和你预想的一样。这是因为当前的碰撞不是弹性的。还需要添加一个特殊的动态行为来指定各种共享的属性:
UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:_ballBearings];
// Elasticity governs the efficiency of the collisions
itemBehavior.elasticity =1.0;
itemBehavior.allowsRotation =NO;
itemBehavior.resistance =0.5;
[behavior addChildBehavior:itemBehavior];
使用UIDynamicItemBehavior来指定碰撞弹性,还有一些其他的属性,例如阻力(类似于空气阻力)和旋转。如果允许旋转的话,可以指定角阻力。UIDynamicItemBehavior也可以设置线速度和角速度,这在匹配手势的速度时很有用。
现在运行程序将会显示一个牛顿支架,它的行为正是你在真实世界期望的。作为扩展你可以研究拖拽钟摆的细绳和滚珠轴承。