UIDynamic(物理仿真)

简介

  1. 什么是UIDynamic
    • UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架
    • 可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象
    • 如: 重力、弹性碰撞等现象
  2. 物理引擎的价值
    • 广泛用于游戏开发,经典成功案例是“愤怒的小鸟”
    • 让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果
    • 提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏
  3. 知名的2D物理引擎
    • Box2d
    • Chipmunk

三大概念

  1. 三大概念

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

    1. 注意
      1. 不是任何对象都能做物理仿真元素
      2. 不是任何对象都能进行物理仿真
        1. 哪些对象才能做物理仿真元素
      3. 任何遵守了UIDynamicItem协议的对象
      4. UIView默认已经遵守了UIDynamicItem协议,因此任何UI控件都能做物理仿真
      5. UICollectionViewLayoutAttributes类默认也遵守UIDynamicItem协议
  3. 物理仿真行为

    1. UIDynamic提供了以下几种物理仿真行为
       UIGravityBehavior:重力行为
       UICollisionBehavior:碰撞行为
       UISnapBehavior:捕捉行为
       UIPushBehavior:推动行为
       UIAttachmentBehavior:附着行为
       UIDynamicItemBehavior:动力元素行为
      
    2. 物理仿真行为须知
      1. 上述所有物理仿真行为都继承自UIDynamicBehavior
      2. 所有的UIDynamicBehavior都可以独立进行
      3. 组合使用多种行为时,可以实现一些比较复杂的效果
  4. 物理仿真器

    1. 物理仿真器须知

      • 它可以让物理仿真元素执行物理仿真行为
      • 它是UIDynamicAnimator类型的对象
    2. UIDynamicAnimator的初始化

      • - (instancetype)initWithReferenceView:(UIView )view;
      • view参数:是一个参照视图,表示物理仿真的范围
    3. UIDynamicAnimator的常见方法

           - (void)addBehavior:(UIDynamicBehavior *)behavior;
           添加1个物理仿真行为
      
           - (void)removeBehavior:(UIDynamicBehavior *)behavior;
           移除1个物理仿真行为
      
           - (void)removeAllBehaviors;
           移除之前添加过的所有物理仿真行为
      
    4. UIDynamicAnimator的常见属性

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

      1. 创建一个物理仿真器(顺便设置仿真范围)

      2. 创建相应的物理仿真行为(顺便添加物理仿真元素)

      3. 将物理仿真行为添加到物理仿真器中 -> 开始仿真

       

      Demo

      1. 环境准备
        1. 创建项目
        2. 设置不使用autolayout(这里仅仅是为了简单,物理仿真支持autolayout,在愤怒的小鸟中演示)
        3. 设置屏幕的尺寸为5.5 因为Xcode7 默认使用是iPhone6 s Plus
        4. 拖入一个View,颜色设置蓝色,大小设置为100,100
        5. 连线到控制器中,属性为blueView
      2. 让蓝色View执行仿真重力仿真行为,代码如下:

            - (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
            {
                //  创建一个物理仿真器,并指定参照系为控制器的View
                    UIDynamicAnimator *dynamicAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
                //  创建重力仿真行为并指定仿真元素为蓝色的view
                    UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.blueView]];
                //  把仿真行为添加到仿真器中
                    [dynamicAnimator addBehavior:gravity];
            }
        

        点击控制器的View,没有任何反应,这是因为还要没有执行动画物理仿真器就销毁,所以我们需要对物理仿真器有一个强引用 懒加载物理仿真器

        1. 定义物理仿真器属性

               @property (nonatomic, strong) UIDynamicAnimator *dynamicAnimator;
          
        2. 懒加载物理仿真器器

               - (UIDynamicAnimator *)dynamicAnimator
               {
                   if (_dynamicAnimator == nil) {
                       //  创建一个物理仿真器,并指定参照系为控制器的View
                       _dynamicAnimator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
                   }
                   return _dynamicAnimator;
               }
          
          1. 修改touchesBegan方法

             - (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
            {
            //  创建重力仿真行为并指定仿真元素为蓝色的view
              UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.blueView]];
            //  把仿真行为添加到仿真器中
              [self.dynamicAnimator addBehavior:gravity];
            }
            
          2. 运行,点击控制器View,蓝色View向下运动,然后从下面出去了
      3. 如果想让blueView到底部停止还需要添加一个碰撞行为

                - (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
                 {
                 //  创建重力仿真行为并指定仿真元素为blueView
                     UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.blueView]];
        
                 //  创建一个碰撞的仿真行为并指定仿真元素为blueView
                     UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.blueView]];
                 //  设置仿真边界为碰撞边界
                     collision.translatesReferenceBoundsIntoBoundary = YES;
                 //  添加碰撞行为到仿真器中
                     [self.dynamicAnimator addBehavior:collision];
        
                 //  把仿真行为添加到仿真器中
                     [self.dynamicAnimator addBehavior:gravity];
                 }
        
      4. 在storyboard上添加一个添加一个红色View,在蓝色的View下方,运行程序蓝色的View会从红色的View上方飘过,这是因为此时红色的View并没有添加到仿真器中,如果你希望蓝色的View撞到红色的View那么就需要把红色的View添加到碰撞的仿真行为中 1.红色View的拖线到控制器中,属性名称为redView 2.把红色View添加到碰撞仿真行为中

             UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.blueView,self.redView]];
        
        1. 运行程序,点击屏幕,红色的View被推下去了;把红色的View放到蓝色View能砸到其角上的一个位置,测试,会有惊喜发现哦!
      5. 添加碰撞边界

        1. 添加直线边界

               - (void) touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
               {
               //  创建重力仿真行为并指定仿真元素为blueView
                   UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.blueView]];
          
               //  创建一个碰撞的仿真行为并指定仿真元素为blueView
                   UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.blueView]];
               //  设置仿真边界为碰撞边界
                   collision.translatesReferenceBoundsIntoBoundary = YES;
          
               //  添加直线碰撞边界
                   [collision addBoundaryWithIdentifier:@"line" fromPoint:CGPointMake(0, self.view.frame.size.height * 0.5) toPoint:CGPointMake(self.view.frame.size.width, self.view.frame.size.height)];
          
               //  添加碰撞行为到仿真器中
                   [self.dynamicAnimator addBehavior:collision];
          
               //  把仿真行为添加到仿真器中
                   [self.dynamicAnimator addBehavior:gravity];
               }
          
        2. 添加一个贝塞尔曲线碰撞边界,把添加直线碰撞边界的代码修改如下代码

               //  添加贝塞尔曲线碰撞边界
               UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width)];
               [collision addBoundaryWithIdentifier:@"bezier" forPath:bezierPath];
          
      6. 重力行为的常见属性

         ////  设置重力的角度,默认为M_PI_2(向下),0为右,M_PI向左
         //    gravity.angle = 0;
         ////  设置重力的大小,默认是1
         //    gravity.magnitude = 2;
        
         //  设置重力向量,(0,0) -> (1,1) 之间的连线,大小为:根号2
             gravity.gravityDirection = CGVectorMake(1, 1);
        
      7. 捕获行为

          //  相同类型仿真行为只能添加到仿真器一次,所以在添加仿真行为之前,移除之前添加仿真行为
             [self.dynamicAnimator removeAllBehaviors];
        
         //  获取描述用户点击的对象
             UITouch *touch = [touches anyObject];
         //  获取用户点击的位置
             CGPoint loc =  [touch locationInView:self.view];
         //  创建一个捕获行为
             UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:self.blueView snapToPoint:loc];
        
         //  设置减震效果,取值范围为 0.0 to 1.0. 0表示最小减震效果,也就是振幅最大
             snap.damping = 0;
        
         //  把捕获行为添加到仿真器中
             [self.dynamicAnimator addBehavior:snap];
      8. Demo2  愤怒的小鸟

          1. 视图
posted @ 2015-11-13 20:54  编程达人丶  阅读(205)  评论(0编辑  收藏  举报