如何在iOS7中使用UIKit动力之UICollisionBehavior

原文出处:点击打开链接

1, 掉落(重力)行为--UIGravityBehavior

本文涉及到的WWDC2013 Session有

在本教程中我们将要使用重力和碰撞效果,我们准备创建一个矩形视图,在重力的驱动下向下落,但是这次落到app的下边缘时不时直接消失而是有一个碰撞效果。

2,碰撞行为--UICollisionBehavior

1.Session 206 Getting Started with UIKit Dynamics

开始在Xcode5中创建一个新的“Single View Application”工程:

3,抛掷行为--UIPushBehavior

2.Session 221 Advanced Techniques with UIKit Dynamics

图片 1

4,吸附行为–UIAttachmentBehavior

 

给它取名为iCollision并选择设备为iPhone:

5,迅猛移动弹跳摆动行为 --UISnapBehavior

什么是UIKit动力学(UIKit Dynamics)

图片 2

6,动力元素行为--UIDynamicItemBehavior

其实就是UIKit的一套动画和交互体系。我们现在进行UI动画基本都是使用CoreAnimation或者UIView
animations。而UIKit动力学最大的特点是将现实世界动力驱动的动画引入了UIKit,比如重力,铰链连接,碰撞,悬挂等效果。一言蔽之,即是,将2D物理引擎引入了人UIKit。需要注意,UIKit动力学的引入,并不是以替代CA或者UIView动画为目的的,在绝大多数情况下CA或者UIView动画仍然是最优方案,只有在需要引入逼真的交互设计的时候,才需要使用UIKit动力学它是作为现有交互设计和实现的一种补充而存在的。

在ViewController.h中添加两个属性,第一个是矩形视图,第二个是动态动画(它的项包含我们需要的动画)

一 ,掉落(重力)行为--UIGravityBehavior

 

@property (nonatomic, weak) IBOutlet UIView *rettangolo;

常用方法:

目的当然是更加自然和炫目的UI动画效果,比如模拟现实的拖拽和弹性效果,放在以前如果单用iOS
SDK的动画实现起来还是相当困难的,而在UIKit
Dynamics的帮助下,复杂的动画效果可能也只需要很短的代码(基本100行以内…其实现在用UIView
animation想实现一个不太复杂的动画所要的代码行数都不止这个数了吧)。总之,便利多多,配合UI交互设计,以前很多不敢想和不敢写(至少不敢自己写)的效果实现起来会非常方便,也相信在iOS7的时代各色使用UIKit动力学的应用的在动画效果肯定会上升一个档次。

@property (nonatomic) UIDynamicAnimator* animator;

– (void)addItem:(id)item;

 

现在,我们转到Main.storyboard去创建一个矩形视图并将这个视图连接到我们之前在ViewController.h中创建的属性

– (void)removeItem:(id)item;

那么,应该怎么做呢?

设定矩形视图的大小:

– (void)setAngle:(CGFloat)angle magnitude:(CGFloat)magnitude;

 

Width: 300 Height: 100

添加或者删除对象组

UIKit动力学实现的结构

改变该视图的背景颜色,任何你喜欢的颜色只要不是白色的。

常用属性:

为了实现动力UI,需要注册一套UI行为的体系,之后UI便会按照预先的设定进行运动了。我们应该了解的新的基本概念有如下四个:

做了上面这些步骤之后的效果是:

CGVector gravityDirection;重力方向(是一个二维向量)默认值
(0,1),代数几何中的方向向量,x,y所成角度即是重力掉落方向

 

图片 3

CGFloat
angle;重力方向(是一个角度,以x轴正方向为0°,顺时针正数,逆时针负数)

UIDynamicItem:用来描述一个力学物体的状态,其实就是实现了UIDynamicItem委托的对象,或者抽象为有面积有旋转的质点;

现在我们又到ViewController.m的viewDidLoad写下如下代码初始化Dynamic
Animator:

-360-360

UIDynamicBehavior:动力行为的描述,用来指定UIDynamicItem应该如何运动,即定义适用的物理规则。一般我们使用这个类的子类对象来对一组UIDynamicItem应该遵守的行为规则进行描述;

_animator = [[UIDynamicAnimator
alloc]initWithReferenceView:self.view];

CGFloat magnitude;量级(用来控制加速度,1.0代表加速度是1000 points
/second²)

UIDynamicAnimator:动画的播放者,动力行为(UIDynamicBehavior)的容器,添加到容器内的行为将发挥作用;

添加并定义“重力”:

具体使用是这样的:gravityDirection
二维向量确定向量的方向,在确定方向后,根据angle 的
大小决定下落方向,magnitude决定下落的加速度大小。

ReferenceView:等同于力学参考系,如果你的初中物理不是语文老师教的话,我想你知道这是啥..只有当想要添加力学的UIView是ReferenceView的子view时,动力UI才发生作用。

UIGravityBehavior* gravityBeahvior = [[UIGravityBehavior alloc]
initWithItems:@[self.rettangolo]];

.h文件

 

添加并定义“碰撞”,连接到之前创建的矩形视图:

@property (nonatomic, strong) UIView *squareView;

光说不练假把式,来做点简单的demo吧。比如为一个view添加重力行为:

UICollisionBehavior* collisionBehavior = [[UICollisionBehavior
alloc] initWithItems:@[self.rettangolo]];

@property (nonatomic, strong) UIDynamicAnimator *animator;

 

collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;

.m文件

  1. – (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.   
  4.     UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(100, 50, 100, 100)]; 
  5.     aView.backgroundColor = [UIColor lightGrayColor]; 
  6.     [self.view addSubview:aView]; 
  7.   
  8.     UIDynamicAnimator* animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; 
  9.     UIGravityBehavior* gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:@[aView]]; 
  10.     [animator addBehavior:gravityBeahvior]; 
  11.     self.animator = animator; 

UIDynamicItemBehavior* propertiesBehavior = [[UIDynamicItemBehavior
alloc] initWithItems:@[self.rettangolo]];

/*重力效果*/

 

propertiesBehavior.elasticity = 0.7;

self.squareView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80,
80)];

 

除了初始化各种行为,我们还要为每个添加了的效果定义属性

self.squareView.backgroundColor = [UIColor yellowColor];

代码很简单:

至于collisionBehavior,要定义一个碰撞边界,指示之前定义的碰撞行为在此边界处发生。

self.squareView.center = self.view.center;

 

至于涉及item的propertiesBehavior行为,我们正用它设置矩形视图的弹性属性。

[self.view addSubview:self.squareView];

1.以现在ViewController的view为参照系(ReferenceView),来初始化一个UIDynamicAnimator。

现在我们只将我们创建的三个“行为”添加到“_animator”:

UIGravityBehavior *gravity = [[UIGravityBehavior alloc]
initWithItems:@[self.squareView]];

 

[_animator addBehavior:propertiesBehavior];

gravity.gravityDirection = CGVectorMake(0, 1);

2.然后分配并初始化一个动力行为,这里是UIGravityBehavior,将需要进行物理模拟的UIDynamicItem传入。UIGravityBehavior的initWithItems:接受的参数为包含id的数组,另外UIGravityBehavior实例还有一个addItem:方法接受单个的id。就是说,实现了UIDynamicItem委托的对象,都可以看作是被力学特性影响的,进而参与到计算中。UIDynamicItem委托需要我们实现bounds,center和transform三个属性,在UIKit
Dynamics计算新的位置时,需要向Behavior内的item询问这些参数,以进行正确计算。iOS7中,UIView和UICollectionViewLayoutAttributes已经默认实现了这个接口,所以这里我们直接把需要模拟重力的UIView添加到UIGravityBehavior里就行了。

[_animator addBehavior:gravityBeahvior];

gravity.angle = 45;

 

[_animator addBehavior:collisionBehavior];

gravity.magnitude = 5.0;

3.把配置好的UIGravityBehavior添加到animator中。

现在,如果运行程序,我们将会看到模拟器中矩形视图在向下落,在停在程序底部之前还要反弹几次。

self.animator = [[UIDynamicAnimator alloc]
initWithReferenceView:self.view];

 

图片 4图片 5图片 6图片 7

[self.animator addBehavior:gravity];

4.strong持有一下animator,避免当前scope结束被ARC释放掉(后果当然就是UIView在哪儿傻站着不掉)

在ViewController.m中加入以下代码,我们还可以在矩形视图碰撞的时候改变颜色:

二,碰撞行为--UICollisionBehavior

 

-(void)collisionBehavior:(UICollisionBehavior *)behavior
beganContactForItem:(id)item withBoundaryIdentifier:(id)identifier
atPoint:(CGPoint)p { [(UIView*)item setBackgroundColor:[UIColor
blackColor]]; } -(void)collisionBehavior:(UICollisionBehavior
*)behavior endedContactForItem:(id)item
withBoundaryIdentifier:(id)identifier { [(UIView*)item
setBackgroundColor:[UIColor grayColor]]; }

常用的方法

运行结果,view开始受重力影响了:

效果是碰撞开始发生时矩形视图是黑色的,碰撞结束时视图是灰色的。要使这段代码运行起来,需要在ViewController.h中添加碰撞的代理collisionDelegate

– (instancetype)initWithItems:(NSArray *)items;

 

@interface ViewController : UIViewController

– (void)addItem:(id)item;

图片 8

然后在ViewController.m的ViewDidLoad函数最后添加:

– (void)removeItem:(id)item;

重力作用下的UIview

collisionBehavior.collisionDelegate = self;

@property (nonatomic, readonly, copy) NSArray* items;

碰撞,我要碰撞

现在编译运行:

– (void)removeAllBoundaries;

没有碰撞的话,物理引擎就没有任何意义了。和重力行为类似,碰撞也有一个UIDynamicBehavior子类来描述碰撞行为,即UICollisionBehavior。在上面的demo中加上几句:

图片 9图片 10图片 11图片 12

参数分析:

 

本文源码下载

UICollisionBehaviorMode collisionMode;

  1. – (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.  
  4.     UIView *aView = [[UIView alloc] initWithFrame:CGRectMake(100, 50, 100, 100)]; 
  5.     aView.backgroundColor = [UIColor lightGrayColor]; 
  6.     [self.view addSubview:aView]; 
  7.  
  8.     UIDynamicAnimator* animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; 
  9.     UIGravityBehavior* gravityBeahvior = [[UIGravityBehavior alloc] initWithItems:@[aView]]; 
  10.     [animator addBehavior:gravityBeahvior]; 
  11.  
  12.     UICollisionBehavior* collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[aView]]; 
  13.     collisionBehavior.translatesReferenceBoundsIntoBoundary = YES; 
  14.     [animator addBehavior:collisionBehavior]; 
  15.     collisionBehavior.collisionDelegate = self; 
  16.  
  17.     self.animator = animator; 

在本教程中我们将要使用重力和碰撞效果,我们准备创建一个矩形视图,在重力的驱动下向下落,但是这次落到…

有以下三个参数

 

UICollisionBehaviorModeItems        = 1 << 0,

也许聪明的你已经看到了,还是一样的,创建新的行为规则(UICollisionBehavior),然后加到animator中…唯一区别的地方是碰撞需要设定碰撞边界范围translatesReferenceBoundsIntoBoundary将整个参照view(也就是self.view)的边框作为碰撞边界(另外你还可以使用setTranslatesReferenceBoundsIntoBoundaryWithInsets:这样的方法来设定某一个区域作为碰撞边界,更复杂的边界可以使用addBoundaryWithIdentifier:forPath:来添加UIBezierPath,或者addBoundaryWithIdentifier:fromPoint:toPoint:来添加一条线段为边界,详细地还请查阅文档);另外碰撞是有回调的,可以在self中实现UICollisionBehaviorDelegate。

   设置这个参数说明碰撞会在dynamic items之间检测

 

UICollisionBehaviorModeBoundaries  = 1 << 1,

最后,只是直直地掉下来的话未免太无聊了,加个角度吧:

  设置这个参数说明碰撞在我们设置的边界上检测

 

UICollisionBehaviorModeEverything  = NSUIntegerMax

  1. aView.transform = CGAffineTransformRotate(aView.transform, 45); 

  设置这个参数说明检测一切的碰撞,不考虑items,边界,或者其他

结果是这样的,帅死了…这在以前只用iOS SDK的话,够写上很长时间了吧..

这三个值可以使用 | 组合使用

 

BOOL
translatesReferenceBoundsIntoBoundary;是否以参照视图的bounds为边界

图片 13


(void)setTranslatesReferenceBoundsIntoBoundaryWithInsets:(UIEdgeInsets)insets;
与上一个参数一致,设置EdgeInsets

碰撞和重力同时作用的动力UI

– (void)addBoundaryWithIdentifier:(id)identifier
forPath:(UIBezierPath*)bezierPath;

 

– (void)addBoundaryWithIdentifier:(id)identifier fromPoint:(CGPoint)p1
toPoint:(CGPoint)p2;

碰撞的delegate可以帮助我们了解碰撞的具体情况,包括哪个item和哪个item开始发生碰撞,碰撞接触点是什么,是否和边界碰撞,和哪个边界碰撞了等信息。这些回调方法保持了Apple一向的命名原则,所以通俗易懂。需要多说一句的是回调方法中对于ReferenceView的Bounds做边界的情况,BoundaryIdentifier将会是nil,自行添加的其他边界的话,ID自然是添加时指定的ID了。

– (UIBezierPath*)boundaryWithIdentifier:(id)identifier;

1.–
collisionBehavior:beganContactForItem:withBoundaryIdentifier:atPoint:

– (void)removeBoundaryWithIdentifier:(id)identifier;

2.– collisionBehavior:beganContactForItem:withItem:atPoint:

自定义路线,要么使用 UIBezierPath,要么自定义point

3.– collisionBehavior:endedContactForItem:withBoundaryIdentifier:

@property (nonatomic, assign, readwrite) id collisionDelegate;

4.– collisionBehavior:endedContactForItem:withItem:

设置Contact不同状态下需要自定义的行为,

 

代码如下:

其他能实现的效果

.h文件

除了重力和碰撞,iOS
SDK还预先帮我们实现了一些其他的有用的物理行为,它们包括:

@property (nonatomic, strong) NSMutableArray *squareViews;

1.UIAttachmentBehavior
描述一个view和一个锚相连接的情况,也可以描述view和view之间的连接。attachment描述的是两个点之间的连接情况,可以通过设置来模拟无形变或者弹性形变的情况(再次希望你还记得这些概念,简单说就是木棒连接和弹簧连接两个物体)。当然,在多个物体间设定多个;UIAttachmentBehavior,就可以模拟多物体连接了..有了这些,似乎可以做个老鹰捉小鸡的游戏了-
-…

@property (nonatomic, strong) UIDynamicAnimator *animator;

 

.m文件

2.UISnapBehavior
将UIView通过动画吸附到某个点上。初始化的时候设定一下UISnapBehavior的initWithItem:snapToPoint:就行,因为API非常简单,视觉效果也很棒,估计它是今后非游戏app里会被最常用的效果之一了;

– (void) viewDidAppear:(BOOL)animated-内

 

/* 碰撞效果 */

3.UIPushBehavior
可以为一个UIView施加一个力的作用,这个力可以是持续的,也可以只是一个冲量。当然我们可以指定力的大小,方向和作用点等等信息。

self.squareViews = [[NSMutableArray alloc] initWithCapacity:5];

 

NSArray *colors = @[[UIColor greenColor],[UIColor yellowColor]];

4.UIDynamicItemBehavior
其实是一个辅助的行为,用来在item层级设定一些参数,比如item的摩擦,阻力,角阻力,弹性密度和可允许的旋转等等。

CGPoint currentCenterPoint = self.view.center;

 

CGSize eachViewSize = CGSizeMake(50, 50);

UIDynamicItemBehavior有一组系统定义的默认值:

for (int counter = 0; counter < 2; counter ++) {

1.allowsRotation YES

UIView *newView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,
eachViewSize.width, eachViewSize.height)];

2.density 1.0

newView.backgroundColor = colors[counter];

3.elasticity 0.0

newView.center = currentCenterPoint;

4.friction 0.0

currentCenterPoint.y += eachViewSize.height +10;

5.resistance 0.0

[self.view addSubview:newView];

 

[self.squareViews addObject:newView];

所有的UIDynamicBehavior都是可以独立作用的,同时作用时也遵守力的合成。也就是说,组合使用行为可以达到一些较复杂的效果。举个例子,希望模拟一个drag物体然后drop后下落的过程,可以用如下代码:

}

 

self.animator = [[UIDynamicAnimator alloc]
initWithReferenceView:self.view];

  1. – (void)viewDidLoad 
  2.     [super viewDidLoad]; 
  3.   
  4.     UIDynamicAnimator* animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; 
  5.     UICollisionBehavior* collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[self.square1]]; 
  6.   
  7.     collisionBehavior.translatesReferenceBoundsIntoBoundary = YES; 
  8.     [animator addBehavior:collisionBehavior]; 
  9.   
  10.     UIGravityBehavior *g = [[UIGravityBehavior alloc] initWithItems:@[self.square1]]; 
  11.     [animator addBehavior:g]; 
  12.   
  13.     self.animator = animator; 
  14.   
  15.   
  16. -(IBAction)handleAttachmentGesture:(UIPanGestureRecognizer*)gesture 
  17.     if (gesture.state == UIGestureRecognizerStateBegan){ 
  18.   
  19.         CGPoint squareCenterPoint = CGPointMake(self.square1.center.x, self.square1.center.y – 100.0); 
  20.         CGPoint attachmentPoint = CGPointMake(-25.0, -25.0); 
  21.   
  22.         UIAttachmentBehavior* attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:self.square1 point:attachmentPoint attachedToAnchor:squareCenterPoint]; 
  23.   
  24.         self.attachmentBehavior = attachmentBehavior; 
  25.         [self.animator addBehavior:attachmentBehavior]; 
  26.   
  27.     } else if ( gesture.state == UIGestureRecognizerStateChanged) { 
  28.   
  29.         [self.attachmentBehavior setAnchorPoint:[gesture locationInView:self.view]]; 
  30.   
  31.     } else if (gesture.state == UIGestureRecognizerStateEnded) { 
  32.         [self.animator removeBehavior:self.attachmentBehavior]; 
  33.     } 

/*create gravity*/

 

UIGravityBehavior *gravity = [[UIGravityBehavior alloc]
initWithItems:self.squareViews];

 

[self.animator addBehavior:gravity];

viewDidiLoad时先在现在环境中加入了重力,然后监测到pan时附加一个UIAttachmentBehavior,并在pan位置更新更新其锚点,此时UIAttachmentBehavior和UIGravityBehavior将同时作用(想象成一根木棒连着手指处和view)。在手势结束时将这个UIAttachmentBehavior移除,view将在重力作用下下落。整个过程如下图:

/* create collision */

 

UICollisionBehavior *collision = [[UICollisionBehavior alloc]
initWithItems:self.squareViews];

图片 14

//    collision.translatesReferenceBoundsIntoBoundary = YES;

Drag & Drop

collision.collisionMode =
UICollisionBehaviorModeBoundaries|UICollisionBehaviorModeItems|UICollisionBehaviorModeEverything;

UIKit力学的物理学分析

collision.collisionDelegate = self;

既然是力学,那显然各种单位是很重要的。在现实世界中,理想情况下物体的运动符合牛顿第二运动定理,在国际单位制中,力的单位是牛顿(N),距离单位是米(m),时间单位是秒(s),质量单位是千克(kg)。根据地球妈妈的心情,我们生活在这样一套体制中:重力加速度约为9.8m/s2
,加速度的单位是m/s2 ,速度单位是m/s,牛顿其实是kg·m/s2
,即1牛顿是让质量为1千克的物体产生1米每二次方秒的加速度所需要的力。

[collision

 

addBoundaryWithIdentifier:kBottomIdentifier

以上是帮助您回忆初中知识,而现在这一套体系在UIKit里又怎么样呢?这其实是每一个物理引擎都要讨论和明白的事情,UIKit的单位体制里由于m这个东西太过夸张,因此用等量化的点(point,之后简写为p)来代替。具体是这样的:UI重力加速度定义为1000p/s2
,这样的定义有两方面的考虑,一时为了简化,好记,确实1000比9.8来的只观好看,二是也算符合人们的直感:一个UIView从y=0开始自由落体落到屏幕底部所需的时间,在3.5寸屏幕上为0.98秒,4寸屏幕上为1.07秒,1秒左右的自由落体的视觉效果对人眼来说是很舒服能进行判断的。

fromPoint:CGPointMake(0.0f, self.view.bounds.size.height – 100.0f)

 

toPoint:CGPointMake(self.view.bounds.size.width,

那么UIView的质量又如何定义呢,这也是很重要的,并涉及到力作用和加速度下UIView的表现。苹果又给出了自己的“UIKit牛顿第二定律”,定义了1单位作用力相当于将一个100px100p的默认密度的UIView以100p/s2
的加速度移动。这里需要注意默认密度这个假设,因为在UIDynamicItem的委托中并没有实现任何密度相关的定义,而是通过UIDynamicItemBehavior来附加指定的。默认情况下,密度值为1,这就相当于质量是10000单位的UIView在1单位的作用力下可以达到1/10的UI重力加速度。

self.view.bounds.size.height – 100.0f)];

 

[self.animator addBehavior:collision];

这样类比之后的结论是,如果将1单位的UI力学中的力等同于1牛顿的话:

#pragma mark UICollisionViewDelegate 

1.1000单位的UI质量,与现实世界中1kg的质量相当,即一个点等同一克;

– (void)collisionBehavior:(UICollisionBehavior *)behavior
beganContactForItem:(id)item withBoundaryIdentifier:(id)identifier
atPoint:(CGPoint)p

2.屏幕的100像素的长度,约和现实世界中0.99米相当(完全可以看为1米)

{

3.UI力学中的默认密度,约和现实世界的0.1kg/m2 相当

Copyright @ 2015-2019 ca88 版权所有
网站地图xml地图