跟著斯坦福白胡子老頭學UIDynamic動畫的技巧
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