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感觉有用的话不妨给个星。

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

推荐阅读更多精彩内容