什么是UIDynamic
UIDynamic 是从 iOS 7开始引入的一种新技术,隶属于UIKit框架可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象,如:重力、弹性碰撞等现象
物理引擎的价值
广泛用于游戏开发,经典成功案例是“愤怒的小鸟”让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏
知名的2D物理引擎
Box2d
Chipmunk
三个概念
物理仿真元素(Dynamic Item)
谁要进行物理仿真?
物理仿真元素要素:
任何遵守了UIDynamicItem协议的对象
UIView 默认已经遵守了 UIDynamicItem 协议,因此任何 UI 控件都能做物理仿真 UICollectionViewLayoutAttributes 类默认也遵守 UIDynamicItem 协议
注意:不是任何对象都能做物理仿真元素,不是任何对象都能进行物理仿真
物理仿真行为(Dynamic Behavior)
执行怎样的物理仿真效果?怎样的动画效果?
UIDynamic提供了以下几种物理仿真行为:
- UIGravityBehavior:重力行为
- UICollisionBehavior:碰撞行为
- UISnapBehavior:捕捉行为
- UIPushBehavior:推动行为
- UIAttachmentBehavior:附着行为
- UIDynamicItemBehavior:动力元素行为
上述所有物理仿真行为都继承自 UIDynamicBehavior
所有的 UIDynamicBehavior 都可以独立进行,组合使用多种行为时,可以实现一些比较复杂的效果
物理仿真器(Dynamic Animator)
让物理仿真元素执行具体的物理仿真行为,UIDynamicAnimator 类型的对象
1
| - (instancetype)initWithReferenceView:(UIView *)view;
|
view参数:是一个参照视图,表示物理仿真的范围
UIDynamicAnimator 的常见方法
1 2 3 4 5 6 7 8
| - (void)addBehavior:(UIDynamicBehavior *)behavior;
- (void)removeBehavior:(UIDynamicBehavior *)behavior;
- (void)removeAllBehaviors;
|
UIDynamicAnimator的常见属性
1 2 3 4 5 6 7 8 9 10 11
| @property (nonatomic, readonly) UIView* referenceView;
@property (nonatomic, readonly, copy) NSArray* behaviors;
@property (nonatomic, readonly, getter = isRunning) BOOL running;
@property (nonatomic, assign) id <UIDynamicAnimatorDelegate> delegate;)
|
使用步骤
要想使用UIDynamic来实现物理仿真效果,大致的步骤如下
- 创建一个物理仿真器(顺便设置仿真范围)
- 创建相应的物理仿真行为(顺便添加物理仿真元素)
- 将物理仿真行为添加到物理仿真器中开始仿真
下面将详细介绍所有的物理仿真行为,并附上示例代码
UIGravityBehavior 重力行为

说明:给定重力方向、加速度,让物体朝着重力方向掉落
常用方法:
1 2 3 4 5 6 7 8
| - (instancetype)initWithItems:(NSArray *)items;
- (void)addItem:(id <UIDynamicItem>)item;
- (void)removeItem:(id <UIDynamicItem>)item;
|
UIGravityBehavior常见属性
1 2 3 4 5 6 7 8 9 10 11
| @property (nonatomic, readonly, copy) NSArray* items;
@property (readwrite, nonatomic) CGVector gravityDirection;
@property (readwrite, nonatomic) CGFloat angle;
@property (readwrite, nonatomic) CGFloat magnitude;
|
code
1 2 3 4 5 6 7 8
| UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init];
[gravity addItem:self.purperView];
[self.animator addBehavior:gravity];
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
- (void)gravityProperty { UIGravityBehavior *gravity = [[UIGravityBehavior alloc]init]; gravity.magnitude = 100; gravity.gravityDirection=CGVectorMake(1, 1); [gravity addItem:self.purperView]; UICollisionBehavior *collision = [[UICollisionBehavior alloc] init]; [collision addItem:self.purperView]; [collision addItem:self.block1]; [collision addItem:self.block2]; collision.translatesReferenceBoundsIntoBoundary = YES; [self.animator addBehavior:gravity]; [self.animator addBehavior:collision]; }
|
UICollisionBehavior 碰撞行为
可以让物体之间实现碰撞效果,可以通过添加边界(boundary),让物理碰撞局限在某个空间中
UICollisionBehavior 边界相关的方法
1 2 3 4 5 6 7 8 9 10 11
| - (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier forPath:(UIBezierPath*)bezierPath;
- (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier fromPoint:(CGPoint)p1 toPoint:(CGPoint)p2;
- (UIBezierPath*)boundaryWithIdentifier:(id <NSCopying>)identifier;
- (void)removeBoundaryWithIdentifier:(id <NSCopying>)identifier;
@property (nonatomic, readonly, copy) NSArray* boundaryIdentifiers;
- (void)removeAllBoundaries;
|
UICollisionBehavior 常见用法
1 2 3 4 5 6 7 8 9 10 11
| @property (nonatomic, readwrite) BOOL translatesReferenceBoundsIntoBoundary;
- (void)setTranslatesReferenceBoundsIntoBoundaryWithInsets:(UIEdgeInsets)insets;
@property (nonatomic, readwrite) UICollisionBehaviorMode collisionMode;
@property (nonatomic, assign, readwrite) id <UICollisionBehaviorDelegate> collisionDelegate;
|
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
- (void)gravityAndCollision { UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init]; [gravity addItem:self.purperView]; UICollisionBehavior *collision = [[UICollisionBehavior alloc] init]; [collision addItem:self.purperView]; [collision addItem:self.block1]; [collision addItem:self.block2]; collision.translatesReferenceBoundsIntoBoundary = YES; [self.animator addBehavior:gravity]; [self.animator addBehavior:collision]; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
- (void)customBounds { UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init]; [gravity addItem:self.purperView]; UICollisionBehavior *collision = [[UICollisionBehavior alloc] init]; [collision addItem:self.purperView]; UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 320, 320)]; [collision addBoundaryWithIdentifier:@"circle" forPath:path]; [self.animator addBehavior:gravity]; [self.animator addBehavior:collision]; }
- (void)makeBoundsPath { UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init]; [gravity addItem:self.purperView]; UICollisionBehavior *collision = [[UICollisionBehavior alloc] init]; [collision addItem:self.purperView]; CGPoint startP = CGPointMake(0, 160); CGPoint endP = CGPointMake(320, 400); [collision addBoundaryWithIdentifier:@"line1" fromPoint:startP toPoint:endP]; CGPoint startP1 = CGPointMake(320, 0); [collision addBoundaryWithIdentifier:@"line2" fromPoint:startP1 toPoint:endP]; [self.animator addBehavior:gravity]; [self.animator addBehavior:collision]; }
|
UISnapBehavior 捕捉行为
可以让物体迅速冲到某个位置(捕捉位置),捕捉到位置之后会带有一定的震动
UISnapBehavior的初始化
1
| - (instancetype)initWithItem:(id <UIDynamicItem>)item snapToPoint:(CGPoint)point;
|
UISnapBehavior常见属性
1 2
| @property (nonatomic, assign) CGFloat damping;
|
注意:
如果要进行连续的捕捉行为,需要先把前面的捕捉行为从物理仿真器中移除
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self.view];
UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.purperView snapToPoint:point];
snap.damping = arc4random_uniform(10) / 10.0;
[self.animator removeAllBehaviors];
[self.animator addBehavior:snap];
|
UIPushBehavior 推动行为
需要结合示例来说明如何使用
请看考UIPushBehavior文件夹
初始化方法和常用的属性:
1 2 3 4 5 6 7 8 9 10
| UIPushBehavior *pushBehavior = [[UIPushBehavior alloc] initWithItems:@[self.image] mode:UIPushBehaviorModeInstantaneous];
pushBehavior.pushDirection = CGVectorMake((velocity.x / 10) , (velocity.y / 10));
pushBehavior.magnitude = magnitude / ThrowingVelocityPadding; self.pushBehavior = pushBehavior; [self.animator addBehavior:self.pushBehavior];
|
UIAttachmentBehavior:附着行为
有关 API 详细的解释见:UIAttachmentBehavior Class Reference
具体来说就是我手怎么动,UIAttachmentBehavior 指定的 view 就怎么动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| - (IBAction)handleAttachmentGesture:(UIPanGestureRecognizer *)pan { if (pan.state == UIGestureRecognizerStateBegan) { [self.animator removeAllBehaviors]; CGPoint location = [pan locationInView:self.view]; CGPoint boxLocation = [pan locationInView:self.lightBlueView]; UIOffset offset = UIOffsetMake(boxLocation.x - CGRectGetMidX(self.lightBlueView.bounds), boxLocation.y - CGRectGetMidY(self.lightBlueView.bounds)); self.attach = [[UIAttachmentBehavior alloc] initWithItem:self.lightBlueView offsetFromCenter:offset attachedToAnchor:location]; [self.animator addBehavior:self.attach]; } [self.attach setAnchorPoint:[pan locationInView:self.view]]; }
|
iOS9以后又新推出了一些方法,我大概看了一下,基本都是做小游戏的,使用这些方法你可以创造一个接近真实的世界,比如一个大风车在转,而每个扇叶顶端都有一个附着的舀水的东东就可以用 UIAttachmentBehavior 实现。
下面介绍一下 UIAttachmentBehavior 的属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @property (readonly, nonatomic) UIAttachmentBehaviorType attachedBehaviorType;
@property (readwrite, nonatomic) CGPoint anchorPoint;
@property (readwrite, nonatomic) CGFloat length;
@property (readwrite, nonatomic) CGFloat damping;
@property (readwrite, nonatomic) CGFloat frequency;
@property (readwrite, nonatomic) CGFloat frictionTorque NS_AVAILABLE_IOS(9_0); @property (readwrite, nonatomic) UIFloatRange attachmentRange NS_AVAILABLE_IOS(9_0);
|
UIDynamicItemBehavior:动力元素行为
具体作用:给一个仿真元素设置各种物理属性。比如:
allowsRotation :是否允许旋转
resistance: 抗阻力 0CGFLOAT_MAX ,阻碍原有所加注的行为(如本来是重力自由落体行为,则阻碍其下落,阻碍程度根据其值来决定)
friction: 磨擦力 0.01.0 在碰撞行为里,碰撞对象的边缘产生
elasticity:弹跳性 0.01.0
density:密度 01
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| - (UIView *) newViewWithCenter:(CGPoint)paramCenter backgroundColor:(UIColor *)paramBackgroundColor{ UIView *newView = [[UIView alloc] initWithFrame: CGRectMake(0.0f, 0.0f, 50.0f, 50.0f)]; newView.backgroundColor = paramBackgroundColor; newView.center = paramCenter; return newView; } - (void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; UIView *topView = [self newViewWithCenter:CGPointMake(100.0f, 0.0f) backgroundColor:[UIColor greenColor]]; UIView *bottomView = [self newViewWithCenter:CGPointMake(100.0f, 50.0f) backgroundColor:[UIColor redColor]]; [self.view addSubview:topView]; [self.view addSubview:bottomView]; self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[topView, bottomView]]; [self.animator addBehavior:gravity]; UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[topView, bottomView]]; collision.translatesReferenceBoundsIntoBoundary = YES; [self.animator addBehavior:collision]; UIDynamicItemBehavior *moreElasticItem = [[UIDynamicItemBehavior alloc] initWithItems:@[bottomView]]; moreElasticItem.elasticity = 1.0f; UIDynamicItemBehavior *lessElasticItem = [[UIDynamicItemBehavior alloc] initWithItems:@[topView]]; lessElasticItem.elasticity = 0.5f; [self.animator addBehavior:moreElasticItem]; [self.animator addBehavior:lessElasticItem]; }
|
本文所有代码:UIDynamic