iOS物理引擎-UIDynamic

UIKit动力学的引入,并不是为了替代CA或者UIView动画,在绝大多数情况下CA或者UIView动画仍然是最优方案,只有在需要引入逼真的交互设计的时候,才需要使用UIKit动力学它是作为现有交互设计和实现的一种补充

UIDynamic中的三个重要概念

  • Dynamic Animator:动画者,为动力学元素提供物理学相关的能力及动画,同时为这些元素提供相关的上下文,是动力学元素与底层iOS物理引擎之间的中介,将Behavior对象添加到Animator即可实现动力仿真

  • Dynamic Animator Item:动力学元素,是任何遵守了UIDynamicItem协议的对象,从iOS 7.0开始,UIView和UICollectionViewLayoutAttributes默认实现该协议。如果自定义的对象实现了该协议,即可通过Dynamic Animator实现物理仿真

  • UIDynamicBehavior:仿真行为,是动力学行为的父类,基本的动力学行为类UIGravityBehavior、UICollisionBehavior、UIAttachmentBehavior、UISnapBehavior、UIPushBehavior以及UIDynamicItemBehavior均继承自该父类

iOS7.0中提供的动力学行为包括:
  • UIGravityBehavior:重力行为
  • UICollisionBehavior:碰撞行为
  • UIAttachmentBehavior:附着行为
  • UISnapBehavior:吸附行为
  • UIPushBehavior:推行为
  • UIDynamicItemBehavior:动力学元素行为

所有的UIDynamicBehavior都是可以独立作用,同时也遵守力的合成。也就是说,组合使用行为可以实现一些较复杂的效果

重力行为(Gravity)

重力行为用于给动力学元素指定一个重力向量,代码实现如下:

@interface GravityBehaviorViewController ()

@property (nonatomic,strong)UIDynamicAnimator *animator;

@property (nonatomic,strong)UIView *greendView;

@property (nonatomic,strong)UIView *blueView;

@end

@implementation GravityBehaviorViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];

}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    //初始化动画者
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    // 创建重力行为
    UIGravityBehavior *behavior = [[UIGravityBehavior alloc] initWithItems:@[self.greendView]];
    //重力等级
    behavior.magnitude = 2;
    //重力方向
//    behavior.angle = M_PI_4;
    // 创建碰撞行为
    UICollisionBehavior *collision = [[UICollisionBehavior alloc] initWithItems:@[self.greendView]];
//    开启边缘检测
    collision.translatesReferenceBoundsIntoBoundary = YES;

    //添加行为
    [self.animator addBehavior:behavior];
    [self.animator addBehavior:collision];
    
}
-(UIView *)greendView{
    if (!_greendView) {
        _greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
        _greendView.backgroundColor = [UIColor greenColor];
        [self.view addSubview:_greendView];
    }
    return _greendView;
}
@end
重力行为

碰撞行为(Collision)

  • 碰撞行为用于指定一组动力学元素,在指定的边界范围内,可以彼此发生碰撞

  • 碰撞行为提供了代理方法,可用于在物体碰撞前、后对动力学元素做碰撞后续的处理

@interface CollisionViewController ()
@property (nonatomic,strong)UIDynamicAnimator *animator;

@property (nonatomic,strong)UIView *greendView;

@property (nonatomic,strong)UIView *blueView;
@end

@implementation CollisionViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    TestView *testView = [[TestView alloc] initWithFrame:self.view.bounds];
    testView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:testView];
    
    _blueView = [[UIView alloc] initWithFrame:CGRectMake(120, 250, 80, 80)];
    _blueView.backgroundColor = [UIColor blueColor];
    [self.view addSubview:_blueView];
    
    _greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    _greendView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:_greendView];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    
    UIGravityBehavior *graBehavoir = [[UIGravityBehavior alloc] initWithItems:@[self.greendView]];
    
    UICollisionBehavior *collBehavoir = [[UICollisionBehavior alloc] initWithItems:@[self.greendView,self.blueView]];
    //碰撞模式
    //    UICollisionBehaviorModeItems        元素与元素之间发生碰撞 (跟边界不碰撞)
    //    UICollisionBehaviorModeBoundaries   元素与边界发生碰撞 (与元素不碰撞)
    //    UICollisionBehaviorModeEverything   元素、边界都碰撞 (默认)
    collBehavoir.collisionMode = UICollisionBehaviorModeEverything;
    collBehavoir.translatesReferenceBoundsIntoBoundary = YES;
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(230, 370) radius:100 startAngle:0 endAngle:M_PI clockwise:YES];
    [collBehavoir addBoundaryWithIdentifier:@"ID" forPath:path];
    
    //动力学元素自身的行为
    UIDynamicItemBehavior *itemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[self.greendView]];
    itemBehavior.elasticity = 0.8;
    itemBehavior.friction = 0.1;
    
    [self.animator addBehavior:graBehavoir];
    [self.animator addBehavior:collBehavoir];
    [self.animator addBehavior:itemBehavior];
    
}

@end

下面是自定义的TestView中绘制的Bezier曲线,用来模拟碰撞效果

- (void)drawRect:(CGRect)rect{
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(230, 370) radius:100 startAngle:0 endAngle:M_PI clockwise:YES];

    [path stroke];
}

碰撞行为

吸附行为(Snap)

  • 吸附行为可以将视图通过动画吸附到某个点上
  • 初始化设定一下UISnapBehavior的initWithItem:snapToPoint:即可
@interface SanpViewController ()

@property (nonatomic,strong)UIDynamicAnimator *animator;
@property (nonatomic,strong)UIView *greendView;
@end

@implementation SanpViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    
    _greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    _greendView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:_greendView];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    
    UISnapBehavior *behavior = [[UISnapBehavior alloc] initWithItem:self.greendView snapToPoint:[touches.anyObject locationInView:self.view] ];
    behavior.damping = 0.9;
                                
    [self.animator addBehavior:behavior];
}

@end
吸附行为

附着行为(Attachment)

  • 附着行为描述一个视图与一个锚点或者另一个视图相连接的情况
  • 附着行为描述的是两点之间的连接情况,可以模拟刚性或者弹性连接
  • 在多个物体间设定多个UIAttachmentBehavior,可以模拟多物体连接
  • 常用属性:
    1. attachedBehaviorType:连接类型(连接到锚点或视图)
    2. items:连接视图数组
    3. anchorPoint:连接锚点
    4. length:距离连接锚点的距离
    5. 只要设置了以下两个属性,即为弹性连接
    6. damping:振幅大小
    7. frequency:振动频率
#import "AttachViewController.h"
#import "TestView.h"

@interface AttachViewController ()
@property (nonatomic,strong)UIDynamicAnimator *animator;
@property (nonatomic,strong)UIView *greendView;
@property (nonatomic,strong)TestView *testView;
@end

@implementation AttachViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.testView = [[TestView alloc] initWithFrame:self.view.bounds];
    self.testView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.testView];
    
    self.view.backgroundColor = [UIColor whiteColor];
    _greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    _greendView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:_greendView];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    
    __weak typeof(self)weakSelf = self;
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    
    
    UIAttachmentBehavior *behavior = [[UIAttachmentBehavior alloc] initWithItem:self.greendView attachedToAnchor:[touches.anyObject locationInView:self.view]];
    behavior.length = 180;
//    action为行为过程中的回调
    behavior.action = ^{
        weakSelf.testView.end = [self.view convertPoint:CGPointMake(50, 50) fromView:self.greendView];
        weakSelf.testView.start = [touches.anyObject locationInView:self.view];
    };
  
    UIGravityBehavior *graBehavior = [[UIGravityBehavior alloc] initWithItems:@[self.greendView]];

    [self.animator addBehavior:behavior];
    [self.animator addBehavior:graBehavior];
}
@end

此时TestView中的代码:

-(void)setEnd:(CGPoint)end{
    _end = end;
    
    [self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect{
 
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    
    [path moveToPoint:self.start];
    
    [path addLineToPoint:self.end];
    
    [path stroke];
}
附着行为

推行为(Push)

  • 推行为可以为一个视图施加一个作用力,该力可以是持续的,也可以是一次性的
  • 可以设置力的大小,方向和作用点等信息
@interface PushBViewController ()
@property (nonatomic,strong)UIDynamicAnimator *animator;
@property (nonatomic,strong)UIView *greendView;
@end

@implementation PushBViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    _greendView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    _greendView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:_greendView];
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    
    UIPushBehavior *pushB = [[UIPushBehavior alloc] initWithItems:@[self.greendView] mode:UIPushBehaviorModeContinuous];
    
    [pushB setAngle:0 magnitude:10];
    
    UICollisionBehavior *collisionB = [[UICollisionBehavior alloc] initWithItems:@[self.greendView]];
    collisionB.translatesReferenceBoundsIntoBoundary = YES;
    
    UIGravityBehavior *graB = [[UIGravityBehavior alloc] initWithItems:@[self.greendView]];
    
    [self.animator addBehavior:graB];
    [self.animator addBehavior:pushB];
    [self.animator addBehavior:collisionB];
    
}

@end


献上DEMO地址

你可能感兴趣的:(iOS物理引擎-UIDynamic)