跟着斯坦福白胡子老头学UIDynamic动画的技巧

        UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架可以认为是一种物理引擎,能模拟和仿真现实生活中的物理重力、弹性碰撞、附着行为、捕捉行为、推动行为和动力元素等现象。

                                                

        首先要在stroyboard里添加一个UIView, 并设置constraint跟父View边界相同(点击Reset to Suggested Constraints), 即充满屏幕。

         我从这个Demo里总结了一些实际编码中可以用到的技巧, 供大家参数。

技巧1: 如果需要实现extension, 可以新建一个swift文件, 一个类/枚举/结构体的所有扩展都放在同一个文件里(例如在A文件里扩展UIView,在B文件也扩展UIView, 这个语法不好维护,对同一个数据结构的扩展要放在一起!); 扩展在整个程序内都有效;示例代码:

extension UIView {
    //根据坐标判断对应的UIView
    func hitTest(p: CGPoint) -> UIView? {
        return hitTest(p, with: nil)
    }
}

extension UIBezierPath {
    //静态函数, 画直线
    class func lineFrom(from: CGPoint, to: CGPoint) -> UIBezierPath  {
        let path = UIBezierPath()
        path.move(to: from)
        path.addLine(to: to)
        return path
    }
}

技巧2:得到随机数, 在Swift语言里使用arc4random()方法,实际编码中建议使用扩展语法实现。 示例代码是CGFloat, 还可以是Int、Double等等。

extension CGFloat {
    
    /**
     *  返回范围内的随机数
     * @param max, 最大值
    */
    static func random(max: Int) -> CGFloat {
        return CGFloat(arc4random() % UInt32(max))  // %是取余数
    }
}

技巧3: UIView是所有界面空间的基类, 能添加子UIView(这点跟Andriod的View不同); 注意父UIView可以添加子UIView, 但是需要使用子UIView的方法才能移除(这点跟Android的ViewGroup不同)。

        let drop = UIView(frame: frame)  //创建一个UIView
        addSubview(drop)  //添加drop到当前UIView里
        drop.removeFromSuperview()  //drop从父UIView中移除, 考虑一下drop执行了哪些生命周期函数?

技巧4: 使用闭包语法为变量赋初值, lazy关键字为懒加载,即运行时调用了animator变量后才会执行闭包代码。

   private lazy var animator: UIDynamicAnimator = {
        let animator = UIDynamicAnimator(referenceView: self)
        animator.delegate = self
        return animator
    }()

技巧5: 监听属性变化didSet/WillSet事件(观察者模式)并添加对应逻辑。 还记得前面博文提到的两个类相互引用的问题么,使用weak关键字解开闭环;注意下面代码, 如果在闭包里使用了self, 那么外部类实例和闭包之间形成了相互引用的关系, 这时需要使用[unowned self]避免内存泄漏。

    private var attachment: UIAttachmentBehavior? {
        willSet {
            if attachment != nil {
                ... //attachment值变化前,做逻辑
            }
        }
        didSet {
            if attachment != nil {
                ... //attachment值变化后,做逻辑
                attachment!.action = { [unowned self] in
                    if let attachedrop = self.attachment!.items.first as? UIView {
                        self.bezierPaths["line"] = UIBezierPath.lineFrom(from: (self.attachment?.anchorPoint)!, to: attachedrop.center)
                    }
                }
            }
        }
    }

技巧6: if逻辑判断需要where关键字的功能, 这时要使用逗号。 下面示例代码的意思是dropToAttachTo不是nil时才执行后面的语句dropToAttachTo.superview != nil , 如果条件都满足则进入代码块。

 if let dropToAttachTo = lastDrop, dropToAttachTo.superview != nil {
                attachment = UIAttachmentBehavior(item: dropToAttachTo, attachedToAnchor: gesturePoint)
 }


技巧7:对应Optional参数类型,即值可能为nil。 在Java语法里要写一堆的判空,语句间使用&&连接; Swift3.0省略了判空操作,再也不用写蛋疼的判空语句了。

请问下面的语句会崩溃吗?

         attachment = nil
         attachment?.anchorPoint = gesturePoint  //attatchment后面是问号,说明他是Optional类型
答: 不会!  

翻译一下: 如果attachment等于nil则不调用.后面的参数, 如果attachment有值则调用后面的参数。扩展一下可以是这样:  attachment?.param1?.param2?.param3?.someValue  , 如果用Java写这条语句要被累死!!!

技巧8: 自定义UIView绘制若干个图形时,可以使用Dictionary数组。 注意setNeedsDisplay函数类似于Android的invalidate,相当于设置个逻辑判断参数为true,UIView会在下个绘制周期时调用drawRect函数; 自定义UIView定义一个数组, 在drawRect函数里遍历并绘制。

  var bezierPaths = [String:UIBezierPath]() {
        didSet {
            setNeedsDisplay()  //触发刷新
        }
    }

    override func draw(_ rect: CGRect) {
        for (_, path) in bezierPaths {
            path.stroke()  //画线
        }
    }


代码下载地址: 

https://github.com/brycegao/DropIt#dropit




  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值