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

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

DropIt.gif

首先要在stroyboard里添加一個UIView, 并設置constraint跟父View邊界相同(點擊Reset to Suggested Constraints), 即充滿屏幕。
我從這個Demo里總結了一些實際編碼中可以用到的技巧, 供大家參數。

技巧1: 如果需要實現extension, 可以新建一個Swift文件, 一個類/枚舉/結構體的所有擴展都放在同一個文件里(例如在A文件里擴展UIView,在B文件也擴展UIView, 這個語法不好維護,對同一個數據結構的擴展要放在一起!); 擴展在整個程序內都有效;示例代碼:
<pre>
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
}
}
</pre>

技巧2:得到隨機數, 在Swift語言里使用arc4random()方法,實際編碼中建議使用擴展語法實現。 示例代碼是CGFloat, 還可以是Int、Double等等。
<pre>
extension CGFloat {

/**
 *  返回范圍內的隨機數
 * @param max, 最大值
*/
static func random(max: Int) -> CGFloat {
    return CGFloat(arc4random() % UInt32(max))
}

}
</pre>

技巧3: UIView是所有界面空間的基類, 能添加子UIView(這點跟Andriod的View不同); 注意父UIView可以添加子UIView, 但是需要使用子UIView的方法才能移除(這點跟Android的ViewGroup不同)。
<pre>
let drop = UIView(frame: frame) //創建一個UIView
addSubview(drop) //添加drop到當前UIView里
drop.removeFromSuperview() //drop從父UIView中移除, 考慮一下drop執行了哪些生命周期函數?
</pre>

技巧4: 使用閉包語法為變量賦初值, lazy關鍵字為懶加載,即運行時調用了animator變量后才會執行閉包代碼。
<pre>
private lazy var animator: UIDynamicAnimator = {
let animator = UIDynamicAnimator(referenceView: self)
animator.delegate = self
return animator
}()
</pre>

技巧5: 監聽屬性變化didSet/WillSet事件(觀察者模式)并添加對應邏輯。 還記得前面博文提到的兩個類相互引用的問題么,使用weak關鍵字解開閉環;注意下面代碼, 如果在閉包里使用了self, 那么外部類實例和閉包之間形成了相互引用的關系, 這時需要使用[unowned self]避免內存泄漏。
<pre>
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)
}
}
}
}
}<</pre>

技巧6: if邏輯判斷需要where關鍵字的功能, 這時要使用逗號。 下面示例代碼的意思是dropToAttachTo不是nil時才執行后面的語句dropToAttachTo.superview != nil , 如果條件都滿足則進入代碼塊。
<pre>
if let dropToAttachTo = lastDrop, dropToAttachTo.superview != nil {
attachment = UIAttachmentBehavior(item: dropToAttachTo, attachedToAnchor: gesturePoint)
}
</pre>

技巧7:對應Optional參數類型,即值可能為nil。 在Java語法里要寫一堆的判空,語句間使用&&連接; Swift3.0省略了判空操作,再也不用寫蛋疼的判空語句了。
請問下面的語句會崩潰嗎?<pre>
attachment = nil
attachment?.anchorPoint = gesturePoint //attatchment后面是問號,說明他是Optional類型
</pre>答: 不會!
翻譯一下: 如果attachment等于nil則不調用.后面的參數, 如果attachment有值則調用后面的參數。擴展一下可以是這樣: attachment?.param1?.param2?.param3?.someValue , 如果用Java寫這條語句要被累死!!!

技巧8: 自定義UIView繪制若干個圖形時,可以使用Dictionary數組。 注意setNeedsDisplay函數類似于Android的invalidate,相當于設置個邏輯判斷參數為true,UIView會在下個繪制周期時調用drawRect函數; 自定義UIView定義一個數組, 在drawRect函數里遍歷并繪制。
<pre>
var bezierPaths = String:UIBezierPath {
didSet {
setNeedsDisplay() //觸發刷新
}
}
override func draw(_ rect: CGRect) {
for (_, path) in bezierPaths {
path.stroke() //畫線
}
} </pre>

代碼下載地址:
https://github.com/brycegao/DropIt#dropit

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,251評論 4 61
  • 簡單整理下在Oc編譯環境下如何與html文件進行交互,本文介紹兩種比較簡單的交互方式 第一種方法 —— 攔截UIW...
    杭州七木科技閱讀 721評論 0 1
  • 清流獨戲水,林蔭小徑踏歌行,炎暑聽蛙鳴。清風微拂面,略有閑情,無淺草沒馬之感,卻亦靜心。酷暑雖難耐,別有風情。聽蟲...
    楚地小生閱讀 523評論 0 7
  • #燃燒之鬼# #追凌# cr殊之 供梗來源:桑果mulberry 原梗作者:燃燒之鬼 藍思追夜獵的時候,不慎沾上了...
    _殊之_閱讀 2,690評論 2 19
  • 自打電子通訊設備軟件有了飛越性的發展,對于我這種年年月月分不清幾時何日的人,真是莫大的驚喜。不論時間如何度過,...
    一思瑤一閱讀 285評論 1 1