iOS UIKit物理引擎(一)介绍

iOS7开始UIKit框架中添加了物理引擎,我们可以使视图通过物理引擎驱动而具有真实的物理运动效果。
如果你想做一个类似早期摩拜单车app中贴纸的小球碰撞效果,我相信这一系列文章肯定会对你有很大帮助的。

框架结构

  • UIDynamicAnimator
    UIDynamicAnimator是为动态元素提供力学相关功能和动画,并为这些运动提供物理空间。它是力学引擎和动态元素之间的媒介,这些元素通过物理行为(UIDynamicBehavior)被添加到UIDynamicAnimator中。
  • UIDynamicBehavior
    UIDynamicBehavior是给一个或多个动态元素参与的二维动画赋予的行为。我们通常使用它的6个物理特征子类:
    UIGravityBehavior(重力)
    UICollisionBehavior(碰撞)
    UIAttachmentBehavior(吸附)
    UIPushBehavior(推动)
    UISnapBehavior(捕获)
    UIDynamicItemBehavior
  • UIDynamicItem
    UIDynamicItem是参与力学运动的基本元素,并且是遵守了UIDynamicItem协议的对象,例如UIViewUICollectionViewLayoutAttributes。如果自定义对象也实现了UIDynamicItem协议,也是可以使用的。

UIDynamicAnimator

详细的介绍一下UIDynamicAnimator

  • 构造方法
    1
    -(instancetype)initWithReferenceView:(UIView *)view NS_DESIGNATED_INITIALIZER;

根据NS_DESIGNATED_INITIALIZER宏可以知道该方法是UIDynamicAnimator指定的初始化方法。
构建代码如下:

1
2
3
@interface ViewController ()
@property (nonatomic,strong) UIDynamicAnimator * animator;
@end

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

函数中的Reference View参数是指_animator所要实施力学动作的‘引用视图’,通俗的说就是运动元素所在的空间系,_animator上所有的力学行为的动态元素都将以该‘引用视图’作为空间系进行物理运动。这里我们把viewController中的self.view设置为‘引用视图’。

注意:在我们构建UIDynamicAnimator对象时一定要被其它对象有效持有,不然在arc模式下将很快被自动释放。

  • 属性
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 引用视图 (只读)
    @property (nullable, nonatomic, readonly) UIView *referenceView;
    // 添加的行为数组 (只读)
    @property (nonatomic, readonly, copy) NSArray<__kindof UIDynamicBehavior*> *behaviors;
    // 是否正在运行 (只读)
    @property (nonatomic, readonly, getter = isRunning) BOOL running;
    // 运行以来的时间间隔(只读,注意编译条件)
    #if UIKIT_DEFINE_AS_PROPERTIES
    @property (nonatomic, readonly) NSTimeInterval elapsedTime;
    #else
    -(NSTimeInterval)elapsedTime;
    #endif
    // 代理
    @property (nullable, nonatomic, weak) id <UIDynamicAnimatorDelegate> delegate;
  • 方法
    1
    2
    3
    4
    5
    6
    // 添加行为
    -(void)addBehavior:(UIDynamicBehavior *)behavior;
    // 移除行为
    -(void)removeBehavior:(UIDynamicBehavior *)behavior;
    // 移除所有行为
    -(void)removeAllBehaviors;
1
2
3
4
// 返回指定矩形区域中的动态项目
-(NSArray<id<UIDynamicItem>> *)itemsInRect:(CGRect)rect;
// 在UIDynamicAnimator中更新我们已经修改的动态项目
-(void)updateItemUsingCurrentState:(id <UIDynamicItem>)item;
  • 代理(UIDynamicAnimatorDelegate)
    1
    2
    3
    4
    5
    @optional
    // 当dynamicAnimator将要恢复调用
    -(void)dynamicAnimatorWillResume:(UIDynamicAnimator *)animator;
    // 当dynamicAnimator已经暂停调用
    -(void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator;

UIDynamicBehavior

在通常情况下我们不会直接使用UIDynamicBehavior类,而是使用它的子类们。在开始介绍它的子类前,先来看看UIDynamicBehavior作为父类的一些属性和方法:

1
2
3
4
// 添加一个自定义行为子类
- (void)addChildBehavior:(UIDynamicBehavior *)behavior;
// 移除一个自定义行为子类
- (void)removeChildBehavior:(UIDynamicBehavior *)behavior;

1
2
// 当一个动态行为被添加或移除,会回调该函数
- (void)willMoveToAnimator:(nullable UIDynamicAnimator *)dynamicAnimator;
1
2
3
4
5
6
// 子行为 (只读)
@property (nonatomic, readonly, copy) NSArray<__kindof UIDynamicBehavior *> *childBehaviors;
// 在运行时调用的每一个动画步骤的block形式动作代码
@property (nullable, nonatomic,copy) void (^action)(void);
// 所属的dynamicAnimator
@property (nullable, nonatomic, readonly) UIDynamicAnimator *dynamicAnimator;

UIDynamicItem

详细介绍一下UIDynamicItem协议的属性:

1
2
3
4
5
6
// 中心
@property (nonatomic, readwrite) CGPoint center;
// 实时范围
@property (nonatomic, readonly) CGRect bounds;
// 旋转状态
@property (nonatomic, readwrite) CGAffineTransform transform;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@optional
/**
元素碰撞边界的形状类型
该参数用来获得元素以何种形式进行碰撞
默认为UIDynamicItemCollisionBoundsTypeRectangle
 */
@property (nonatomic, readonly) UIDynamicItemCollisionBoundsType collisionBoundsType NS_AVAILABLE_IOS(9_0);

typedef NS_ENUM(NSUInteger, UIDynamicItemCollisionBoundsType) {
// 矩形碰撞边界
    UIDynamicItemCollisionBoundsTypeRectangle,
// 椭圆形碰撞边界,椭圆的形状是由元素的宽度和高度决定
    UIDynamicItemCollisionBoundsTypeEllipse,
// 基于路径碰撞边界。这种类型的形状是一个UIBezierPath对象存储在元素的collisionBoundingPath属性中。
    UIDynamicItemCollisionBoundsTypePath
} NS_ENUM_AVAILABLE_IOS(9_0);
1
2
3
4
5
6
7
/**
碰撞路径
该路径必须是一个凸的多边形围绕而成,并且没有自身的交叉
(0,0) 代表路径的中心
(翻译自官方文档)
 */
@property (nonatomic, readonly) UIBezierPath *collisionBoundingPath NS_AVAILABLE_IOS(9_0);

代码补充:

补充演示中的代码,方便后续文章的理解:

1
2
3
4
_dynamicItem1View = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
_dynamicItem1View.backgroundColor    = [UIColor blueColor];
_dynamicItem1View.layer.cornerRadius = 25;
[self.view addSubview:_dynamicItem1View];

1
2
3
4
_dynamicItem2View = [[UIView alloc] initWithFrame:CGRectMake(160, 250, 100, 100)];
_dynamicItem2View.backgroundColor   = [UIColor blackColor];
_dynamicItem2View.layer.cornerRadius = 50;
[referenceView addSubview:_dynamicItem2View];

iOS UIKit物理引擎(二)重力(UIGravityBehavior)
iOS UIKit物理引擎(三)碰撞(UICollisionBehavior)
iOS UIKit物理引擎(四)吸附(UIAttachmentBehavior)
iOS UIKit物理引擎(五)推动(UIPushBehavior)
iOS UIKit物理引擎(六)捕获(UISnapBehavior)
iOS UIKit物理引擎(七)其它配置(UIDynamicItemBehavior)


版权声明:出自Jerry LMJ的原创作品,未经作者允许不得转载。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器