跟著斯坦福白胡子老頭學UIDynamic動畫的技巧

leonx918 7年前發布 | 8K 次閱讀 UIDynamic iOS開發 移動開發

UIDynamic是從iOS 7開始引入的一種新技術,隸屬于UIKit框架可以認為是一種物理引擎,能模擬和仿真現實生活中的物理重力、彈性碰撞、附著行為、捕捉行為、推動行為和動力元素等現象。

DropIt.gif

首先要在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

 

來自:http://www.jianshu.com/p/d115c941c2ab

 

 本文由用戶 leonx918 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!