使用CADisplayLink實現UILabel動畫特效

在開發時,我們有時候會遇到需要定時對UIView進行重繪的需求,進而讓view產生不同的動畫效果。

本文項目

效果圖

初探 CADisplayLink

定時對View進行定時重繪可能會第一時間想到使用NSTimer,但是這樣的動畫實現起來是不流暢的,因為在timer所處的runloop中要處理多種不同的輸入,導致timer的最小周期是在50到100毫秒之間,一秒鐘之內最多只能跑20次左右。

但如果我們希望在屏幕上看到流暢的動畫,我們就要維持60幀的刷新頻率,也就意味著每一幀的間隔要在0.016秒左右,NSTimer是無法實現的。所以要用到Core Animation的另一個timer,CADisplayLink

CADisplayLink的頭文件中,我們可以看到它的使用方法跟NSTimer是十分類似的,其同樣也是需要注冊到RunLoop中,但不同于NSTimer的是,它在屏幕需要進行重繪時就會讓RunLoop調用CADisplayLink指定的selector,用于準備下一幀顯示的數據。而NSTimer是需要在上一次RunLoop整個完成之后才會調用制定的selector,所以在調用頻率與上比NSTimer要頻繁得多。

另外和NSTimer不同的是,NSTimer可以指定timeInterval,對應的是selector調用的間隔,但如果NSTimer觸發的時間到了,而RunLoop處于阻塞狀態,其觸發時間就會推遲到下一個RunLoop。而CADisplayLink的timer間隔是不能調整的,固定就是一秒鐘發生60次,不過可以通過設置其frameInterval屬性,設置調用一次selector之間的間隔幀數。另外需要注意的是如果selector執行的代碼超過了frameInterval的持續時間,那么CADisplayLink就會直接忽略這一幀,在下一次的更新時候再接著運行。

配置 RunLoop

在創建CADisplayLink的時候,我們需要指定一個RunLoop和RunLoopMode,通常RunLoop我們都是選擇使用主線程的RunLoop,因為所有UI更新的操作都必須放到主線程來完成,而在模式的選擇就可以用NSDefaultRunLoopMode,但是不能保證動畫平滑的運行,所以就可以用NSRunLoopCommonModes來替代。但是要小心,因為如果動畫在一個高幀率情況下運行,會導致一些別的類似于定時器的任務或者類似于滑動的其他iOS動畫會暫停,直到動畫結束。

private func setup() {_displayLink = CADisplayLink(target: self, selector: #selector(update))_displayLink?.isPaused = true_displayLink?.add(to: RunLoop.main, forMode: .commonModes)
}復制代碼

實現不同的字符變換動畫

在成功建立CADisplayLink計時器后,就可以著手對字符串進行各類動畫操作了。在這里我們會使用NSAttributedString來實現效果

setupAnimatedText(from labelText: String?)這個方法中,我們需要使用到兩個數組,一個是durationArray,一個是delayArray,通過配置這兩個數組中的數值,我們可以實現對字符串中各個字符的出現時間出現時長的控制。

打字機效果的配置

  • 每個字符出現所需時間相同
  • 下一個字符等待上一個字符出現完成后再出現
  • 通過修改NSAttributedStringKey.baselineOffset調整字符位置
case .typewriter:attributedString.addAttribute(.baselineOffset, value: -label.font.lineHeight, range: NSRange(location: 0, length: attributedString.length))let displayInterval = duration / TimeInterval(attributedString.length)for index in 0..<attributedString.length {durationArray.append(displayInterval)delayArray.append(TimeInterval(index) * displayInterval)}復制代碼

閃爍效果的配置

  • 每個字符出現所需時間隨機
  • 確保所有字符能夠在duration內均完成出現
  • 修改NSAttributedStringKey.foregroundColor透明度來實現字符的出現效果
case .shine:attributedString.addAttribute(.foregroundColor, value: label.textColor.withAlphaComponent(0), range: NSRange(location: 0, length: attributedString.length))for index in 0..<attributedString.length {delayArray.append(TimeInterval(arc4random_uniform(UInt32(duration) / 2 * 100) / 100))let remain = duration - Double(delayArray[index])durationArray.append(TimeInterval(arc4random_uniform(UInt32(remain) * 100) / 100))}
復制代碼

漸現效果的配置

  • 每個字符出現所需時間漸減
  • 修改NSAttributedStringKey.foregroundColor透明度來實現字符的出現效果
case .fade:attributedString.addAttribute(.foregroundColor, value: label.textColor.withAlphaComponent(0), range: NSRange(location: 0, length: attributedString.length))let displayInterval = duration / TimeInterval(attributedString.length)for index in 0..<attributedString.length  {delayArray.append(TimeInterval(index) * displayInterval)durationArray.append(duration - delayArray[index])}
復制代碼

完善每一幀的字符串更新效果

接下來就需要完善剛才在CADisplayLink中配置的update方法了,在這個方法中我們會根據我們剛才配置的兩個數組中的相關數據對字符串進行變換。

核心代碼

  • 通過開始時間當前時間獲取動畫進度
  • 根據字符位置對應duationArraydelayArray中的數據
  • 根據durationArraydelayArray中的數據計算當前字符的顯示進度
var percent = (CGFloat(currentTime - beginTime) - CGFloat(delayArray[index])) / CGFloat(durationArray[index])
percent = fmax(0.0, percent)
percent = fmin(1.0, percent)
attributedString.addAttribute(.baselineOffset, value: (percent - 1) * label!.font.lineHeight, range: range)
復制代碼

隨后便可以將處理完的NSAttributedString返回給label進行更新

番外:利用正弦函數實現波紋進度

波紋路徑

首先介紹一下正弦函數:y = A * sin(ax + b)

  • 在 x 軸方向平移 b 個單位(左加右減)
  • 橫坐標伸長(0 < a < 1)或者縮短(a > 1) 1/a 倍
  • 縱坐標伸長(A > 1)或者縮短(0 < A < 1)A 倍

在簡單了解了這些知識后,我們回到wavePath()方法中,在這個方法我們使用正弦函數來繪制一段UIBezierPath

let originY = (label.bounds.size.height + label.font.lineHeight) / 2
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: _waveHeight!))
var yPosition = 0.0
for xPosition in 0..<Int(label.bounds.size.width) {yPosition = _zoom! * sin(Double(xPosition) / 180.0 * Double.pi - 4 * _translate! / Double.pi) * 5 + _waveHeight!path.addLine(to: CGPoint(x: Double(xPosition), y: yPosition))
}
path.addLine(to: CGPoint(x: label.bounds.size.width, y: originY))
path.addLine(to: CGPoint(x: 0, y: originY))
path.addLine(to: CGPoint(x: 0, y: _waveHeight!))
path.close()
復制代碼

波紋高度與動畫的更新

  • 隨著進度高度不斷升高
  • 隨著進度波紋不斷波動

CADisplayLink注冊的update的方法中,我們對承載了波紋路徑的Layer進行更新

_waveHeight! -= duration / Double(label!.font.lineHeight)
_translate! += 0.1
if !_reverse {_zoom! += 0.02if _zoom! >= 1.2 {_reverse = true}
} else {_zoom! -= 0.02if _zoom! <= 1.0 {_reverse = false}
}
shapeLayer.path = wavePath()
復制代碼

結語

以上就是我對CADisplayLink的一些運用,其實它的使用方法還有很多,可以利用它實現更多更復雜而精美的動畫,同時希望各位如果有更好的改進也能與我分享。

如果你喜歡這個項目,歡迎到GitHub上給我一個star。

參考

  • RQShineLabel
  • Apple Developer Document - CADisplayLink
  • iOS核心動畫高級技巧

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282695.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282695.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282695.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《ASP.NET Core 6框架揭秘》實例演示[27]:ASP.NET Core 6 Minimal API的模擬實現

Minimal API僅僅是在基于IHost/IHostBuilder的服務承載系統上作了小小的封裝而已&#xff0c;它利用WebApplication和WebApplicationBuilder這兩個類型提供了更加簡潔的API&#xff0c;同時提供了與現有API的兼容。[本文節選《ASP.NET Core 6框架揭秘》第17章]一、基礎模型二、…

Mysql的關聯查詢語句

一 內連接( inner join&#xff09; 1、多表中同時符合某種條件的數據記錄的集合 (取兩表公共部分) 2、inner join 可以縮寫成 join 例如: select * from A,B WHERE A.idB.id 或者 select * from A inner join B on A.idB.id 內連接分為三類:{ &#xff08;1&#xff0…

高性能Server---Reactor模型

無處不在的C/S架構 在這個充斥著云的時代,我們使用的軟件可以說99%都是C/S架構的&#xff01; 你發郵件用的Outlook,Foxmail等你看視頻用的優酷&#xff0c;土豆等你寫文檔用的Office365,googleDoc&#xff0c;Evernote等你瀏覽網頁用的IE,Chrome等(B/S是特殊的C/S)……C/S架構…

計算機控制系統的試題,計算機控制系統練習題(1)

21. 給出多通道復用一個D/A轉換器的原理示意圖。 答&#xff1a;22. 什么是信號重構&#xff1f;答&#xff1a;把離散信號變為連續信號的過程&#xff0c;稱為信號重構&#xff0c;它是采樣的逆過程。23. 寫出零階保持器的傳遞函數&#xff0c;引入零階保持器對系統開環傳遞函…

springmvc_3(將數據放入map中)

jsp頁面 結果 轉載于:https://www.cnblogs.com/mohehpc/p/6491376.html

怎樣用原生js配合css的transition寫個無縫滾動

之所以想要寫原生js配合css轉換的無縫滾動&#xff0c;是因為之前在簡書上看到一哥們寫的一篇文章&#xff0c;說是在網上找了一堆js配合css transition屬性寫的輪播插件&#xff0c;可惜沒有無縫的效果&#xff0c;結果他用原生js重寫了一個可以無縫滾動的。好吧&#xff0c;我…

聊聊策略模式

1、簡介策略模式就是把各個平等的具體實現進行抽象、封裝成為獨立的算法類&#xff0c;然后通過上下文和具體的算法類來進行交互。各個策略算法都是平等的&#xff0c;地位是一樣的&#xff0c;正是由于各個算法的平等性&#xff0c;所以它們才是可以相互替換的。雖然我們可以動…

小學計算機課每周幾節,小學信息技術課時多少

滿意答案小學信息技術課程標準一、課程任務和教學目標中小學信息技術課程的主要任務是&#xff1a;培養學生對信息技術的興趣和意識&#xff0c;讓學生了解和掌握信息技術基本知識和技能&#xff0c;了解信息技術的發展及其應用對人類日常生活和科學技術的深刻影響。通過信息技…

張旭升20162329 2006-2007-2 《Java程序設計》第一周學習總結

20162329 2006-2007-2 《Java程序設計》第一周學習總結 教材學習內容總結 通過打書上的代碼熟悉了Java編程的基本過程 教材學習中的問題和解決過程 1.因為我的虛擬機不可用所以我在Windows中安裝了bash和git&#xff0c;但是由于Windows下bash中沒有中文而且我英語又不是很好所…

《圖解 HTTP》讀書筆記(未完待續)

ARP 協議&#xff08;Address Resolution Protocol&#xff09;一種以解析地址的協議&#xff0c;根據通信雙方的 IP 地址就可以查出對應的 MAC 地址。MAC&#xff08; Media Access Control Address&#xff09;地址是指網卡所屬的固定的地址MIME&#xff0c;多部分對象集合&a…

SQL查詢的安全方案

1.使用預處理語句防sql注入 2.寫入數據庫的數據要進行特殊字符轉義 3.錯誤信息不返回給用戶,記錄到日志 4.定期做數據備份 5.不給查詢用戶root權限,合理分配權限 6.關閉遠程訪問數據庫權限 7.修改root口令,不使用默認口令,使用較復雜口令 8.刪除多余的用戶 9.改變root用戶的名稱…

.NET 實現啟動時重定向程序運行路徑及 Windows 服務運行模式部署

日常工作中有時候會遇到需要將程序直接在服務器上運行&#xff0c;而不依賴于 IIS 托管的情況&#xff0c;直接運行有兩種方式&#xff0c;一種是部署為 服務模式&#xff0c;另一種則是 直接啟動 .NET 發布之后的 exe 文件以 控制臺模式運行&#xff0c;控制臺模式運行主要問題…

iOS runtime實戰應用:關聯對象

在開始之前建議先閱讀iOS runtime的基礎理解篇&#xff1a;iOS內功篇&#xff1a;runtime 有筒子在面試的時候&#xff0c;遇到這樣一個問題&#xff1a;“如何給NSArray添加一個屬性&#xff08;不能使用繼承&#xff09;”&#xff0c;筒子立馬蒙逼了&#xff0c;不能用繼承&…

黑龍江科技大學計算機考研復試科目,2020年黑龍江科技大學計算機應用技術考研經驗分享...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓育明考研備考策略隨著IT業的迅猛發展&#xff0c;各高校計算機專業報名火爆&#xff0c;甚至文科學生跨專業報考時都會選擇計算機。計算機專業競爭日趨激烈&#xff0c;那么如何在充分發揮公共科目優勢的同時&#xff0c;盡量縮小專…

Mysql數據庫安全性問題【防注入】

一、SQL注入實例 后臺的插入語句代碼&#xff1a; $unsafe_variable $_POST[user_input]; mysql_query("INSERT INTO table (column) VALUES ($unsafe_variable)"); 當POST的內容為&#xff1a; value); DROP TABLE table;--以上的整個SQL查詢語句變成&#xff1…

Unexpected end of JSON input while parsing near錯誤解決方式(網上的方法)

原本是想創建一個create-react-app來著&#xff0c;但是在創建的中間會出現Unexpected end of JSON input while parsing near... 的錯誤。 在網上找到了一些方法&#xff0c;首先是清空npm的緩存。 npm cache clean --force 氮素&#xff0c;然并卵。near后面的內容變化了一下…

解決Qt5 Creator無法切換輸入法(fcitx),Ubuntu中不能使用搜狗輸入法錄入漢字問題...

2016年6月8日修正&#xff0c;ubuntu 16.04 Qt5.7.0 以及 Qt5.6.1均測試通過在Qt5.3之前&#xff0c;我發布過解決辦法 解決Qt5 Creator無法切換輸入法&#xff08;fcitx&#xff09;&#xff0c;不能錄入漢字問題&#xff0c;Qt5.4以及Qt5.5&#xff0c;舊辦法失效&#xff0c…

目前市場上用于個人計算機的硬盤尺寸是,第5章-硬盤(計算機組裝與維護).docx

ADDIN CNKISM.UserStyle一、選擇題1.磁盤存儲器的主要技術指標有多項&#xff0c;下面不屬于硬盤指標的是( )。A.存儲容量B.單碟容量C.轉速D.帶寬2.硬盤的平均尋道時間通常以毫秒為單位測量&#xff0c;是指( )。A.磁頭從一個柱面移到另一個隨機距離遠的柱面所需的平均時間B.…

Xmemcached學習筆記一(安裝memcached)

memcached有三種java客戶端 第一種&#xff1a;Com.danga 包下面的memcached&#xff0c;需引入jar(本人用的是memcached-2.5.2.jar 文末附上附件需要的可以下載) 第二種&#xff1a;spyMemcached 第三種&#xff1a;XMemcached 據說第三種是使用最簡單&#xff0c;最好用的&a…

WrapPanel 實現虛擬化

WrapPanel 實現虛擬化控件名&#xff1a;VirtualizingWrapPanel作者&#xff1a;WPFDevelopersOrg原文鏈接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;項目使用 MIT 開源許可協議&#xff1b;眾…