zoukankan      html  css  js  c++  java
  • iOS动画——DynamicAnimate

    力学动画

    以dynamicAnimate为首的力学动画是苹果在iOS7加入的API,里面包含了很多力学行为,这套API是基于Box2d实现的。其中包含了重力、碰撞、推、甩、和自定义行为。

    涉及到的类如下

    涉及类 描述
    UIDynamicAnimator 相当于一个manager,用于管理所有添加的力学行为
    UIDynamicBehavior 所有力学行为的父类,是一个抽象类
    UIGravityBehavior 重力
    UICollisionBehavior 碰撞,弹力
    UIAttachmentBehavior 吸附力
    UIPushBehavior 推力
    UISnapBehavior 甩行力
    UIDynamicItemBehavior 自定义行为

    UIDynamicAnimator需要是一个实例变量,如果是局部变量动画会不起作用,个人感觉像是动作没有添加到RunLoop在函数执行结束后被释放了。

    UIDynamicAnimator的初始化需要绑定一个视图,而且与视图必须是一对一的关系。

    每一个行为都可以作用在很多的item上面,只要这个对象实现了<UIDynamicItem>协议,UIView默认就是所以不需要我们手动实现。

    下面我们看几个例子来逐个解释

    重力

    重力很简单,我们先看一个demo

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
        view.backgroundColor = [UIColor grayColor];
        [self.view addSubview:view];
        
        _animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
        UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[view]];
        [gravity setAngle:3.14/2 magnitude:0.5];
        
        [_animator addBehavior:gravity];
    }

    其中_animator是一个实例变量,上面解释过了。运行后会发现view像是在重力作用下向下做匀加速直线运动。

    上面代码我们让重力作于在view上面,同时设置了重力的方向和大小。

    @property (readwrite, nonatomic) CGVector gravityDirection;
    @property (readwrite, nonatomic) CGFloat angle;
    @property (readwrite, nonatomic) CGFloat magnitude;
    - (void)setAngle:(CGFloat)angle magnitude:(CGFloat)magnitude;

    上面是重力的方法和属性,我们逐个看一下。

    gravityDirection是重力向量,既然是向量就有方向和大小。使用的坐标系为UIKit坐标系,所以默认左上角为(0,0)点,而向量的大小就是重力的大小。

    angle为向量的方向,我们可以不通过gravityDirection来设定重力方向而用angle设置方向,因为angle更加的直观不用计算。

    同理用magnitude来设置重力的大小。

    弹力

    弹力是一个很有意思的行为,除了我们设置弹力的item外,还要设置弹力的边界。

    我们先看例子

    UICollisionBehavior *collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[view]];
    [collisionBehavior addBoundaryWithIdentifier:@"123ß" fromPoint:CGPointMake(0, 300) toPoint:CGPointMake(300, 600)];
    collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
    [_animator addBehavior:collisionBehavior];

    弹力中有一个属性

    @property (nonatomic, readwrite) BOOL translatesReferenceBoundsIntoBoundary;

    是否把关联视图设置为边界,这里的关联视图指的就是UIDynamicAnimator中的视图。把该属性设置为YES,运行代码,大家会发view掉落到底部时会与底部放生弹性碰撞。

    其实弹力行为提供了很多关于边界的方法

    - (void)setTranslatesReferenceBoundsIntoBoundaryWithInsets:(UIEdgeInsets)insets;
    - (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier forPath:(UIBezierPath*)bezierPath;
    - (void)addBoundaryWithIdentifier:(id <NSCopying>)identifier fromPoint:(CGPoint)p1 toPoint:(CGPoint)p2;
    - (UIBezierPath*)boundaryWithIdentifier:(id <NSCopying>)identifier;
    - (void)removeBoundaryWithIdentifier:(id <NSCopying>)identifier;

    这些都比较简单就不一一解释了,感兴趣大家可以自己试一下。

    下面着重介绍一个属性

    @property (nonatomic, assign, readwrite) id <UICollisionBehaviorDelegate> collisionDelegate;

    弹力有一个代理,是不是觉得很有意思,我们继续看例子

    - (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2 atPoint:(CGPoint)p
    {
        NSLog(@"began contact item");
    }
    
    - (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2
    {
        NSLog(@"end contanct item");
    }
    
    - (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(id <NSCopying>)identifier atPoint:(CGPoint)p
    {
        NSLog(@"began contact boundary");
    }
    
    - (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(id <NSCopying>)identifier
    {
        NSLog(@"end contact boundary");
    }

    我们实现弹力的代理并实现方法,运行程序,当初碰到底部的时候我们发现系统打印出了

    2015-08-19 15:31:49.123 TransAnimate[25564:17037174] began contact boundary
    2015-08-19 15:31:49.157 TransAnimate[25564:17037174] end contact boundary
    2015-08-19 15:31:49.524 TransAnimate[25564:17037174] began contact boundary
    2015-08-19 15:31:49.557 TransAnimate[25564:17037174] end contact boundary

    每次发生弹力将要作用和结束作用都会分别调用代理方法。

    根据方法名就可以明白,这两组代理方法一组针对物体碰撞,一组针对边界碰撞。

    吸附力

    关于吸附力,首先要解释一下,大家可以把吸附力理解为在吸附原点有一根棍,注意是棍不是绳子,连接着item。也就是说吸附力是刚性的。

    下面看demo

        UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:view attachedToAnchor:CGPointMake(100, 200)];
        attachment.length = 100;
        [_animator addBehavior:attachment];

    可以看到这里我们用的吸附力的构造方法是一个点,length就代表"棍"的长度,运行程序发现物体在重力的作用下会以Anchor为中心,以length为半径,稍微转一下。

    吸附力提供了很多的构造方法

    - (instancetype)initWithItem:(id <UIDynamicItem>)item attachedToAnchor:(CGPoint)point;
    - (instancetype)initWithItem:(id <UIDynamicItem>)item offsetFromCenter:(UIOffset)offset attachedToAnchor:(CGPoint)point;
    
    - (instancetype)initWithItem:(id <UIDynamicItem>)item1 attachedToItem:(id <UIDynamicItem>)item2;
    - (instancetype)initWithItem:(id <UIDynamicItem>)item1 offsetFromCenter:(UIOffset)offset1 attachedToItem:(id <UIDynamicItem>)item2 offsetFromCenter:(UIOffset)offset2;

    这两组构造方法的区别在于吸附对象,第一组是以点为对象,第二组以item为对象。

    再解释一下offset,这里指的是被吸附对象的锚点偏移量,默认是center。

    下面看一下属性

    @property (readonly, nonatomic) UIAttachmentBehaviorType attachedBehaviorType;
    
    @property (readwrite, nonatomic) CGPoint anchorPoint;
    
    @property (readwrite, nonatomic) CGFloat length;
    @property (readwrite, nonatomic) CGFloat damping; 
    @property (readwrite, nonatomic) CGFloat frequency; 

    UIAttachmentBehaviorType属性表明是吸附点是对象还是锚点。

    下面几个分别是锚点,吸附长度,阻力和振动频率,就不说了。

    推力
    和重力差不多这里就不举例子了看一下属性和方法

    - (void)setTargetOffsetFromCenter:(UIOffset)o forItem:(id <UIDynamicItem>)item;
    
    @property (nonatomic, readonly) UIPushBehaviorMode mode;
    @property (nonatomic, readwrite) BOOL active;
    
    @property (readwrite, nonatomic) CGFloat angle;
    
    @property (readwrite, nonatomic) CGFloat magnitude;
    @property (readwrite, nonatomic) CGVector pushDirection;
    
    - (void)setAngle:(CGFloat)angle magnitude:(CGFloat)magnitude;

    下面的angle、magnitude、pushDirection和重力一模一样不多说了。

    重点说一下UIPushBehaviorMode和active

    UIPushBehaviorMode表示该推力是持续作用还是短暂作用,active表示推力是否还在作用。

    上面的- (void)setTargetOffsetFromCenter:(UIOffset)o forItem:(id <UIDynamicItem>)item;方法是说推力作用点的偏移量,默认是center。

    甩行力

    甩行力我也解释不是很清楚,我觉的可以理解为一个黑洞在吸附物体吧,这样应该挺形象的...

    直接看例子吧

    我们用故事板对控制器添加一个tapGesture的事件

    - (IBAction)handleGesture:(UIGestureRecognizer *)sender {
        CGPoint point = [sender locationInView:self.view];
        UISnapBehavior *snap = [[UISnapBehavior alloc] initWithItem:view snapToPoint:point];
        [_animator addBehavior:snap];
    }

    运行程序发现我们点击的位置,view会直接飞过去并且中间会有阻力。

    甩行力只有一个属性,就是阻力。

    自定义行为

    一般我们都是用不上自定义行为的,只有在少数时候需要自己定制

    我们可以定制的属性有很多

    属性 描述
    desnsity 密度,如果一个100*100点的物体,它的密度为1.0,作用力是1.0那么它的加速度就是100点/S2
    elasticity 弹力洗漱,取值范围0.0~1.0,0.0代表没有弹力,1.0代表返券弹性碰撞
    friction 摩擦系数,0.0表示没有摩擦力,1.0表示摩擦力很强,如果要设置更强的可以大于1
    resistance 阻力,物体运动的时候,在线性方向的阻力,0.0没有阻力,CGFLOAT_MAX表示最大阻力
    allowRotation 是否允许旋转。
    angularResistance 角阻力,物体旋转时候,旋转方向的阻力
  • 相关阅读:
    第十三周课程总结
    第十二周学习总结
    第十一周课程总结
    第十周课程总结
    第九周课程总结&实验报告(七)
    第八周课程总结&实验报告(六)
    第七周课程总结&实验报告(五)
    第六周学习总结&java实验报告四
    课程总结
    第十四周学习总结&课程实验报告
  • 原文地址:https://www.cnblogs.com/madpanda/p/4742563.html
Copyright © 2011-2022 走看看