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 角阻力,物体旋转时候,旋转方向的阻力
  • 相关阅读:
    Constants and Variables
    随想
    C#基础篇之语言和框架介绍
    Python基础19 实例方法 类方法 静态方法 私有变量 私有方法 属性
    Python基础18 实例变量 类变量 构造方法
    Python基础17 嵌套函数 函数类型和Lambda表达式 三大基础函数 filter() map() reduce()
    Python基础16 函数返回值 作用区域 生成器
    Python基础11 List插入,删除,替换和其他常用方法 insert() remove() pop() reverse() copy() clear() index() count()
    Python基础15 函数的定义 使用关键字参数调用 参数默认值 可变参数
    Python基础14 字典的创建修改访问和遍历 popitem() keys() values() items()
  • 原文地址:https://www.cnblogs.com/madpanda/p/4742563.html
Copyright © 2011-2022 走看看