UIDynamic(物理仿真)
简介
- 什么是UIDynamic
- UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架
- 可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象
- 如: 重力、弹性碰撞等现象
- 物理引擎的价值
- 广泛用于游戏开发,经典成功案例是“愤怒的小鸟”
- 让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果
- 提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏
- 知名的2D物理引擎
- Box2d
- Chipmunk
三大概念
-
三大概念
- 物理仿真元素(Dynamic Item)
- 谁要进行物理仿真?
- 物理仿真行为(Dynamic Behavior)
- 执行怎样的物理仿真效果?怎样的动画效果
- 物理仿真器(Dynamic Animator)
- 让物理仿真元素执行具体的物理仿真行为
- 物理仿真元素(Dynamic Item)
-
物理仿真元素
- 注意
- 不是任何对象都能做物理仿真元素
- 不是任何对象都能进行物理仿真
- 哪些对象才能做物理仿真元素
- 任何遵守了UIDynamicItem协议的对象
- UIView默认已经遵守了UIDynamicItem协议,因此任何UI控件都能做物理仿真
- UICollectionViewLayoutAttributes类默认也遵守UIDynamicItem协议
- 注意
-
物理仿真行为
- UIDynamic提供了以下几种物理仿真行为
UIGravityBehavior:重力行为 UICollisionBehavior:碰撞行为 UISnapBehavior:捕捉行为 UIPushBehavior:推动行为 UIAttachmentBehavior:附着行为 UIDynamicItemBehavior:动力元素行为
- 物理仿真行为须知
- 上述所有物理仿真行为都继承自UIDynamicBehavior
- 所有的UIDynamicBehavior都可以独立进行
- 组合使用多种行为时,可以实现一些比较复杂的效果
- UIDynamic提供了以下几种物理仿真行为
-
物理仿真器
-
物理仿真器须知
- 它可以让物理仿真元素执行物理仿真行为
- 它是UIDynamicAnimator类型的对象
-
UIDynamicAnimator的初始化
- - (instancetype)initWithReferenceView:(UIView )view;
- view参数:是一个参照视图,表示物理仿真的范围
-
UIDynamicAnimator的常见方法
- (void)addBehavior:(UIDynamicBehavior *)behavior; 添加1个物理仿真行为 - (void)removeBehavior:(UIDynamicBehavior *)behavior; 移除1个物理仿真行为 - (void)removeAllBehaviors; 移除之前添加过的所有物理仿真行为
-
UIDynamicAnimator的常见属性
@property (nonatomic, readonly) UIView* referenceView; 参照视图 @property (nonatomic, readonly, copy) NSArray* behaviors; 添加到物理仿真器中的所有物理仿真行为 @property (nonatomic, readonly, getter = isRunning) BOOL running; 是否正在进行物理仿真 @property (nonatomic, assign) id <UIDynamicAnimatorDelegate> delegate; 代理对象(能监听物理仿真器的仿真过程,比如开始和结束)
-
使用步骤
-
创建一个物理仿真器(顺便设置仿真范围)
-
创建相应的物理仿真行为(顺便添加物理仿真元素)
-
将物理仿真行为添加到物理仿真器中 -> 开始仿真
Demo
-
- 环境准备
- 创建项目
- 设置不使用autolayout(这里仅仅是为了简单,物理仿真支持autolayout,在愤怒的小鸟中演示)
- 设置屏幕的尺寸为5.5 因为Xcode7 默认使用是iPhone6 s Plus
- 拖入一个View,颜色设置蓝色,大小设置为100,100
- 连线到控制器中,属性为blueView
-
让蓝色View执行仿真重力仿真行为,代码如下:
- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 创建一个物理仿真器,并指定参照系为控制器的View UIDynamicAnimator *dynamicAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; // 创建重力仿真行为并指定仿真元素为蓝色的view UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.blueView]]; // 把仿真行为添加到仿真器中 [dynamicAnimator addBehavior:gravity]; }
点击控制器的View,没有任何反应,这是因为还要没有执行动画物理仿真器就销毁,所以我们需要对物理仿真器有一个强引用 懒加载物理仿真器
-
定义物理仿真器属性
@property (nonatomic, strong) UIDynamicAnimator *dynamicAnimator;
-
懒加载物理仿真器器
- (UIDynamicAnimator *)dynamicAnimator { if (_dynamicAnimator == nil) { // 创建一个物理仿真器,并指定参照系为控制器的View _dynamicAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; } return _dynamicAnimator; }
-
修改
touchesBegan
方法- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 创建重力仿真行为并指定仿真元素为蓝色的view UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.blueView]]; // 把仿真行为添加到仿真器中 [self.dynamicAnimator addBehavior:gravity]; }
- 运行,点击控制器View,蓝色View向下运动,然后从下面出去了
-
-
-
如果想让
blueView
到底部停止还需要添加一个碰撞行为- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 创建重力仿真行为并指定仿真元素为blueView UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.blueView]]; // 创建一个碰撞的仿真行为并指定仿真元素为blueView UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.blueView]]; // 设置仿真边界为碰撞边界 collision.translatesReferenceBoundsIntoBoundary = YES; // 添加碰撞行为到仿真器中 [self.dynamicAnimator addBehavior:collision]; // 把仿真行为添加到仿真器中 [self.dynamicAnimator addBehavior:gravity]; }
-
在storyboard上添加一个添加一个红色View,在蓝色的View下方,运行程序蓝色的View会从红色的View上方飘过,这是因为此时红色的View并没有添加到仿真器中,如果你希望蓝色的View撞到红色的View那么就需要把红色的View添加到碰撞的仿真行为中 1.红色View的拖线到控制器中,属性名称为redView 2.把红色View添加到碰撞仿真行为中
UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.blueView,self.redView]];
- 运行程序,点击屏幕,红色的View被推下去了;把红色的View放到蓝色View能砸到其角上的一个位置,测试,会有惊喜发现哦!
-
添加碰撞边界
-
添加直线边界
- (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 创建重力仿真行为并指定仿真元素为blueView UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.blueView]]; // 创建一个碰撞的仿真行为并指定仿真元素为blueView UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.blueView]]; // 设置仿真边界为碰撞边界 collision.translatesReferenceBoundsIntoBoundary = YES; // 添加直线碰撞边界 [collision addBoundaryWithIdentifier:@"line" fromPoint:CGPointMake(0, self.view.frame.size.height * 0.5) toPoint:CGPointMake(self.view.frame.size.width, self.view.frame.size.height)]; // 添加碰撞行为到仿真器中 [self.dynamicAnimator addBehavior:collision]; // 把仿真行为添加到仿真器中 [self.dynamicAnimator addBehavior:gravity]; }
-
添加一个贝塞尔曲线碰撞边界,把添加直线碰撞边界的代码修改如下代码
// 添加贝塞尔曲线碰撞边界 UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width)]; [collision addBoundaryWithIdentifier:@"bezier" forPath:bezierPath];
-
-
重力行为的常见属性
//// 设置重力的角度,默认为M_PI_2(向下),0为右,M_PI向左 // gravity.angle = 0; //// 设置重力的大小,默认是1 // gravity.magnitude = 2; // 设置重力向量,(0,0) -> (1,1) 之间的连线,大小为:根号2 gravity.gravityDirection = CGVectorMake(1, 1);
-
捕获行为
// 相同类型仿真行为只能添加到仿真器一次,所以在添加仿真行为之前,移除之前添加仿真行为 [self.dynamicAnimator removeAllBehaviors]; // 获取描述用户点击的对象 UITouch *touch = [touches anyObject]; // 获取用户点击的位置 CGPoint loc = [touch locationInView:self.view]; // 创建一个捕获行为 UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.blueView snapToPoint:loc]; // 设置减震效果,取值范围为 0.0 to 1.0. 0表示最小减震效果,也就是振幅最大 snap.damping = 0; // 把捕获行为添加到仿真器中 [self.dynamicAnimator addBehavior:snap];
- Demo2 愤怒的小鸟
- 视图
-