iOS開發(fā)文集iOS 10 Day By Day:UIViewPropertyAnimator屬性
基于UIView的動畫block在動畫屬性(框架、變換,等等)之間添加過渡,輕松建立。他們創(chuàng)建起來是難以置信的容易,只需要幾行代碼:
view.alpha = 1 UIView.animate(withDuration: 2) { containerView.alpha = 0 }
你還可以添加completion blocks,當動畫完成時它們將被執(zhí)行,如果默認的線性運動不能運行則調(diào)整動畫曲線。
然而,如果你需要創(chuàng)建你自己的自定義動畫曲線,就需要被動畫迅速啟動的屬性,然后迅速減速,這時會發(fā)生什么呢?另一個稍微棘手的問題是你需要取消一個運行中的動畫。當然這些可以通過使用第三方庫或創(chuàng)建一個新的動畫更換目前正在進行的動畫來解決,蘋果增加了一個新的組件到UIKit框架,使得這個問題解決起來變得容易許多:進入UIViewPropertyAnimator。
一個新的動畫時代
UIViewPropertyAnimator是寫得很好的且高度可擴展的API。它涵蓋了許多與“舊式”UIView動畫相同的功能,但能給你動畫的精確編程控制。這意味著你可以暫停一個進度,在稍后的某個日期如果你愿意的話再啟動它,甚至可以動態(tài)修改動畫屬性(比如將以前左下角的動畫結束點位置改變到屏幕的右上方)。
為了探索這個新的類,我們將看幾個我們在屏幕上制作的圖像動畫的例子。像所有的Day by Day博客一樣,代碼放在GitHub上(為了方便各位讀者,小編已經(jīng)為大家整理了,請點擊這里下載)。這一次,我們要使用一個Playground。
Playground演練
我們所有的Playground頁面都有一個忍者在屏幕上移動。為了使頁面盡可能的貼切,我們將我們共同的代碼隱藏在Sources文件夾里。這不僅是幫助你簡化你的頁面的代碼,也使他們的運行速度同源被編譯了一樣快。
源包含一個簡單的UIView子類,叫做NinjaContainerView。這個通過用顯示我們的忍者的UIImageView子視圖簡單設置了視圖。我已將圖像添加到Playground的Resources組里。
import UIKit public class NinjaContainerView: UIView { public let ninja: UIImageView = { let image = UIImage(named: "ninja") let view = UIImageView(image: image) view.frame = CGRect(x: 0, y: 0, width: 45, height: 39) return view }() public override init(frame: CGRect) { // Animating view super.init(frame: frame) // Position ninja in the bottom left of the view ninja.center = { let x = (frame.minX + ninja.frame.width / 2) let y = (frame.maxY - ninja.frame.height / 2) return CGPoint(x: x, y: y) }() // Add image to the container addSubview(ninja) backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1) } required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } /// Moves the ninja view to the bottom right of its container, positioned just inside. public func moveNinjaToBottomRight() { ninja.center = { let x = (frame.maxX - ninja.frame.width / 2) let y = (frame.maxY - ninja.frame.height / 2) return CGPoint(x: x, y: y) }() } }
現(xiàn)在,在每個Playground頁面,我們可以復制和粘貼以下代碼:
import UIKit import PlaygroundSupport // Container for our animating view let containerView = NinjaContainerView(frame: CGRect(x: 0, y: 0, width: 400, height: 400)) let ninja = containerView.ninja // Show the container view in the Assistant Editor PlaygroundPage.current.liveView = containerView
這將使用Playground有用的“Live View”功能,無需啟動模擬器就可以給我們一個我們的動畫的可視化演示。他們?nèi)匀挥凶约旱姆绞?,但Playgrounds是嘗試新東西的明智之舉。
要顯示Live View窗格,導航到View > Assistant Editor > Show Assistant Editor,或者,選擇在工具欄中右上方看起來像兩個相交圓的圖標。如果你在Assistant Editor中沒有看到Live View,要確保已選擇了Timeline,而不是Manual,比起我認識到這一點,我已經(jīng)失去了更多的時間!
從簡單的開始
我們可以用同舊式的基于block的API完全相同的方式使用UIViewPropertyAnimator :
UIViewPropertyAnimator(duration: 1, curve: .easeInOut) { containerView.moveNinjaToBottomRight() }.startAnimation()
這將啟動一個持續(xù)1秒的動畫,有一個簡化后的時間曲線。執(zhí)行的動畫在關閉中。
請注意,我們必須通過調(diào)用startAnimation()明確啟動動畫。另一種方法來創(chuàng)建無需自身啟動的動畫是使用runningPropertyAnimator(withDuration:delay:options:animations:completion:)——一個公平的mouthful,所以你可能更喜歡使用手動啟動版。
在動畫已經(jīng)創(chuàng)建之后,添加額外的動畫是很容易的。
// Now we've set up our view, let's animate it with a simple animation let animator = UIViewPropertyAnimator(duration: 1, curve: .easeInOut) // Add our first animation block animator.addAnimations { containerView.moveNinjaToBottomRight() } // Now here goes our second animator.addAnimations { ninja.alpha = 0 }
這些動畫blocks會跑在一起。
Completion blocks可以以類似的方式添加:
animator.addCompletion { _ in print("Animation completed") } animator.addCompletion { position in switch position { case .end: print("Completion handler called at end of animation") case .current: print("Completion handler called mid-way through animation") case .start: print("Completion handler called at start of animation") } }
在動畫被允許運行它的整個持續(xù)時間的情況下,我們將看到以下版塊:
Animation completed Completion handler called at end of animation
滑動和反向
我們可以使用animator來滑動我們的動畫:
let animator = UIViewPropertyAnimator(duration: 5, curve: .easeIn) // Add our first animation block animator.addAnimations { containerView.moveNinjaToBottomRight() } let scrubber = UISlider(frame: CGRect(x: 0, y: 0, width: containerView.frame.width, height: 50)) containerView.addSubview(scrubber) let eventListener = EventListener() eventListener.eventFired = { animator.fractionComplete = CGFloat(scrubber.value) } scrubber.addTarget(eventListener, action: #selector(EventListener.handleEvent), for: .valueChanged)
Playground是很棒的,你甚至可以添加交互式UI元素到Live View中。不幸的是,接收事件是痛苦的,因為我們需要一個符合NSObject可以監(jiān)聽事件的類,像是.valueChanged。為此,當handleEvent方法被調(diào)用時,我們用一個簡單的對象EventHandler調(diào)用我們的eventFired關閉。
分數(shù)與時間無關,所以我們看不到我們的忍者在一個像我們定義的優(yōu)雅的easeIn曲線上移動。
屬性動畫的真正力量來自于能夠中斷正在進行的動畫的能力。我們可以通過簡單的切換isReversed屬性來反向移動動畫。
為了說明這一點,我們使用關鍵幀動畫,這樣我們就可以定義一個多級動畫:
animator.addAnimations { UIView.animateKeyframes(withDuration: animationDuration, delay: 0, options: [.calculationModeCubic], animations: { UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) { ninja.center = containerView.center } UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) { containerView.moveNinjaToBottomRight() } }) } let button = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 30))) button.setTitle("Reverse", for: .normal) button.setTitleColor(.black(), for: .normal) button.setTitleColor(.gray(), for: .highlighted) let listener = EventListener() listener.eventFired = { animator.isReversed = true } button.addTarget(listener, action: #selector(EventListener.handleEvent), for: .touchUpInside) containerView.addSubview(button) animator.startAnimation()
當按下按鈕,屬性動畫將反轉(zhuǎn)動畫,如果其狀態(tài)是激活的(即動畫目前在運行中,忍者尚未到達其最終目的地)。
添加你自己的時間曲線
屬性動畫是很簡單的,其余是漂亮的擴展。如果你需要另一種蘋果所提供的動畫曲線,你可以通過你自己符合UITimingCurveProvider協(xié)議的情況。在大多數(shù)情況下,你可能會使用UICubicTimingParameters或UISpringTimingParameters。
我們說我們的忍者加速真的很快,然后在屏幕上他們的旅途中出現(xiàn)一個漸進的停止。我們將使用Bezier曲線,如下圖(使用這個方便的在線工具繪制)。
let bezierParams = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.05, y: 0.95), controlPoint2: CGPoint(x: 0.15, y: 0.95)) let animator = UIViewPropertyAnimator(duration: 4, timingParameters:bezierParams) animator.addAnimations { containerView.moveNinjaToBottomRight() } animator.startAnimation()
進一步的閱讀
新的屬性動畫通過帶來在一致的API下的現(xiàn)有能力,看起來會讓動畫項目比以往任何時候都容易,增加了中斷動畫和你自己的自定義時序曲線的能力。
蘋果提供了一些優(yōu)秀的UIViewPropertyAnimator文件?;蛘?,你可以觀看WWDC演講,將在新的API進行深入研究和探討可以用來如何創(chuàng)建UIViewControllers之間的自定義轉(zhuǎn)換。其他有趣的演示也包括了簡單的游戲。
本文翻譯自:iOS 10 Day By Day: UIViewPropertyAnimator