UIDynamic 物理引擎概念介绍UIDynamicAnimator(动画者)动力行为(UIDynamicBehavior)一、抽象类 UIDynamicBehavior二、UIGravityBeh

时间:2022-05-16
本文章向大家介绍UIDynamic 物理引擎概念介绍UIDynamicAnimator(动画者)动力行为(UIDynamicBehavior)一、抽象类 UIDynamicBehavior二、UIGravityBeh,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

概念介绍

  • UIDynamic从ios7才开始有的,其他2D仿真引擎: BOX2D:C语言框架,免费 Chipmunk:C语言框架免费,其他版本收费(C#、Objective-C、Java)
  • 必须遵守了UIDynamicItem协议的控件才能应用这些行为,UIView遵守了,所以所有控件都可以使用
  • 使用步骤:创建一个动画者对象UIDynamicAnimator并设置坐标系,再添加一个动画行为对象(并设置动画作用的控件)
  • UIDynamic中的三个重要概念
  1. UIDynamicAnimator:动画者,为动力学元素提供物理学相关的能力及动画,同时为这些元素提供相关的上下文,是动力学元素与底层iOS物理引擎之间的中介,将Behavior对象添加到Animator即可实现动力仿真
  2. UIDynamicBehavior:仿真行为,是动力学行为的父类,基本的动力学行为类UIGravityBehavior、UICollisionBehavior、UIAttachmentBehavior、UISnapBehavior、UIPushBehavior以及UIDynamicItemBehavior均继承自该父类
  3. UIDynamicItem:动力学元素(动力项,就是应用行为的控件),是任何遵守了UIDynamicItem协议的对象,从iOS 7.0始,UIView和UICollectionViewLayoutAttributes默认实现该协议。如果自定义的对象实现了该协议,即可通过Dynamic Animator实现物理仿真

UIDynamicAnimator(动画者)

是动力行为(UIDynamicBehavior)的容器,添加到容器内的行为才会发挥作用 注意UIDynamicAnimator对象是否是强引用,可以创建一个强引用animator属性,进行懒加载

方法:

  1. 创建一个动画者并设置一个坐标系view,参数:动画效果在哪个view的范围、坐标系之内
-(instancetype)initWithReferenceView:(UIView *)view;

例子:

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

2.添加行为

- (void)addBehavior:(UIDynamicBehavior *)behavior;

3.移除行为、移除所有行为

- (void)removeBehavior:(UIDynamicBehavior *)behavior;
- (void)removeAllBehaviors;

属性:

1.获取坐标系View

@property (nullable, nonatomic, readonly) UIView *referenceView;

2.获取所有行为对象

@property (nonatomic, readonly, copy) NSArray<__kindof UIDynamicBehavior*> *behaviors;

动力行为(UIDynamicBehavior)

所有的动力学行为可以独立作用也可以组合使用,注意:避免重复添加行为对象,可以用懒加载

UIDynamicBehavior (抽象类): UIGravityBehavior:重力行为 UICollisionBehavior:碰撞行为 UISnapBehavior:甩行为 UIAttachmentBehavior:附着行为 UIPushBehavior:推行为

UIDynamicItemBehavior:动力学元素行为(执行动画物体自身的行为) UIDynamicItem 协议,动力学元素、动力项协议 ios9新特性参考 http://www.cocoachina.com/ios/20150719/12613.html

@property (nonatomic, readwrite) CGPoint center;
@property (nonatomic, readonly) CGRect bounds;(只读)
@property (nonatomic, readwrite) CGAffineTransform transform;

一、抽象类 UIDynamicBehavior

属性:

1.获取添加到该动态行为中的子动态行为

@property (nonatomic, readonly, copy) NSArray<__kindof UIDynamicBehavior *> *childBehaviors;

2.action属性:行为执行期间一直调用这个action block,可以用来不断监听行为,如在block中重绘

@property (nullable, nonatomic,copy) void (^action)(void);

3.获取该动态行为相关联的dynamicAnimator

@property (nullable, nonatomic, readonly) UIDynamicAnimator *dynamicAnimator;

方法:

1、添加一个子动态行为

- (void)addChildBehavior:(UIDynamicBehavior *)behavior;

2、移除一个子动态行为

- (void)removeChildBehavior:(UIDynamicBehavior *)behavior;

3、 当该动态行为将要被添加到一个UIDynamicAnimator中时,这个方法会被调用

- (void)willMoveToAnimator:(nullable UIDynamicAnimator *)dynamicAnimator;

二、UIGravityBehavior(重力行为)

属性

1.获取该重力行为的所有动力项

@property (nonatomic, readonly, copy) NSArray<id <UIDynamicItem>> *items;

2.重力的方向,默认(0.0,1.0)向下移动

(1.0,1.0),是右下角移动,(1.0,0.0)是向右移动,坐标上非0的位置表示移动的方向

@property (readwrite, nonatomic) CGVector gravityDirection;

例子:

gravity.gravityDirection = CGVectorMake(1.0, 1.0);

3.方向的弧度,向哪个方向进行重力动画(和上面的效果差不多)

@property (readwrite, nonatomic) CGFloat angle;

例子:

gravity.angle = M_PI_2;

4.力度、力量(越大掉的越快,默认是1)

@property (readwrite, nonatomic) CGFloat magnitude;

方法

1.创建一个重力行为同时添加一组动力项

- (instancetype)initWithItems:(NSArray<id <UIDynamicItem>> *)items;

例子:

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

2.添加一个动力项

- (void)addItem:(id <UIDynamicItem>)item;

3.移除一个动力项

- (void)removeItem:(id <UIDynamicItem>)item;

4.设置重力方向和力度

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

三、UICollisionBehavior:碰撞行为

属性

1.获取该碰撞行为的所有动力项

@property (nonatomic, readonly, copy) NSArray<id <UIDynamicItem>> *items;

2.碰撞模式

@property (nonatomic, readwrite) UICollisionBehaviorMode collisionMode;

UICollisionBehaviorMode 枚举: UICollisionBehaviorModeItems 仅仅和控件碰撞 UICollisionBehaviorModeBoundaries 仅仅和边界碰撞 UICollisionBehaviorModeEverything 可以和边界和控件碰撞

3.是否以参照视图的bounds为碰撞边界,设置为YES会设置当前view为边界

@property (nonatomic, readwrite) BOOL translatesReferenceBoundsIntoBoundary;

4.获取该碰撞所有的边界标识

@property (nullable, nonatomic, readonly, copy) NSArray<id <NSCopying>> *boundaryIdentifiers;

5.代理对象(可以监听动力项的碰撞过程)

@property (nullable, nonatomic, weak, readwrite) id <UICollisionBehaviorDelegate> collisionDelegate;

方法

1.创建一个碰撞行为同时添加给一组动力项

- (instancetype)initWithItems:(NSArray<id <UIDynamicItem>> *)items;

2.给该行为添加一个动力项

- (void)addItem:(id <UIDynamicItem>)item;

3.给该行为移除一个动力项

- (void)removeItem:(id <UIDynamicItem>)item;

4.设置参照视图的bounds为边界,并且设置视图的内边距

- (void)setTranslatesReferenceBoundsIntoBoundaryWithInsets:(UIEdgeInsets)insets;

5.设置边界线的两种方法,identifier参数是给这个边界随意取一个标识,碰到边界后会产生一些行为方法,所以要指定一个标识,用于以后引用 (1)设置一个贝塞尔曲线路径为边界

- (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier forPath:(UIBezierPath *)bezierPath;

例子:

UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 320, 320)];
[collision addBoundaryWithIdentifier:@"circle" forPath:path];

(2)设置一条线为边界

- (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier fromPoint:(CGPoint)p1 toPoint:(CGPoint)p2;

6.移除该碰撞所有边界

- (void)removeAllBoundaries;

7.根据边界标识获取路径

- (nullable UIBezierPath *)boundaryWithIdentifier:(id <NSCopying>)identifier;

8.根据边界标识移除边界

- (void)removeBoundaryWithIdentifier:(id <NSCopying>)identifier;

UICollisionBehaviorDelegate 代理方法:

注意:碰撞代理是collisionDelegate,而不是delegate,注意与父类代理区分,如: collision.collisionDelegate = self;

1.一个动力项碰到另一个动力项: (1)一个动力项开始碰到另一个动力项时调用

- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2 atPoint:(CGPoint)p;

(2)一个动力项结束碰到另一个动力项时调用

- (void)collisionBehavior:(UICollisionBehavior *)behavior endedContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2;

2.动力项和边界的碰撞: (1)动力项开始碰到某个边界

- (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(nullable id <NSCopying>)identifier atPoint:(CGPoint)p;

(2)动力项结束碰到某个边界

- (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(nullable id <NSCopying>)identifier;

例子:一个动力项碰撞到边界后改变不同颜色

// 碰撞行为的代理方法
- (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(nullable id <NSCopying>)identifier atPoint:(CGPoint)p {
    //获取当前碰撞到边界的动力项(把item参数强转成使用的控件)
    UIView *view = (UIView *)item;
    //获取当前碰撞到的边界名称
    NSString *ID = (NSString *)identifier;
    //判断当前碰撞到的边界是否是“黄色view的边界”
    if ([ID isEqualToString:@"yellow_boundary"]) {
        //碰到后就变成蓝色
        view.backgroundColor = [UIColor blueColor];
        //0.3秒后变成红色
        [UIView animateWithDuration:0.3 animations:^{
            view.backgroundColor = [UIColor redColor];
        }];
    }
}

四、UISnapBehavior(甩行为)

效果:触摸哪个点这个红色块就跟随到哪里,并产生附着效果,甩行为可以将视图通过动画甩(吸附)到某个点上

属性

1.振幅,默认0.5,0振幅最大,1振幅最小

@property (nonatomic, assign) CGFloat damping;

2.设置附着点

@property (nonatomic, assign) CGPoint snapPoint;

方法

1.创建一个甩行为同时设置动力项和附着点

- (instancetype)initWithItem:(id <UIDynamicItem>)item snapToPoint:(CGPoint)point;

例子:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = touches.anyObject;
    CGPoint loc = [touch locationInView:self];
    // 1. 创建动画者
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self];
    // 2. 创建物理行为
    UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.redView snapToPoint:loc];
    snap.damping = 0;
    // 3. 把行为添加到animator中
    [self.animator addBehavior:snap];
}

五、UIAttachmentBehavior(附着行为、吸附行为)

描述一个view和一个锚相连接的情况,也可以描述view和view之间的连接 在多个物体间设定多个UIAttachmentBehavior,可以模拟多物体连接 注意:吸附行为重复添加的问题,建议懒加载行为对象

刚性附着和弹性附着

分为刚性附着和弹性附着 1、刚性附着,固定了length就是刚性附着,红、蓝两点距离固定

attachment.length = 100;

2、弹性附着(设置了频率和振幅),红、蓝两色像皮筋一样距离不固定

attachment.damping = 0;
attachment.frequency = 0.5;

属性

1.获取该吸附行为的所有动力项 当吸附行为类型是UIAttachmentBehaviorTypeItems时有l两个动力项,当吸附行为类型是UIAttachmentBehaviorTypeAnchor时只有一个动力项

@property (nonatomic, readonly, copy) NSArray<id <UIDynamicItem>> *items;

2.获取连接类型

@property (readonly, nonatomic) UIAttachmentBehaviorType attachedBehaviorType;

UIAttachmentBehaviorType 枚举: UIAttachmentBehaviorTypeItems 连接到视图view(至少两个动力项) UIAttachmentBehaviorTypeAnchor 连接到锚点(只有一个动力项)

3.设置动力项吸附的锚点

@property (readwrite, nonatomic) CGPoint anchorPoint;

4.视图点连接锚点的距离,两个吸附点之间的距离

@property (readwrite, nonatomic) CGFloat length;

5.只要设置了以下两个属性,即为弹性连接 (1)振幅大小, 吸附行为减弱的阻力大小

@property (readwrite, nonatomic) CGFloat damping;

(2)振幅频率

@property (readwrite, nonatomic) CGFloat frequency;

方法

注意: 吸附行为的创建不是直接 alloc + init, 而是 alloc + initWithItem

1.构造方法 (1)创建一个吸附行为,让一个动力项的中点和一个指定的锚点进行吸附,该初始化方法的吸附行为的类型是UIAttachmentBehaviorTypeAnchor

- (instancetype)initWithItem:(id <UIDynamicItem>)item attachedToAnchor:(CGPoint)point;

(2)创建一个吸附行为,让两个动力项的中点进行吸附, 该初始化方法的吸附行为的类型是UIAttachmentBehaviorTypeItems

- (instancetype)initWithItem:(id <UIDynamicItem>)item1 attachedToItem:(id <UIDynamicItem>)item2;

(3)创建一个吸附行为,让一个动力项的某一点和指定的锚点进行吸附,UIAttachmentBehaviorTypeAnchor类型,offset相对于动力项center的偏移

- (instancetype)initWithItem:(id <UIDynamicItem>)item offsetFromCenter:(UIOffset)offset attachedToAnchor:(CGPoint)point;

(4)创建一个吸附行为,让一个动力项的某一点和另一个动力项的某一点进行吸附,UIAttachmentBehaviorTypeItems类型,offset相对于动力项center的偏移

- (instancetype)initWithItem:(id <UIDynamicItem>)item1 offsetFromCenter:(UIOffset)offset1 attachedToItem:(id <UIDynamicItem>)item2 offsetFromCenter:(UIOffset)offset2;

把以子视图为基准的坐标转换为以父视图为基准的视图坐标方法: 注意:不能直接修改子控件的anchorPoint为0,0,因为以后使用center就会导致无法使用!!!

子视图为基准的坐标转换为以父视图为基准的视图坐标方法


UIPushBehavior(推行为)

注意要进行懒加载,多次添加会导致无效

1.获取该行为作用的动力项

@property (nonatomic, readonly, copy) NSArray<id <UIDynamicItem>> *items;

2.推力类型

@property (nonatomic, readonly) UIPushBehaviorMode mode;

UIPushBehaviorModeContinuous 持续推力 UIPushBehaviorModeInstantaneous 瞬时推力

3.是否激活,active表示推力是否还在作用,如果是瞬时推力,需要激活

@property (nonatomic, readwrite) BOOL active;

4、推动角度,可使用0~M_PI*2之间的值来定位所有方向

@property (readwrite, nonatomic) CGFloat angle;

5、重力加速度,推力大小,越远力量越大,默认为1.0,可取负值

@property (readwrite, nonatomic) CGFloat magnitude;

算法:取触摸点loc的x,y与动力项的中点center的x,y做差算出两条直线长度,再用勾股定理:sqrt(x2+y2) 求出 例子:

CGFloat offsetX = loc.x - self.redView.center.x;
CGFloat offsetY = loc.y - self.redView.center.y;
CGFloat distance = sqrtf(powf(offsetX, 2.0) + powf(offsetY, 2.0));
//powf 函数为浮点型的参数1的参数2次方

6.推动方向,CGVector 矢量

@property (readwrite, nonatomic) CGVector pushDirection;

例子:push.pushDirection = CGVectorMake(1, 1);

方法:

1.将行为添加到动力项当中

- (void)addItem:(id <UIDynamicItem>)item;

2.将行为从动力项当中移除

- (void)removeItem:(id <UIDynamicItem>)item;

3.创建一个推行为添加给一组动力项,并设置推力类型

- (instancetype)initWithItems:(NSArray<id<UIDynamicItem>> *)items mode:(UIPushBehaviorMode)mode;

4.设置推动角度和力度

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

5.获取推力作用点的偏移量,默认是center

- (UIOffset)targetOffsetFromCenterForItem:(id <UIDynamicItem>)item;

6.设置推力作用点的偏移量,默认是center

- (void)setTargetOffsetFromCenter:(UIOffset)o forItem:(id <UIDynamicItem>)item;

UIDynamicItemBehavior(动力项行为)

控件本身的行为:弹性系数、摩擦系数、密度、阻力、角阻力以及是否允许旋转等

属性

1.获取有该行为的所有动力项

@property (nonatomic, readonly, copy) NSArray<id <UIDynamicItem>> *items;

2.设置弹性系数,决定了碰撞的弹性程度,比如碰撞时物体的弹性,值从0—1,0为无弹力

@property (readwrite, nonatomic) CGFloat elasticity;

3.摩擦系数,决定了沿接触面滑动时的摩擦力大小,0为无摩擦,1最大

@property (readwrite, nonatomic) CGFloat friction;

4.密度,和size结合使用,计算物体的总质量。质量越大,物体加速或减速就越困难,默认为1

@property (readwrite, nonatomic) CGFloat density;

5.阻力,决定线性移动的阻力大小,与摩擦系数不同,摩擦系数只作用于滑动运动,0为无阻力

@property (readwrite, nonatomic) CGFloat resistance;

6.角阻力,决定旋转运动时的阻力大小

@property (readwrite, nonatomic) CGFloat angularResistance;

7.是否允许旋转,设置这个属性为 NO 无论施加多大的转动力物体都不会转动

@property (readwrite, nonatomic) BOOL allowsRotation;

8.charge 代表能够影响一个动力项在电磁场上如何移动的电荷

@property (readwrite, nonatomic) CGFloat charge;

9.anchored本质上是将图形变成了碰撞中的一个静态物体,但没有响应事件(如果有什么东西撞上了它,它会丝毫不动),所以可以完美地用来表示地板或墙壁。

@property (nonatomic, getter = isAnchored) BOOL anchored;

方法

1.创建一个动力项行为对象,并添加到一组动力项当中

- (instancetype)initWithItems:(NSArray<id <UIDynamicItem>> *)items;

2.将行为添加到动力项当中

- (void)addItem:(id <UIDynamicItem>)item;

3.将行为从动力项当中移除

- (void)removeItem:(id <UIDynamicItem>)item;

4、配置一些公用的属性,与其他的Dynamic Behavior共同配合 (1)添加线速度,滑行时候的速度

- (void)addLinearVelocity:(CGPoint)velocity forItem:(id <UIDynamicItem>)item;

(2)获取线速度

- (CGPoint)linearVelocityForItem:(id <UIDynamicItem>)item;

(3)添加角速度,旋转时候的速度

- (void)addAngularVelocity:(CGFloat)velocity forItem:(id <UIDynamicItem>)item;

(4)获取角速度,动力项自身旋转的速度

- (CGFloat)angularVelocityForItem:(id <UIDynamicItem>)item;