繪圖動畫基礎-時間軸
-
時間軸視圖 TimelineView
前面幾課所學的「動畫效果」,一則是利用 Animation 物件,根據時間曲線用內插法計算兩個視圖中間的過程,一則是利用「定時器」Timer 物件提供時間,按時觸發視圖本身狀態的變化。歸納起來,動畫的關鍵要素離不開「時間」,可以說「時間」驅動了動畫的效果。
本單元將介紹SwiftUI新的時間軸視圖 TimelineView,是第3種製作動畫效果的方式,從名稱就可看出,這個視圖物件也跟時間有關。
TimelineView 是一種視圖容器(View Container),與 VStack, HStack, List, NavigationView...等排版功能的容器類似,可用來容納別的視圖,在第2單元第4課(2-4c)提過,容器內的視圖可稱為子視圖。
如果說,排版功能的容器是用來控制子視圖的空間排列,那麼,時間軸視圖就是控制子視圖的「時間排程」。我們先用一個簡單的範例程式,來看看時間軸視圖能做什麼:
操作步驟如下:
選取畫面左方的「+」號新增電子書頁面。
將新增的電子書面頁命名為「(29)SwiftUI動畫-4時間軸」。
在「Main」模組中撰寫程式:
// 4-4a TimelineView (時間軸視圖) // Created by Philip, Heman, Jean 2022/04/07 // Revised by Jean 2025/01/26 import PlaygroundSupport import SwiftUI struct 週期性排程: View { var body: some View { TimelineView(.periodic(from: Date(), by: 1.0)) { 時間參數 in Text("\(時間參數.date)") .font(.title2) .padding() } } } struct 連續時間排程: View { var body: some View { TimelineView(.animation) { 時間參數 in let 本地曆法 = Calendar.current let 秒數 = 本地曆法.component(.second, from: 時間參數.date) let 奈秒 = 本地曆法.component(.nanosecond, from: 時間參數.date) Text("\(時間參數.date)\n\(秒數):\(奈秒)") .font(.title2) .onAppear { print(時間參數.date) print(時間參數.cadence) } } } } struct 時間軸視圖: View { var body: some View { 週期性排程() 連續時間排程() } } PlaygroundPage.current.setLiveView(時間軸視圖())
時間軸的語法和其他視圖類似,需要一個參數並尾隨一個匿名函式,參數需提供「時間排程(Schedule)」,時間軸視圖會在時間排程的每一時刻,將「時間參數」帶入匿名函式中。
範例第一部分設定為「週期性排程」(periodic),從現在時刻Date()開始,每1.0秒鐘更新一次容器內的子視圖:
TimelineView(.periodic(from: Date(), by: 1.0)) { 時間參數 in Text("\(時間參數.date)") .font(.title2) .padding() }
參數時間排程(Schedule)目前有以下5種預設屬性或方法,本節範例使用前兩個最常用的排程,以初步熟悉時間軸視圖的物件特性:
1. periodic(from: 起始時間, by: 週期間隔) — 週期性排程
2. animation — 動畫排程或連續排程,物件屬性
3. animation(minimumInterval: 最短間隔, paused: 可否暫停)
4. everyMinute — 分鐘週期排程,物件屬性
5. explicit(時間序列) — 明確排程或有限排程時間軸視圖的週期性排程(periodic)與上一課用的定時器(Timer)效果相當,不過兩者內部原理不同,所以用法也不一樣:
- 定時器(Timer)是用第3單元第2課學過的Publisher-Subscriber模式,視圖透過 .onReceive(定時器) 來控制自身狀態變化,相當於在視圖上裝一個接收天線,以接收定時器發出的訊號。
- 時間軸不用Publisher-Subscriber模式,而是每次時間一到,直接透過「時間參數」送進匿名函式各個子視圖,由子視圖根據時間參數的變化來控制自身狀態。時間軸送入匿名函式的「時間參數」是個物件,有兩個屬性,範例中會將「時間參數.date」,也就是觸發當下的時間顯示出來:
- date — 觸發當下的時間
- cadence — 節奏,即大約的週期(.live, .second, .minute)範例第二部分是觀察「連續時間排程」animation,這是一個物件屬性。也有個物件方法取同名animation(),用物件方法可以進一步控制時間間隔以及排程暫停。
TimelineView(.animation) { 時間參數 in let 本地曆法 = Calendar.current let 秒數 = 本地曆法.component(.second, from: 時間參數.date) let 奈秒 = 本地曆法.component(.nanosecond, from: 時間參數.date) Text("\(時間參數.date)\n\(秒數):\(奈秒)") .font(.title2) .onAppear { print(時間參數.date) print(時間參數.cadence) } }
animation 字面意思是動畫,跟前面所提的動畫 Animation物件有關嗎?其實沒有直接關係,或許新的 Animation物件可以透過這個 animation排程來加以實現。
animation 時間排程原則上就是「儘量快」,如果CPU資源允許,會以每秒60次(60fps)以上的速度來更新子視圖,相當於時間間隔在0.0167秒以下,對人眼來說,幾乎是連續的,所以可名為「動畫排程」或「連續排程」。
範例中,我們利用第1單元第10課學過的Calendar物件,取出時間中的秒數以及小數點以下到奈秒,請留意animation排程的顯示速度。