什么是CADisplayLink?
蘋果官方文檔中對CADisplayLink的描述如下:
A CADisplayLink object is a timer object that allows your application to synchronize its drawing to the refresh rate of the display.
由此我們可以知道海铆, CADisplaylink本質(zhì)上就是一個能讓我們患蹂,以和屏幕刷新率相同的頻率搅幅,將內(nèi)容畫到屏幕上的定時器颠猴。也就是說陶珠,系統(tǒng)在每次刷新屏幕時都會觸發(fā)CADisplayLink事件星压。
開發(fā)過程中常用的類似定時器功能又三個:GCD,Timer,CADisplayLink缩搅,此處不再擴展凌埂,有興趣可以查看相關(guān)文章學(xué)習(xí)一下蛋铆,或者留言討論馋评。
CADisplayLink的基本使用
mkDisplayLink = CADisplayLink.init(target: self, selector: #selector(MKLabel.updateLabelDisplay))
mkDisplayLink?.add(to: .current, forMode: .commonModes)
mkDisplayLink?.isPaused = true
通過以上代碼可以知道,CADisplayLink的實現(xiàn)需要添加一個target和selector刺啦,target就是要響應(yīng)的實例對象留特,selector則是要響應(yīng)的具體方法。然后將CADisplayLink實例對象添加到Runloop中玛瘸,這樣屏幕刷新時就會回調(diào)selector方法蜕青。你也可以通過isPaused等于true或false控制對selector的調(diào)用。另外CADisplayLink還有屬性duration糊渊、frameInterval 和 timestamp分別用來獲取當(dāng)前屏幕的刷新時間間隔右核,兩次調(diào)用selector之間屏幕刷新次數(shù)間隔,以及上一次執(zhí)行selector的時間戳渺绒。
如果不再需要CADisplayLink時贺喝,可以通過invalidate進(jìn)行釋放(類似Timer)菱鸥。
mkDisplayLink?.invalidate()
重點來了,如何利用CADisplayLink的這些特性躏鱼,實現(xiàn)文字的動態(tài)顯示效果呢氮采?
先看效果
同時可以響應(yīng)點擊事件以及自定義高亮話題等功能
直接上代碼:
首先,對動畫進(jìn)行一些基本數(shù)據(jù)的設(shè)置染苛,開始時間扳抽,結(jié)束時間,顯示的類型(庫支持的擴展效果類型)等一些需求屬性
func beginFadeAnimation(isFadeIn fadeIn: Bool, displayType: MKLabelDisplayType = .normal, completionBlock: @escaping completionBlock) {
? ? ? ? self.displayType = displayType
? ? ? ? self.completion = completionBlock
? ? ? ? self.isFadeIn = fadeIn
? ? ? ? self.beginTime = CACurrentMediaTime()
? ? ? ? self.endTime = self.beginTime! + self.durationTime
? ? ? ? self.mkDisplayLink?.isPaused = false
? ? ? ? self.setAttributedTextString(NSAttributedString(string:self.text!))
? ? }
其次殖侵,根據(jù)設(shè)置的屬性計算出每個文字在動畫過程中顯示的時間點以及顯示的時長贸呢。
? ? func setAttributedTextString(_ attributedText: NSAttributedString) {
? ? ? ? self.attributedTextString = self.setAlphaAttributedTextString(attributedText) as? NSMutableAttributedString
? ? ? ? var delayTime = 0.0, animationDuration = 0.0
? ? ? ? let totalLength = attributedText.length
? ? ? ? if delayTimeArray.count > 0 {
? ? ? ? ? ? delayTimeArray.removeAll()
? ? ? ? ? ? animationTimeArray.removeAll()
? ? ? ? }
? ? ? ? for index in 0..<totalLength {
? ? ? ? ? ? switch self.displayType {
? ? ? ? ? ? case .normal:
? ? ? ? ? ? ? ? delayTime = Double(arc4random_uniform(UInt32(durationTime/2.0 * 100)))/100.0
? ? ? ? ? ? ? ? let animationTime = durationTime - delayTime
? ? ? ? ? ? ? ? animationDuration = (Double(arc4random_uniform(UInt32(animationTime * 100)))/100.0)
? ? ? ? ? ? case .ascend:
? ? ? ? ? ? ? ? delayTime = Double((durationTime * Double(index))/Double(totalLength))
? ? ? ? ? ? ? ? let animationTime = durationTime - delayTime
? ? ? ? ? ? ? ? animationDuration = animationTime
? ? ? ? ? ? ? ? if self.isFadeIn == false {
? ? ? ? ? ? ? ? ? ? if animationTime > delayTime{
? ? ? ? ? ? ? ? ? ? ? ? animationDuration = delayTime
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? case .descend:
? ? ? ? ? ? ? ? delayTime = Double((durationTime * Double(index))/Double(totalLength))
? ? ? ? ? ? ? ? let animationTime = durationTime - delayTime
? ? ? ? ? ? ? ? animationDuration = animationTime
? ? ? ? ? ? ? ? if self.isFadeIn == false {
? ? ? ? ? ? ? ? ? ? if animationTime > delayTime{
? ? ? ? ? ? ? ? ? ? ? ? animationDuration = delayTime
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? case .middle:
? ? ? ? ? ? ? ? delayTime = Double((durationTime * Double(index))/Double(totalLength))
? ? ? ? ? ? ? ? let animationTime = durationTime - delayTime
? ? ? ? ? ? ? ? animationDuration = animationTime
? ? ? ? ? ? ? ? if self.isFadeIn == false {
? ? ? ? ? ? ? ? ? ? if animationTime > delayTime{
? ? ? ? ? ? ? ? ? ? ? ? animationDuration = delayTime
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? delayTime = Double(arc4random_uniform(UInt32(durationTime/2.0 * 100)))/100.0
? ? ? ? ? ? ? ? let animationTime = durationTime - delayTime
? ? ? ? ? ? ? ? animationDuration = (Double(arc4random_uniform(UInt32(animationTime * 100)))/100.0)
? ? ? ? ? ? }
? ? ? ? ? ? delayTimeArray.append(delayTime)
? ? ? ? ? ? animationTimeArray.append(animationDuration)
? ? ? ? ? ? if self.displayType == .middle{
? ? ? ? ? ? ? ? delayTimeArray.reverse()
? ? ? ? ? ? ? ? animationTimeArray.reverse()
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if self.displayType == .descend{
? ? ? ? ? ? delayTimeArray.reverse()
? ? ? ? ? ? animationTimeArray.reverse()
? ? ? ? }
? ? }
最后,在屏幕每次刷新拢军,selector方法調(diào)用時楞陷,計算每個文字的顯示情況以及透明度,注意透明度是根據(jù)當(dāng)前時間與動畫開始時間茉唉,動畫周期固蛾,文字的動畫顯示時長綜合關(guān)聯(lián)計算出來的,此處也是該效果的實現(xiàn)核心度陆。
? ?func updateLabelDisplay(displayLink: CADisplayLink) -> Void {
? ? ? ? guard self.attributedTextString != nil else {
? ? ? ? ? ? return
? ? ? ? }
? ? ? ? guard self.endTime != nil else {
? ? ? ? ? ? return
? ? ? ? }
? ? ? ? let nowTime = CACurrentMediaTime()
? ? ? ? let attributedLength = self.attributedTextString?.length
? ? ????for index in 0..<attributedLength! {
? ? ? ? ? ? self.attributedTextString?.enumerateAttributes(in: NSMakeRange(index, 1), options: NSAttributedString.EnumerationOptions.longestEffectiveRangeNotRequired, using: {(value, range, error) -> Void in
? ? ? ? ? ? ? ? let colorA = value[NSAttributedStringKey.foregroundColor] as! UIColor
? ? ? ? ? ? ? ? let colorAlpha = colorA.cgColor.alpha
? ? ? ? ? ? ? ? let isNeedUpdate = (nowTime - self.beginTime!) > self.delayTimeArray[index] || (isFadeIn && colorAlpha < 1.0) || (!isFadeIn && colorAlpha > 0.0)
? ? ? ? ? ? ? ? if isNeedUpdate == false {
? ? ? ? ? ? ? ? ? ? return
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? var percentage = (nowTime - self.beginTime! - self.delayTimeArray[index])/self.animationTimeArray[index]
? ? ? ? ? ? ? ? if !self.isFadeIn {
? ? ? ? ? ? ? ? ? ? percentage = 1 - percentage
? ? ? ? ? ? ? ? }
//? ? ? ? ? ? ? ? let color = self.textColor.withAlphaComponent(CGFloat(percentage))
? ? ? ? ? ? ? ? let color = colorA.withAlphaComponent(CGFloat(percentage))
? ? ? ? ? ? ? ? self.attributedTextString?.addAttribute(NSAttributedStringKey.foregroundColor, value: color, range: range)
? ? ? ? ? ? })
? ? ? ? }
? ? ? ? super.attributedText = self.attributedTextString
? ? ? ? if nowTime > self.endTime! {
? ? ? ? ? ? self.mkDisplayLink?.isPaused = true
? ? ? ? ? ? guard self.completion != nil else {
? ? ? ? ? ? ? ? return
? ? ? ? ? ? }
? ? ? ? ? ? self.completion!(true, self.text!)
? ? ? ? }
? ? }
此處僅寫出了三個比較核心的函數(shù)艾凯,來講解具體實現(xiàn)的步驟,因為我的開源庫實現(xiàn)的功能比較多懂傀,此處不便一一貼出代碼詳解趾诗,有興趣的同學(xué)可以去我的Github上下載一下源碼查看https://github.com/minhechen/MKLabel歡迎討論。
最后蹬蚁,推廣時間恃泪,求關(guān)注,求star犀斋,謝謝你的時間贝乎。
愿世界更美好。