ios物理引擎-UIDynamicAnimator

开始一本正经的胡说八道,如有错误请包含,请指出。

物体的几种物理行为:

1. UIAttachmentBehavior 吸附固定行为

2. UICollisionBehavior 碰撞行为

3. UIGravityBehavior 重力行为

4. UIPushBehavior 推力行为

5. UISnapBehavior 甩行为(暂且这么叫)


UIAttachmentBehavior:

UIAttachmentBehavior的初始化方法有好几种,可以通过构造器也可以通过类方法,不外乎两大类型,物体和物体、物体和点之间。具体的可以看下文档。这里介绍下管于该类型的一些属性和简单的使用方法。
items:一个UIAttachmentBehavior对象所包含的物体
attachedBehaviorType:附属或者吸附行为的类型get类型在生成一个 UIAttachmentBehavior的对象时已经决定了,就是物体和物体之间的附属行为,或者物体和一个点之间的附属行为
length:两个物体之间的距离(或者是点和物体之间的距离)
damping:吸附还原时的阻力大小
frequency:震荡频率
frictionTorque:克服一个物体做圆周运动的力的大小
attachmentRange:吸附行为的运动范围
例子:

import UIKit

/** 吸附行为 */

class AttachViewController: UIViewController {
    
    var attachButton = UIButton()
    var animator = UIDynamicAnimator()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "吸附行为"
        view.backgroundColor = UIColor.darkGray
        attachButton = UIButton(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
        attachButton.backgroundColor = UIColor.blue
        view.addSubview(attachButton)
        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        showAttach()
    }
    
    func showAttach() {
        
        animator = UIDynamicAnimator(referenceView: self.view)
        animator.delegate = self
        
        let attachBehavior = UIAttachmentBehavior(item: attachButton, attachedToAnchor: attachButton.center)
        attachBehavior.length = 20
        //设置吸附还原时的阻力系数大小
        attachBehavior.damping = 0.1
        //设置震荡频率
        attachBehavior.frequency = 2.3
        animator.addBehavior(attachBehavior)
        
        //加一个推力 推力的类型有两种一种是瞬间的推力,一种是连续的 分别对应的是 instantaneous 和  continuous
        let pushBehavior = UIPushBehavior(items: [attachButton], mode: .instantaneous)
        //设置推力可用
        pushBehavior.active = true
        //设置力的方向
        pushBehavior.pushDirection = CGVector(dx: 1, dy: 0)
        //设置力的大小
        pushBehavior.magnitude = 100
        animator.addBehavior(pushBehavior)
    }
}

extension AttachViewController : UIDynamicAnimatorDelegate {
    
    func dynamicAnimatorDidPause(_ animator: UIDynamicAnimator) {
        print("\(#function)")
    }
    
}


UICollisionBehavior

只介绍属性,方法自己看
collisionMode:碰撞类型有三种items(物体和物体间的碰撞)、boundaries(物体和边界的朋转)、everything(不管什么)
translatesReferenceBoundsIntoBoundary:添加物理行为时,必须将这些物理行为添加到UIDynamicAnimator对象中,在创建UIDynamicAnimator对象时UIDynamicAnimator(referenceView: self.view)方法会指定一个UIView,translatesReferenceBoundsIntoBoundary属性就是是否碰撞边界为这个UIView的边界,之后只要添加到这个UIDynamicAnimator上的物理行为,都是发生在该UIView的坐标系统中。

import UIKit

/** 碰撞行为 */

class CollisionViewController: UIViewController {

    var attachButton = UIButton()
    var newButton = UIButton()
    var animationer = UIDynamicAnimator()

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "碰撞行为"
        view.backgroundColor = UIColor.darkGray
        attachButton = UIButton(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
        attachButton.backgroundColor = UIColor.blue
        view.addSubview(attachButton)
        
        
        newButton = UIButton(frame: CGRect(x: 160, y: 100, width: 50, height: 50))
        newButton.backgroundColor = UIColor.red
        self.view.addSubview(newButton)
        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        collisionAnimation()
    }
    
    fileprivate func collisionAnimation() {
        animationer = UIDynamicAnimator(referenceView: self.view)
        
        //给物体一个力,这个力可以是重力,也可以是通过UIPushBehavior自定义的一个推力
        
        let gBehavior = UIGravityBehavior(items: [attachButton])
        gBehavior.gravityDirection = CGVector(dx: 0, dy: 1)
        animationer.addBehavior(gBehavior)
        
        let collisionBehavior = UICollisionBehavior(items: [attachButton])
        collisionBehavior.translatesReferenceBoundsIntoBoundary = true
        collisionBehavior.collisionMode = .everything
        animationer.addBehavior(collisionBehavior)
    }
    
}

UIGravityBehavior只介绍属性,方法自己看文档

gravityDirection:重力方向 是一个CGVector类型的结构体,(0,1)代表竖直向下,(0,-1)代表竖直向上,(1,0)水平向右。(-1,0)水平向左
angle: 力的角度
magnitude: 力的大小

class GravityViewController: UIViewController,UIDynamicAnimatorDelegate  {

    let ball = Ellipse()
    let circile = UIView()
    var animator = UIDynamicAnimator()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "重力行为"
        view.backgroundColor = UIColor.darkGray
        // Do any additional setup after loading the view.
        
        ball.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
        ball.layer.cornerRadius = min(ball.bounds.size.width / 2, ball.bounds.size.height / 2)
        ball.layer.masksToBounds = true
        ball.backgroundColor = UIColor.blue
        self.view.addSubview(ball)
        
        circile.frame = CGRect(x: 10, y: 10, width: 10, height: 10)
        circile.backgroundColor = UIColor.black
        circile.layer.cornerRadius = min(circile.bounds.size.width / 2, circile.bounds.size.height / 2)
        circile.layer.masksToBounds = true
        ball.addSubview(circile)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        gravity()
    }
    
    func gravity() {
        /*
            注意**: 当只给物体添加一个重力行为时,物体是无限制下落的,尽管我们的屏幕尺寸只有那么大;
            就好比一个物体从一个无限高的地方下落,永远都不会着地,所以下落也就不会结束。
            dynamicAnimatorDidPause这个函数也不会运行.
            所以在这里我给物体加一个碰撞边界
         */
        animator = UIDynamicAnimator(referenceView: self.view)
        animator.delegate = self
        
        let gravityBehavior = UIGravityBehavior(items: [ball])
        gravityBehavior.magnitude = 2
        gravityBehavior.gravityDirection = CGVector(dx: 0, dy: 1)
        gravityBehavior.angle = CGFloat(M_PI_4)
        animator.addBehavior(gravityBehavior)
        
        let colission = UICollisionBehavior(items: [ball])
        colission.translatesReferenceBoundsIntoBoundary = true
        colission.collisionMode = .everything
        animator.addBehavior(colission)
        
        let itemBehavior = UIDynamicItemBehavior(items: [ball])
        itemBehavior.elasticity = 0.4
        animator.addBehavior(itemBehavior)
    }
    
    func dynamicAnimatorDidPause(_ animator: UIDynamicAnimator) {
        print("\(#function)")
    }
    
    func dynamicAnimatorWillResume(_ animator: UIDynamicAnimator) {
        print("\(#function)")
    }
    
}

class Ellipse: UIView {
    override var collisionBoundsType: UIDynamicItemCollisionBoundsType {
        return .ellipse
    }
}


UIPushBehavior推力行为

mode:在初始话推力时要定义推力的类型,有两种类型continuous、instantaneous,分别是连续的力和瞬间的力
active: 当前推力是否可用
angle: 推力的角度
magnitude: 推力的大小
pushDirection:推理的方向和重力的方向一样

class PushViewController: UIViewController {
    
    let ball = Ellipse()
    let circile = UIView()
    var animator = UIDynamicAnimator()

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "推动行为"
        view.backgroundColor = UIColor.darkGray
        
        ball.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
        ball.layer.cornerRadius = min(ball.bounds.size.width / 2, ball.bounds.size.height / 2)
        ball.layer.masksToBounds = true
        ball.backgroundColor = UIColor.blue
        self.view.addSubview(ball)
        // Do any additional setup after loading the view.
        circile.frame = CGRect(x: 10, y: 10, width: 10, height: 10)
        circile.backgroundColor = UIColor.black
        circile.layer.cornerRadius = min(circile.bounds.size.width / 2, circile.bounds.size.height / 2)
        circile.layer.masksToBounds = true
        ball.addSubview(circile)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        push()
    }
    
    func push() {
        animator = UIDynamicAnimator(referenceView: self.view)
        
        //推力在之前已经做过介绍,可以看碰撞行为的代码
        let pushBehavior = UIPushBehavior(items: [ball], mode: .continuous)
        pushBehavior.active = true
        pushBehavior.magnitude = 1
        pushBehavior.pushDirection = CGVector(dx: 0, dy: 1)
        animator.addBehavior(pushBehavior)
        
        let colission = UICollisionBehavior(items: [ball])
        colission.translatesReferenceBoundsIntoBoundary = true
        colission.collisionMode = .everything
        animator.addBehavior(colission)
        
        let itemBehavior = UIDynamicItemBehavior(items: [ball])
        itemBehavior.elasticity = 0.4
        itemBehavior.addAngularVelocity(-3, for: itemBehavior.items.first!)
        animator.addBehavior(itemBehavior)
    }
    
}

class Ellipse: UIView {
    override var collisionBoundsType: UIDynamicItemCollisionBoundsType {
        return .ellipse
    }
}

UISnapBehavior甩行为

snapPoint: 将物体甩向这个点
damping:将物体甩向一个指定的点后,物体会震荡一会,damping为震荡系数,默认值为0.5,取值范围为0-1太大了的话可能看不到震荡效果

class SnapViewController: UIViewController {
    
    let ball = Ellipse()
    let circile = UIView()
    var animator = UIDynamicAnimator()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "迅速移动"
        view.backgroundColor = UIColor.darkGray
        
        ball.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
        ball.layer.cornerRadius = min(ball.bounds.size.width / 2, ball.bounds.size.height / 2)
        ball.layer.masksToBounds = true
        ball.backgroundColor = UIColor.blue
        self.view.addSubview(ball)
        // Do any additional setup after loading the view.
        circile.frame = CGRect(x: 10, y: 10, width: 10, height: 10)
        circile.backgroundColor = UIColor.black
        circile.layer.cornerRadius = min(circile.bounds.size.width / 2, circile.bounds.size.height / 2)
        circile.layer.masksToBounds = true
        ball.addSubview(circile)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        snap()
    }
    
    func snap() {
        animator = UIDynamicAnimator(referenceView: self.view)
        
        let snapBehavior = UISnapBehavior(item: ball, snapTo: CGPoint(x:UIScreen.main.bounds.size.width + 20,y:500))
        //将物体甩向一个设定的点,停止在指定的点时,会震荡摇晃一会
        snapBehavior.damping = 0.1
        animator.addBehavior(snapBehavior)
    }
}

extension AttachViewController : UIDynamicAnimatorDelegate {
    
    func dynamicAnimatorDidPause(_ animator: UIDynamicAnimator) {
        print("\(#function)")
    }
    
}


最后的最后说说在添加这些物理属性时要注意的问题和UIDynamicItemBehavior:
因为说的时物理引擎,给物体加上一些物理属性,所以再给一些物体加上物理行为之后如碰撞、吸附时不要忘记给物体加一个力,重力或者一个自定义的推力,要不然物体怎么运动?
再给物体加上重力效果时,记得要加上一个碰撞边界要不然物体下落的这个过程永远执行不完。就好比从一个无限高的地方扔下一块石头,石头永远不会着地。
UIDynamicItemBehavior:可以给物体增加一些物理属性
elasticity:弹性系数
friction:摩擦力
density:密度
resistance:线性方向阻力
charge:代表能够影响一个元素在电磁场上如何移动的电荷(是的,听起来很疯狂) 网找的,不明白是什么鬼
isAnchored:本质上是将图形变成了碰撞中的一个静态物体,但没有响应事件(如果有什么东西撞上了它,它会丝毫不动),所以可以完美地用来表示地板或墙壁
angularResistance:角阻力,物体旋转时的阻力
allowsRotation:是否允许物体旋转

关于上面这些内容我写了个小Demo感觉有用的话不妨给个星。

你可能感兴趣的:(ios物理引擎-UIDynamicAnimator)