圖層時間CAMediaTiming

CoreAnimationXmind.png

這是 Core Animation 的系列文章母蛛,介紹了 Core Animation 的用法彩郊,以及如何進行性能優(yōu)化蚪缀。

  1. CoreAnimation基本介紹
  2. CGAffineTransform和CATransform3D
  3. CALayer及其各種子類
  4. CAAnimation:屬性動畫CABasicAnimation、CAKeyframeAnimation以及過渡動畫违帆、動畫組
  5. 圖層時間CAMediaTiming
  6. 計時器CADisplayLink
  7. 影響動畫性能的因素及如何使用 Instruments 檢測
  8. 圖像IO之圖片加載刷后、解碼渊抄,緩存
  9. 圖層性能之離屏渲染护桦、柵格化、回收池

上一篇文章介紹了使用CAAnimation及其子類創(chuàng)建多種類型動畫抱慌。動畫是一段時間的變化眨猎,因此時間也是一個至關(guān)重要的概念睡陪。這一篇文章介紹 Core Animation 中的時間。

1. CAMediaTiming 協(xié)議

CAMediaTiming協(xié)議定義了一系列屬性信殊,用于管理動畫過程中的時間涡拘。CALayerCAAnimation都遵守CAMediaTiming協(xié)議据德,因此,時間可以針對于圖層橱野,也可以針對于動畫水援。

1.1 常用屬性

  • duration:是CFTimeInterval類型茅郎,單位為秒系冗,與NSTimeInterval類似。指定動畫單次迭代時長成畦。

  • repeatCount:指定動畫重復次數(shù)涝开。如果duration為2秒舀武,repeatCount為3.5次,則動畫總時長為7秒瘪匿。

    durationrepeatCount屬性默認均為0,但不意味著動畫時長0秒核偿,播放0次漾岳,這里的0表示使用默認值粉寞,即0.25秒唧垦、1次。

  • repeatDuration:指定循環(huán)時間巧还,而非repeatCount的循環(huán)次數(shù)双炕。

  • autoreverses:如果為true妇斤,則向前播放后后退播放。默認為false荸恕。

1.2 使用 repeatDuration 和 autoreverses 實現(xiàn)擺動動畫

下面代碼實現(xiàn)了無限擺動門的動畫融求。其中算撮,使用autoreverses實現(xiàn)自動反轉(zhuǎn)擺動肮柜,設(shè)置repeatDurationinfinity實現(xiàn)永不停歇擺動。repeatCountrepeatDuration可能沖突莱睁,同時使用的結(jié)果可能是未定義的仰剿。

    private func swingingDoorUsingAutoreverse() {
        doorLayer.bounds = CGRect(x: 0, y: 0, width: 128, height: 256)
        // 設(shè)置錨點為layer左邊緣中點
        doorLayer.anchorPoint = CGPoint(x: 0, y: 0.5)
        doorLayer.contents = UIImage(named: "Door")?.cgImage
        view.layer.addSublayer(doorLayer)
        
        // Apply perspective transform
        var perspective = CATransform3DIdentity
        perspective.m34 = -1.0 / 500.0
        view.layer.sublayerTransform = perspective
        
        // Apply swinging animation
        let animation = CABasicAnimation(keyPath: "transform.rotation.y")
        animation.toValue = -.pi/2.0
        animation.duration = 2.0
        animation.repeatDuration = .infinity
        animation.autoreverses = true
        doorLayer.add(animation, forKey: nil)
    }

效果如下:

CATimingAutoreverses.gif

1.3 相對時間

Core Animation 中的時間是相對時間南吮,每個動畫有自身的時間概念旨袒,可以獨立的加速术辐、延時或偏移辉词。

beginTime屬性指定動畫開始之前的延遲時間,動畫起點從添加到可見圖層開始計算敷搪。默認值為0幢哨,即添加到可見圖層后動畫立即開始捞镰。

speed是時間的倍數(shù),默認為1.0践樱。speed減小拷邢,動畫屎慢、圖層時間會變慢腻惠;speed增大,動畫廷臼、圖層時間會變快荠商。如果speed為2.0,對于一個duration為1秒的動畫初肉,實際上0.5秒就會完成饰躲。如果為 layer 的speed是2.0嘹裂,則所有添加到該 layer 的速度都會變?yōu)槎丁H绻麆赢?code>speed是3丁寄,layer 的speed是0.5伊磺,則動畫是正常速度的1.5倍删咱。

timeOffset指定時間偏移量痰滋。beginTime延遲動畫開始時間,timeOffset快進到指定時間徊哑。例如莺丑,動畫時長1秒墩蔓,timeOffset為0.5秒意味著動畫從0.5秒開始奸披,執(zhí)行到結(jié)束后再次從0秒執(zhí)行到0.5秒阵面。

beginTime不同洪鸭,timeOffset不受speed影響览爵。例如镇饮,動畫duration時間為1秒储藐,設(shè)置speed為2.0钙勃、timeOffset為0.5,此時動畫時長變?yōu)?.5秒左医。timeOffset讓動畫從結(jié)束位置開始同木,但仍然會播放一個完整的時長(這里時0.5秒)彤路,這個動畫僅僅是從結(jié)尾開始播放洲尊,最終完成了一個循環(huán)奈偏。

使用下面的示例設(shè)置不同的speed惊来、timeOffset裁蚁,點擊 Play 按鈕查看播放效果:

    private func createShip() {
        // Create a path
        let centerX = view.bounds.size.width / 2
        let centerY = view.bounds.size.height / 2

        bezierPath.move(to: CGPoint(x: view.bounds.size.width / 2 - 150, y: centerY))
        bezierPath.addCurve(to: CGPoint(x: centerX + 150, y: centerY), controlPoint1: CGPoint(x: centerX - 75, y: centerY - 150), controlPoint2: CGPoint(x: centerX + 75, y: centerY + 150))
        
        // Draw the path using a CAShapeLayer
        let pathLayer = CAShapeLayer()
        pathLayer.path = bezierPath.cgPath
        pathLayer.fillColor = UIColor.clear.cgColor
        pathLayer.strokeColor = UIColor.red.cgColor
        pathLayer.lineWidth = 3.0
        view.layer.addSublayer(pathLayer)
        
        // Add the ship
        shipLayer.bounds = CGRect(x: 0, y: 0, width: 64, height: 64)
        shipLayer.position = CGPoint(x: centerX - 150, y: centerY)
        shipLayer.contents = UIImage(named: "Ship")?.cgImage
        view.layer.addSublayer(shipLayer)
        
        updateSliders()
    }
    
    @IBAction func updateSliders() {
        let timeOffset = timeOffsetSlide.value
        timeOffsetLabel.text = String(format: "%.2f", timeOffset)
        
        let speed = speedSlide.value
        speedLabel.text = String(format: "%.2f", speed)
    }
    
    @IBAction func playButtonTapped(_ sender: Any) {
        // Create the keyframe animation
        let animation = CAKeyframeAnimation(keyPath: "position")
        animation.timeOffset = CFTimeInterval(timeOffsetSlide.value)
        animation.speed = speedSlide.value
        animation.duration = 1.0
        animation.path = bezierPath.cgPath
        animation.rotationMode = .rotateAuto
        animation.isRemovedOnCompletion = true
        shipLayer.add(animation, forKey: nil)
    }

效果如下:

CAtimeOffset.gif

1.4 fillMode

fillMode屬性可以管理動畫開始矮男、結(jié)束時的表現(xiàn)毡鉴。

1.4.1 removed

fillMode默認值為CAMediaTimingFillMode.removed。如果設(shè)置了beginTime管削,動畫從beginTime開始含思;如果沒有設(shè)置甘晤,則立即開始线婚。動畫結(jié)束后移除動畫塞弊。

CAfillModeRemoved.png
1.4.2 backwards

無論是否立即執(zhí)行動畫,CAMediaTimingFillMode.backwards都會展示動畫的第一幀饰抒。

CAfillModeBackwards.png
1.4.3 forwards

CAMediaTimingFillMode.forwards像往常一樣播放動畫袋坑,但將動畫的最后一幀保留到屏幕上枣宫,直到你移除動畫也颤。

CAfillModeForwards.png
1.4.4 both

CAMediaTimingFillMode.bothforwardsbackwards的組合翅娶,即立即顯示動畫的第一幀范咨,動畫結(jié)束后保留最后一幀渠啊。

CAfillModeBoth.png

如果想保留上一部分動畫結(jié)束狀態(tài)替蛉,可以設(shè)置動畫為forwards,并設(shè)置isRemovedOnCompletion為 false译柏。

那我們是否需要使用fillMode姐霍?是否應(yīng)該移除動畫镊折?應(yīng)如何更新圖層以實現(xiàn)平滑動畫?

最重要的一條原則就是:移除動畫并盡可能避免使用fillMode骂因,除非要實現(xiàn)的效果沒有其他方式可以實現(xiàn)寒波。fillMode會使UI元素失去交互性俄烁,并且圖層與model不匹配僚碎。一般在添加動畫后立即更新圖層 model 即可勺阐。如果出現(xiàn)動畫起始矛双、終止時閃爍议忽,可以嘗試添加動畫前更新 model栈幸。

2. 時間層級關(guān)系

在第一篇文章CoreAnimation基本介紹中介紹了圖層層級結(jié)構(gòu),動畫時間也有類似結(jié)構(gòu)玩焰。

每個動畫昔园、圖層有自身時間概念默刚。調(diào)整一個圖層的時間會影響其自身及其子類的動畫,但不會影響父圖層澜搅。

2.1 全局時間和本地時間

Core Animation 的全局時間也稱為馬赫時間(mach time)勉躺,mach 是iOS秃流、macOS系統(tǒng)內(nèi)核名稱舶胀。在同一設(shè)備的不同線程嚣伐、進程中,mach time 相同放典;不同設(shè)備 mach time 不同奋构。mach time 可以作為動畫時間參照點拱层。使用以下函數(shù)獲取馬赫時間:

        let machTime = CACurrentMediaTime()

CACurrentMediaTime()具體值無關(guān)緊要根灯,真實作用在于為動畫時間測量提供了一個相對值烙肺。CACurrentMediaTime()不能用于更新時鐘時間。

每個CALayer桃笙、CAAnimation實例有自身時間概念怎栽,是根據(jù)父圖層、動畫層級中的beginTime谬以、timeOffsetspeed等屬性計算出。就跟轉(zhuǎn)換不同layer間坐標系統(tǒng)一樣由桌,CALayer也提供了轉(zhuǎn)換不同圖層之間的本地時間方法:

  • convertTime:fromLayer:將 time interval 從指定 layer 轉(zhuǎn)換為接收者時間空間为黎。
  • convertTime:toLayer:將 time interval 從接收者空間轉(zhuǎn)換為指定 layer 時間空間。

想要同步多個具有不同speed行您、timeOffsetbeginTime的圖層時铭乾,上述方法會非常有效。

2.1 暫停娃循、回退和快進

動畫speed被設(shè)置為零會暫停動畫炕檩,但動畫被添加到圖層后不能修改,因此不能使用speed屬性暫停運行中的動畫捌斧。添加CAAnimation到 layer 時,復制一份不可變動畫對象捞蚂,因此修改原始動畫對進行中的動畫沒有效果妇押。使用animation(forKey:)可以獲取進行中的動畫,但不能修改獲取的動畫姓迅,修改該動畫會導致無法預期的行為敲霍。

從圖層移除進行中的動畫,動畫會急速返回動畫之前的狀態(tài)丁存。在動畫被移除前肩杈,復制 presentation layer 的屬性,并賦值給 model layer解寝,動畫就會有看起來停止了的效果锋恬。

增大主window layer 的speed,可以加快整個應(yīng)用動畫速度编丘,達到快進效果。設(shè)置speed為負值彤悔,可以達到回退效果嘉抓。在模擬器中,勾選 Debug > Slow Animations 后可以開啟慢動畫晕窑,可以用于調(diào)試動畫效果抑片。

Demo名稱:CoreAnimation
源碼地址:https://github.com/pro648/BasicDemos-iOS/tree/master/CoreAnimation

上一篇:CAAnimation:屬性動畫CABasicAnimation、CAKeyframeAnimation以及過渡動畫杨赤、動畫組

下一篇:計時器CADisplayLink

參考資料:

  1. Controlling Animation Timing

歡迎更多指正:https://github.com/pro648/tips

本文地址:https://github.com/pro648/tips/blob/master/sources/圖層時間CAMediaTiming.md

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敞斋,一起剝皮案震驚了整個濱河市截汪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌植捎,老刑警劉巖衙解,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異焰枢,居然都是意外死亡蚓峦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門济锄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來暑椰,“玉大人,你說我怎么就攤上這事荐绝∫黄” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵低滩,是天一觀的道長召夹。 經(jīng)常有香客問我,道長委造,這世上最難降的妖魔是什么戳鹅? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮昏兆,結(jié)果婚禮上枫虏,老公的妹妹穿的比我還像新娘。我一直安慰自己爬虱,他們只是感情好隶债,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跑筝,像睡著了一般死讹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上曲梗,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天赞警,我揣著相機與錄音,去河邊找鬼虏两。 笑死愧旦,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的定罢。 我是一名探鬼主播笤虫,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了琼蚯?” 一聲冷哼從身側(cè)響起酬凳,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遭庶,沒想到半個月后宁仔,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡罚拟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年台诗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赐俗。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡拉队,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出阻逮,到底是詐尸還是另有隱情粱快,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布叔扼,位于F島的核電站事哭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏瓜富。R本人自食惡果不足惜鳍咱,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望与柑。 院中可真熱鬧谤辜,春花似錦、人聲如沸价捧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽结蟋。三九已至脯倚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嵌屎,已是汗流浹背推正。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宝惰,地道東北人舔稀。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像掌测,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容