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<UITouch *> *)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<UITouch *> *)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<UITouch *> *)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<UITouch *> *)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<UITouch *> *)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地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容