iOS使用CADisplayLink詳解,NSMutableAttributedString來實現(xiàn)文字淡入淡出效果

什么是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犀斋,謝謝你的時間贝乎。

愿世界更美好。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叽粹,一起剝皮案震驚了整個濱河市览效,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌虫几,老刑警劉巖锤灿,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異持钉,居然都是意外死亡衡招,警方通過查閱死者的電腦和手機篱昔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門每强,熙熙樓的掌柜王于貴愁眉苦臉地迎上來始腾,“玉大人,你說我怎么就攤上這事空执±思” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵辨绊,是天一觀的道長奶栖。 經(jīng)常有香客問我,道長门坷,這世上最難降的妖魔是什么宣鄙? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮默蚌,結(jié)果婚禮上冻晤,老公的妹妹穿的比我還像新娘。我一直安慰自己绸吸,他們只是感情好鼻弧,可當(dāng)我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锦茁,像睡著了一般攘轩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上码俩,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天度帮,我揣著相機與錄音,去河邊找鬼稿存。 笑死够傍,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的挠铲。 我是一名探鬼主播冕屯,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拂苹!你這毒婦竟也來了安聘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瓢棒,失蹤者是張志新(化名)和其女友劉穎浴韭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脯宿,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡念颈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了连霉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榴芳。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡嗡靡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出窟感,到底是詐尸還是另有隱情讨彼,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布柿祈,位于F島的核電站哈误,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躏嚎。R本人自食惡果不足惜蜜自,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卢佣。 院中可真熱鬧袁辈,春花似錦、人聲如沸珠漂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽媳危。三九已至荞彼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間待笑,已是汗流浹背鸣皂。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留暮蹂,地道東北人寞缝。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像仰泻,于是被迫代替她去往敵國和親荆陆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,675評論 2 359

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