503-UIDynamic

一、简介

1.使用步骤

1)创建一个物理仿真器

// animator
- (UIDynamicAnimator *)animator
{
    if (!_animator) {
        _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    }
    return _animator;
}

2)创建相应的物理仿真行为

// 重力行为
UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init];

3)添加物理仿真元素

// 添加物理仿真元素
[gravity addItem:self.redView];

4)将物理仿真行为添加到物理仿真器中,执行仿真

// 执行仿真,让物理仿真元素执行仿真行为
[self.animator addBehavior:gravity];

2.概念性名词

1)物理仿真元素

注意:不是任何的对象都可以做物理仿真元素,不是任何对象都能进行物理仿真

物理仿真元素要素:任何遵守了UIDynamicItem协议的对象

UIView默认已经遵守了UIDynamicItem协议,因此任何UI控件都能做物理仿真

2)物理仿真行为

UIGravityBehavior:重力行为

UICollisionBehavior:碰撞行为

UISnapBehavior:捕捉行为

UIPushBehavior:推动行为

UIAttachmentBehavior:附着行为

UIDynamicItemBehavior:动力元素行为

3)物理仿真器

它可以让物理仿真元素执行物理仿真行为,属于UIDynamicAnimator类型的对象

常见方法和属性

// 1.方法
- (void)addBehavior:(UIDynamicBehavior *)behavior;    // 添加1个物理仿真行为
- (void)removeBehavior:(UIDynamicBehavior *)behavior;  // 移除1个物理仿真行为
- (void)removeAllBehaviors;                           // 移除之前添加过的所有物理仿真行为
// 2.属性
@property (nonatomic, readonly) UIView* referenceView;                   // 参照视图 
@property (nonatomic, readonly, copy) NSArray* behaviors;                // 添加到物理仿真器中的所有物理仿真行为
@property (nonatomic, readonly, getter = isRunning) BOOL running;        // 是否正在进行物理仿真
@property (nonatomic, assign) id <UIDynamicAnimatorDelegate> delegate;   // 代理对象(能监听物理仿真器的仿真过程,比如开始和结束)

 

二、重力行为

1.说明

给定重力方向、加速度,让物体朝着重力方向掉落

2.方法

// 1.UIGravityBehavior的初始化
// items:存放着物理仿真元素的数组
- (instancetype)initWithItems:(NSArray *)items;
// 2.添加1个物理仿真元素
- (void)addItem:(id <UIDynamicItem>)item;
// 3.移除1个物理仿真元素
- (void)removeItem:(id <UIDynamicItem>)item;

3.属性

// 添加到重力行为中的所有物理仿真元素
@property (nonatomic, readonly, copy) NSArray* items;
// 重力方向(是一个二维向量)
@property (readwrite, nonatomic) CGVector gravityDirection;
// 重力方向(是一个角度,以x轴正方向为0°,顺时针正数,逆时针负数)
@property (readwrite, nonatomic) CGFloat angle;
// 量级(用来控制加速度,1.0代表加速度是1000 points /second²)
@property (readwrite, nonatomic) CGFloat magnitude;

4.代码实例

#import "GravityController.h"

@interface GravityController ()

@property (nonatomic, strong) UIDynamicAnimator *animator; // 物理仿真器
@property (nonatomic, strong) UIView *redView;

@end

@implementation GravityController

#pragma mark - 懒加载
// animator
- (UIDynamicAnimator *)animator
{
    if (!_animator) {
        _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    }
    return _animator;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
   
    [self initUI]; // 界面
}

#pragma mark - 界面
- (void)initUI
{
    self.view.backgroundColor = [UIColor whiteColor];
    
    // redView
    _redView = [[UIView alloc] initWithFrame:CGRectMake(40, 70, 100, 100)];
    _redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_redView];
    
    // btn
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    btn.frame = CGRectMake(20, 20, 100, 35);
    btn.tintColor = [UIColor whiteColor];
    btn.backgroundColor = [UIColor orangeColor];
    [btn setTitle:@"重力测试" forState:UIControlStateNormal];
    btn.titleLabel.font = [UIFont systemFontOfSize:15];
    [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}

#pragma mark - 点击事件
- (void)btnClick:(UIButton *)sender
{
    // 1.创建仿真行为
    // 重力行为
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init];
    // 1)设置重力的方向(角度和二维向量二选一)
    // a.角度设置(angle)
    // 角度坐标:水平向右是0°,垂直向下是90°,水平向左是180°,垂直向上是-90°
    gravity.angle = M_PI_2 - M_PI_4;
    // b.二维向量设置(gravityDirection)
    // 给定坐标平面内的一个点,然后用原点(0, 0)来连接它,就构成了一个向量
    // gravity.gravityDirection = CGVectorMake(1, 0);
    // 2)设置重力的加速度
    // 重力的加速度越大,碰撞就越厉害
    // 速度:point/s
    // 加速度:point/s²
    gravity.magnitude = 10;
    [gravity addItem:self.redView];
    
    // 2.添加物理仿真元素
    [gravity addItem:self.redView];
    
    // 3.将物理仿真行为添加到物理仿真器中,执行仿真
    [self.animator addBehavior:gravity];
}

@end

 

三、碰撞行为

1.简介

可以让物体之间实现碰撞效果,可以通过添加边界(boundary),让物理碰撞局限在某个空间中

2.UICollisionBehavior边界相关的方法

// 1.添加边界路径
- (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier forPath:(UIBezierPath*)bezierPath;
// 2.添加直线边界
- (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier fromPoint:(CGPoint)p1 toPoint:(CGPoint)p2;
// 3.返回指定标示符的边界路径
- (UIBezierPath*)boundaryWithIdentifier:(id <NSCopying>)identifier;
// 4.移除指定标示符的边界
- (void)removeBoundaryWithIdentifier:(id <NSCopying>)identifier;
// 5.移除所有边界
- (void)removeAllBoundaries;
// 6.存放边界标示符的数组
@property (nonatomic, readonly, copy) NSArray* boundaryIdentifiers;

3.UICollisionBehavior常见属性和方法

// 1.是否以参照视图的bounds为边界
@property (nonatomic, readwrite) BOOL translatesReferenceBoundsIntoBoundary;
// 2.碰撞模式(分为3种:元素碰撞、边界碰撞、全体碰撞)
@property (nonatomic, readwrite) UICollisionBehaviorMode collisionMode;
// 3.代理对象(可以监听元素的碰撞过程)
@property (nonatomic, assign, readwrite) id <UICollisionBehaviorDelegate> collisionDelegate;
// 4.设置参照视图的bounds为边界,并且设置内边距
- (void)setTranslatesReferenceBoundsIntoBoundaryWithInsets:(UIEdgeInsets)insets;

4.代码

#import "GravityCollisionController.h"

@interface GravityCollisionController ()

@property (nonatomic, strong) UIDynamicAnimator *animator; // 物理仿真器
@property (nonatomic, strong) UIView *redView;
@property (nonatomic, strong) UIProgressView *progressView;
@property (nonatomic, strong) UISegmentedControl *segmentedControl;

@end

@implementation GravityCollisionController

#pragma mark - 懒加载
// animator
- (UIDynamicAnimator *)animator
{
    if (!_animator) {
        _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    }
    return _animator;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self initUI]; // 界面
}

#pragma mark - 界面
- (void)initUI
{
    self.view.backgroundColor = [UIColor whiteColor];
    
    // redView
    _redView = [[UIView alloc] initWithFrame:CGRectMake(40, 70, 100, 100)];
    _redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_redView];
    
    // progressView
    _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(10, 200, 200, 10)];
    [self.view addSubview:_progressView];
    
    // segmentedControl
    _segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"杨洋", @"任嘉伦", @"鹿晗"]];
    _segmentedControl.frame = CGRectMake(30, 240, 150, 40);
    [self.view addSubview:_segmentedControl];
    
    // btn
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    btn.frame = CGRectMake(20, 20, 120, 35);
    btn.tintColor = [UIColor whiteColor];
    btn.backgroundColor = [UIColor orangeColor];
    [btn setTitle:@"重力&碰撞测试" forState:UIControlStateNormal];
    btn.titleLabel.font = [UIFont systemFontOfSize:15];
    [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}

#pragma mark - 点击事件
- (void)btnClick:(UIButton *)sender
{
    [self testGravityAndCollsion1];   // 1.以参照视图的边框成为碰撞检测的边界
    // [self testGravityAndCollision2];  // 2.以2根线段作为边界
    // [self testGravityAndCollision3];  // 3.以圆作为边界
}

#pragma mark - 重力行为+碰撞检测
// 1.以参照视图的边框成为碰撞检测的边界
- (void)testGravityAndCollsion1
{
    // 1.重力行为
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init];
    [gravity addItem:self.redView];
    
    // 2.碰撞行为
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] init];
    [collision addItem:self.redView];
    [collision addItem:self.progressView];
    [collision addItem:self.segmentedControl];
    // 让参照视图的边框成为碰撞检测的边界
    collision.translatesReferenceBoundsIntoBoundary = YES;
    
    // 3.执行仿真
    [self.animator addBehavior:gravity];
    [self.animator addBehavior:collision];
}
// 2.以2根线段作为边界
- (void)testGravityAndCollision2
{
    // 1.重力行为
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init];
    [gravity addItem:self.redView];
    
    // 2.碰撞检测行为
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] init];
    [collision addItem:self.redView];
    CGPoint startP = CGPointMake(50, 160);
    CGPoint endP = CGPointMake(320, 400);
    // 注意:这个标示符不能为空,可以为字符串,因为需要遵守NSCopying协议
    [collision addBoundaryWithIdentifier:@"line1" fromPoint:startP toPoint:endP];
    CGPoint startP1 = CGPointMake(320, 0);
    [collision addBoundaryWithIdentifier:@"line2" fromPoint:startP1 toPoint:endP];
    
    // 3.开始仿真
    [self.animator addBehavior:gravity];
    [self.animator addBehavior:collision];
}
// 3.以圆作为边界
- (void)testGravityAndCollision3
{
    // 1.重力行为
    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] init];
    [gravity addItem:self.redView];
    
    // 2.碰撞检测行为
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] init];
    [collision addItem:self.redView];
    // 添加一个椭圆为碰撞边界
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 320, 200)];
    [collision addBoundaryWithIdentifier:@"circle" forPath:path];
    
    // 3.开始仿真
    [self.animator addBehavior:gravity];
    [self.animator addBehavior:collision];
}

  

四、捕捉行为

1.说明

可以让物体迅速冲到某个位置(捕捉位置),捕捉到位置之后会带有一定的震动

2.常见方法和属性

// 1.UISnapBehavior的初始化
- (instancetype)initWithItem:(id <UIDynamicItem>)item snapToPoint:(CGPoint)point;
// 2.用于减幅、减震(取值范围是0.0 ~ 1.0,值越大,震动幅度越小)
@property (nonatomic, assign) CGFloat damping;

3.UISnapBehavior使用注意

如果要进行连续的捕捉行为,需要先把前面的捕捉行为从物理仿真器中移除

4.代码实例

#import "SnapController.h"

@interface SnapController ()

@property (nonatomic, strong) UIDynamicAnimator *animator; // 物理仿真器
@property (nonatomic, strong) UIView *redView;

@end

@implementation SnapController

#pragma mark - 懒加载
// animator
- (UIDynamicAnimator *)animator
{
    if (!_animator) {
        _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    }
    return _animator;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self initUI]; // 界面
}

#pragma mark - 界面
- (void)initUI
{
    self.view.backgroundColor = [UIColor whiteColor];
    
    // redView
    _redView = [[UIView alloc] initWithFrame:CGRectMake(40, 70, 100, 100)];
    _redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:_redView];
}

#pragma mark - 触摸屏幕
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 获取一个触摸点
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:touch.view];
    
    // 1.创建捕捉行为
    // 需要传入两个参数:一个是物理仿真元素,一个是捕捉点
    UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.redView snapToPoint:point];
    // 设置防震系数(0-1,数值越大,震动的幅度越小)
    snap.damping = arc4random_uniform(10) / 10.0;
    
    // 2.执行捕捉行为
    // 注意:这个控件只能用在一个仿真行为上,如果要拥有持续的仿真行为,那么需要把之前的所有仿真行为删除
    // 删除之前的所有仿真行为
    [self.animator removeAllBehaviors];
    [self.animator addBehavior:snap];
}

@end

 

posted @ 2017-11-16 09:24  Frank9098  阅读(169)  评论(0)    收藏  举报