带你了解UIKit动力学

一、简单介绍

1.什么是UIDynamic
UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象如:重力、弹性碰撞等现象

2.物理引擎的价值
广泛用于游戏开发,经典成功案例是“愤怒的小鸟”,让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏

3.知名的2D物理引擎
Box2d
Chipmunk

二、使用步骤

要想使用UIDynamic来实现物理仿真效果,大致的步骤如下:

1.创建一个物理仿真器(顺便设置仿真范围)
2.创建相应的物理仿真行为(顺便添加物理仿真元素)
3.将物理仿真行为添加到物理仿真器中开始仿真

三、相关说明

1.三个概念

  • 谁要进行物理仿真?
      物理仿真元素(Dynamic Item)
  • 执行怎样的物理仿真效果?怎样的动画效果?
      物理仿真行为(Dynamic Behavior)
  • 让物理仿真元素执行具体的物理仿真行为
      物理仿真器(Dynamic Animator)

2.物理仿真元素
注意:
不是任何对象都能做物理仿真元素
不是任何对象都能进行物理仿真

物理仿真元素要素:
任何遵守了UIDynamicItem协议的对象
UIView默认已经遵守了UIDynamicItem协议,因此任何UI控件都能做物理仿真

UICollectionViewLayoutAttributes类默认也遵守UIDynamicItem协议

3.物理仿真行为

(1)UIDynamic提供了以下几种物理仿真行为

  • UIGravityBehavior:重力行为
  • UICollisionBehavior:碰撞行为

  • UISnapBehavior:捕捉行为

  • UIPushBehavior:推动行为

  • UIAttachmentBehavior:附着行为

  • UIDynamicItemBehavior:动力元素行为

(2)物理仿真行为须知
上述所有物理仿真行为都继承自UIDynamicBehavior,所有的UIDynamicBehavior都可以独立进行组合使用多种行为时,可以实现一些比较复杂的效果

4.物理仿真器

(1)物理仿真器须知
它可以让物理仿真元素执行物理仿真行为
它是UIDynamicAnimator类型的对象
(2)UIDynamicAnimator的初始化

- (instancetype)initWithReferenceView:(UIView *)view;
`</pre>

view参数:是一个参照视图,表示物理仿真的范围

5.物理仿真器的说明

(1)UIDynamicAnimator的常见方法

<pre>`//移除之前添加过的所有物理仿真行为  
- (void)addBehavior:(UIDynamicBehavior *)behavior; 
//移除之前添加过的所有物理仿真行为 
- (void)removeBehavior:(UIDynamicBehavior *)behavior; 
//添加1个物理仿真行为 
- (void)removeAllBehaviors; 
`</pre>

(2)UIDynamicAnimator的常见属性

<pre>` //参照视图 
@property (nonatomic, readonly) UIView* referenceView;  
//添加到物理仿真器中的所有物理仿真行为
@property (nonatomic, readonly, copy) NSArray* behaviors;
//是否正在进行物理仿真
@property (nonatomic, readonly, getter = isRunning) BOOL running;
//代理对象(能监听物理仿真器的仿真过程,比如开始和结束)
@property (nonatomic, assign) id 
`</pre>

### 四、案例介绍

**先看效果吧**
- UIGravityBehavior:重力行为

![UIGravityBehavior.gif](http://upload-images.jianshu.io/upload_images/1868951-9cd5fca7213318f5.gif?imageMogr2/auto-orient/strip)
  • UICollisionBehavior:碰撞行为

    UICollisionBehavior.gif

  • UISnapBehavior:捕捉行为

    UISnapBehavior.gif

  • UIPushBehavior:推动行为

    UIPushBehavior.gif

  • 复合效果

    复合效果.gif

    最后是代码

    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    <pre>`#import &lt;UIKit/UIKit.h&gt;
    @interface SecondViewController : UIViewController
    @property (weak, nonatomic) IBOutlet UIView *blueView;
    @property (weak, nonatomic) IBOutlet UIView *orangeView;
    @property (weak, nonatomic) IBOutlet UISegmentedControl *segmented;
    @property (weak, nonatomic) IBOutlet UIView *redView;
    @property (nonatomic,strong) UIDynamicAnimator *animator;
    @end
    #import "SecondViewController.h"
    @interface SecondViewController ()
    @property (nonatomic,strong)UISnapBehavior *snapBehavior;
    @property (nonatomic,strong)UIPushBehavior *pushBehavior;
    @property (nonatomic,strong)UIAttachmentBehavior *attachmentBehavior;
    @end
    @implementation SecondViewController
    - (void)viewDidLoad {
    [super viewDidLoad];
    self.blueView.transform = CGAffineTransformMakeRotation(M_PI_4);
    self.segmented.transform = CGAffineTransformMakeRotation(-M_PI / 8);
    if (!_pushBehavior) {
    _pushBehavior = [[UIPushBehavior alloc]initWithItems:@[_blueView] mode:UIPushBehaviorModeContinuous];
    }
    _pushBehavior.active = YES;
    _pushBehavior.pushDirection = CGVectorMake(10.0f, 10.0f);
    _pushBehavior.magnitude = 1.0f;
    [self.animator addBehavior:_pushBehavior];
    }
    - (void)touchesBegan:(NSSet&lt;UITouch *&gt; *)touches withEvent: (UIEvent *)event {
    _pushBehavior.active = NO;
    //创建重力行为
    UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc]init];
    [gravityBehavior addItem:self.blueView];
    [gravityBehavior addItem:self.orangeView];
    [gravityBehavior addItem:self.segmented];
    gravityBehavior.gravityDirection = CGVectorMake(-0.3f, 1.0f);
    //加速度
    gravityBehavior.magnitude = 3;
    //创建碰撞行为
    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc]init];
    //碰撞类型为元素和边界
    collisionBehavior.collisionMode = UICollisionBehaviorModeEverything;
    CGFloat Y = self.view.frame.size.height - CGRectGetHeight(self.redView.frame);
    CGFloat X = self.view.frame.size.width;
    CGFloat height = self.view.frame.size.height;
    //设置红色的View为底边界,左边框跟右边框作为边界
    [collisionBehavior addBoundaryWithIdentifier:@"collision1" fromPoint:CGPointMake(0,Y) toPoint:CGPointMake(X, Y)];
    [collisionBehavior addBoundaryWithIdentifier:@"collision2" fromPoint:CGPointMake(0, 0) toPoint:CGPointMake(0, height)];
    [collisionBehavior addBoundaryWithIdentifier:@"collision3" fromPoint:CGPointMake(X,0) toPoint:CGPointMake(X, height)];
    [collisionBehavior addItem:self.blueView];
    [collisionBehavior addItem:self.segmented];
    [collisionBehavior addItem:self.orangeView];
    [self.animator addBehavior:collisionBehavior];
    [self.animator addBehavior:gravityBehavior];
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.blueView]];
    [itemBehavior setElasticity:0.5];
    [self.animator addBehavior:itemBehavior];
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapGesture:)];
    [self.view addGestureRecognizer:tap];
    }
    - (void)tapGesture:(UITapGestureRecognizer *)gesture
    {
    CGPoint tapPoint = [gesture locationInView:self.view];
    if (_snapBehavior) {
    [self.animator removeBehavior:_snapBehavior];
    _snapBehavior = nil;
    }
    _snapBehavior = [[UISnapBehavior alloc]initWithItem:self.blueView snapToPoint:tapPoint];
    _snapBehavior.action = ^(){
    NSLog(@"UISnapBehavior 在执行");
    };
    _snapBehavior.damping = 0.9;
    [self.animator addBehavior:_snapBehavior];
    }
    - (UIDynamicAnimator *)animator{
    if (_animator == nil) {
    _animator = [[UIDynamicAnimator alloc]init];
    }
    return _animator;
    }
    - (void)panGestureRecognizer:(UIPanGestureRecognizer *)gesture
    {
    CGPoint location = [gesture locationInView:self.view];
    CGPoint imageLocation = [gesture locationInView:self.orangeView];
    switch (gesture.state) {
    case UIGestureRecognizerStateBegan:
    {
    NSLog(@"touch position %@",NSStringFromCGPoint(location));
    NSLog(@"loction in image %@",NSStringFromCGPoint(imageLocation));
    [self.animator removeAllBehaviors];
    UIOffset centerOffset = UIOffsetMake(imageLocation.x - CGRectGetMidX(self.orangeView.bounds), imageLocation.y - CGRectGetMidY(self.orangeView.bounds));
    _attachmentBehavior = [[UIAttachmentBehavior alloc]initWithItem:self.orangeView offsetFromCenter:centerOffset attachedToAnchor:location];
    _attachmentBehavior.damping = 0.5;
    _attachmentBehavior.frequency = 0.8;
    [self.animator addBehavior:_attachmentBehavior];
    }
    break;
    case UIGestureRecognizerStateEnded:
    {
    [self.animator removeBehavior:_attachmentBehavior];
    }
    break;
    default:
    {
    [_attachmentBehavior setAnchorPoint:[gesture locationInView:self.view]];
    }
    break;
    }
    }
    - (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }
    /*
    #pragma mark - Navigation
    // In a storyboard-based application, you will often want to do a little preparation before navigation
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
    }
    */
    @end

UIKit动力学的部分介绍完了.
最开始项目中只是用到了重力跟碰撞行为,参考学习了:UIDynamic 详细用法 中的案例.然后自己又展开了解了下UIKit动力学的知识,把捕捉行为,推动行为加了进去,做个完善.整理了一下.就是这样.