Symbol Effects:SwiftUI為每個圖示提供的內建動畫詞彙
SF Symbols 5(iOS 17)推出了一套每個iOS應用程式都能使用的動畫詞彙。SF Symbols 6(iOS 18)擴充了它。SF Symbols 7(iOS 26)再次擴充。然而,大多數應用程式仍然將圖示渲染為靜態圖片。這套動畫詞彙就在SF Symbols.app裡,並透過單一的SwiftUI修飾符即可使用,採用零成本,由Apple動畫團隊設計以呈現原生質感,並在無需各應用程式自行處理的情況下尊重輔助使用設定。這項遺漏是目前正在出貨的iOS應用程式中最常見的品質損失模式之一。
這套詞彙為圖示能做的事命名:它可以bounce、pulse、scale、用另一個符號替換自己、跨圖層動畫色彩、breathe、rotate、wiggle、appear或disappear。每個動詞都有特定的意義、特定的視聽特性,以及在應用程式行為中能展現其價值的特定時刻。系統提供開發者這些動詞;開發者的工作是選擇哪個動詞符合哪個時刻。
TL;DR
- SwiftUI的
.symbolEffect(...)修飾符可為任何SF Symbol加上動畫,效果包括.bounce、.pulse、.scale、.variableColor、.breathe、.rotate、.wiggle、.appear和.disappear1。 - 另一個獨立的API表面
.contentTransition(.symbolEffect(.replace))會在兩個不同的SF Symbols之間執行精心設計的轉場。replace效果存在於ContentTransition上,而非SymbolEffect上;兩者協同運作,但是不同的API。 - 效果可以執行一次(透過
value:進行值觸發)、在某個狀態為true時持續(透過isActive:進行狀態觸發),或持續重複(options: .repeating)。 - 效能基本上是免費的:動畫透過SF Symbol資產進行渲染並在GPU上派發。應用程式只需付出讀取一個值來決定何時觸發的成本。
- 輔助使用已被妥善處理:重度動態效果會自動尊重系統的「減少動態效果」設定,無需各應用程式撰寫程式碼2。
各效果一覽,依動詞分類
每個效果都為圖示能做的事命名。請依照使用者在那個時刻應該感受到什麼來選擇動詞。
.bounce
單次彈性彈跳,可設定為.up或.down。此效果代表簡短的正面事件:確認、通知到達、刷新完成。它是「是的,事情發生了」的視覺等價物。透過value:參數觸發:每次該值變更,符號就彈跳一次。
@State private var unreadCount = 0
Image(systemName: "bell.badge")
.symbolEffect(.bounce, value: unreadCount)
值觸發模式每次變更時剛好執行一次彈跳。沒有狀態機、沒有動畫時間運算、沒有版面影響。
.pulse
重複的不透明度脈動。此效果代表持續中的狀態,需要關注但不至於令人警覺。常見用途:來電指示、錄製中圓點、「直播中」徽章。脈動會持續執行,直到被移除。
Image(systemName: "record.circle")
.symbolEffect(.pulse, options: .repeating)
.foregroundStyle(.red)
.repeating選項讓脈動持續;沒有.repeating則執行單次脈動。
.scale
放大或縮小的處理方式。此效果強調圖示重要性的狀態變化:按鈕被按下、項目被選取、控制項取得焦點。Scale效果支援方向修飾符(.scale.up、.scale.down)和雙向預設值;當持續啟用時,符號會保持縮放狀態。
Image(systemName: "heart")
.symbolEffect(.scale, isActive: isLiked)
isActive:參數是另一種觸發模式:當布林值為true時,效果保持;當它變為false時,效果解除。此模式適合任何切換狀態,其中圖示的動畫應直接追蹤該狀態。
.variableColor
層級感知的色彩動畫。此效果依序點亮符號的圖層(想像Wi-Fi訊號條逐格填滿,或電池充電)。Behavior選項決定方向(.iterative向前執行、.cumulative填滿後保持、.reversing先向前再向後),而.dimInactiveLayers控制非啟用層級是淡化還是消失。
Image(systemName: "wifi")
.symbolEffect(
.variableColor.iterative.reversing.dimInactiveLayers,
options: .repeating
)
Variable color效果就是iOS控制中心、設定以及大多數Apple應用程式為任何「訊號強度」或「等級指示器」符號所使用的方式。此模式在整個平台上具有辨識度,因為動畫在整個平台上是共享的。
.replace
打破SwiftUI預設符號交換時的交叉淡入淡出效果。.contentTransition(.symbolEffect(.replace))在兩個符號之間執行精心設計的轉場,可選擇.downUp(離開的符號向下移,到達的符號向上移)或預設的縮放與淡入淡出行為。
@State private var isPlaying = false
Image(systemName: isPlaying ? "pause.circle.fill" : "play.circle.fill")
.contentTransition(.symbolEffect(.replace))
.onTapGesture { isPlaying.toggle() }
此模式適用於任何切換按鈕(播放/暫停、靜音/取消靜音、展開/收合、按讚/取消按讚)。SwiftUI預設的行為會在兩張圖片之間進行交叉淡入淡出,完全沒有符號感知的呼應;而symbol-effect的replace是尊重符號結構而精心設計的轉場。
.appear和.disappear
圖示進入或離開版面時的條件式顯示/隱藏動畫。這些效果搭配SwiftUI的view transitions,使符號的出現感覺刻意而非突兀。
Image(systemName: "checkmark.circle.fill")
.symbolEffect(.appear, isActive: isVisible)
當圖示的存在本身具有意義時(確認指示器、完成時出現的狀態徽章),請使用它們。對於永久性圖示,這些效果無法展現其價值。
.breathe
緩慢的縮放與不透明度呼吸動作(iOS 18+)。此效果代表平靜、環境性的狀態,希望吸引使用者目光但不急迫。冥想計時器、環境音訊指示器、閒置狀態。
.rotate和.wiggle
旋轉與搖擺動畫(iOS 18+)。Rotate適合載入狀態(刷新箭頭、同步齒輪)。Wiggle適合「這是可編輯的,拖曳我」或「需要您注意」的提示。兩者皆有方向與速度選項。
文法:觸發機制與選項
每個效果都支援相同的三種觸發模式。請選擇符合該時刻的那一種。
值觸發(value:參數)。當綁定的值變更時,效果執行一次。適合事件:計數遞增、狀態轉換。系統讀取值的識別,執行效果,然後重置。
狀態觸發(isActive:參數)。當綁定的布林值為true時,效果持續執行。適合保持狀態:啟用時應脈動的切換、錄製時應脈動的錄製指示器。
持續性(options: .repeating)。效果持續執行直到修飾符被移除。適合環境性訊號:載入指示器、脈動的直播徽章、呼吸的冥想圖示。
每個效果上的選項可細化行為:.speed(_)調整動畫節奏、.nonRepeating覆寫偏好重複效果的預設值、方向修飾符(.up/.down/.iterative/.cumulative/.reversing)塑造動作。每個選項都很小;組合後產生完整的詞彙。
訣竅:跨狀態的符號變體
更微妙的模式是將符號效果與依狀態解析名稱的Image(systemName:)搭配使用。狀態驅動的符號選擇加上.contentTransition(.symbolEffect(.replace)),可讓單一Imageview在多種狀態之間動畫切換,完全無需手動撰寫動畫程式碼。
@State private var connectionState: ConnectionState = .disconnected
var symbol: String {
switch connectionState {
case .disconnected: "wifi.slash"
case .connecting: "wifi.exclamationmark"
case .weak: "wifi.low"
case .medium: "wifi.medium"
case .strong: "wifi"
}
}
Image(systemName: symbol)
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.variableColor.iterative, options: .repeating, value: connectionState == .connecting)
五個符號狀態、兩個分層效果(符號之間的replace轉場、連線中的variable color)、一個Image view、零自訂動畫程式碼。此模式之所以可行,是因為SF Symbols被設計為一個連貫的家族;同一個連線圖示在多種強度下,是一個視覺概念在多個解析度下的呈現。
效能:為何成本實際上為零
Symbol effects在GPU上動畫,並透過SF Symbols已用於靜態渲染的相同渲染路徑派發。動畫被編碼於符號資產本身;應用程式讀取一個值,系統排程動畫,GPU執行它。沒有逐幀版面工作、沒有view階層的混亂、沒有objectWillChange.send()的串聯。
開發者付出的成本是驅動觸發器的綁定成本:@State、@Bindable、@Observable屬性。無論是否有動畫,該成本都存在。動畫本身基本上是相對於靜態渲染的免費升級。
此成本對於即時相機UI、含有許多圖示的列表儲存格,以及任何60 fps不容妥協的view階層而言至關重要。Symbol effects可以毫無顧忌地大量套用,不會產生自訂withAnimation區塊的效能成本;底層引擎會處理工作。
輔助使用:「減少動態效果」已被自動尊重
Symbol effects自動尊重系統的「減少動態效果」設定。涉及顯著動作的效果(.bounce、.scale、.rotate、.wiggle)在啟用「減少動態效果」時會減弱或跳過。主要基於不透明度的效果(.pulse、.breathe)通常會保留,因為它們不會引發動態敏感性問題。
此行為已內建於SwiftUI修飾符中;開發者不需為每個效果撰寫if accessibilityReduceMotion { ... } else { ... }。Apple人機介面準則指出這些效果尊重系統設定,而SwiftUI的實作與文件相符。
對於建構輔助使用優先應用程式的開發者(為前庭功能障礙患者、低視能使用者、動態敏感使用者所設計的應用程式),symbol effects是正確的模式,因為各應用程式所需的輔助使用工作量為零。
何時Symbol Effects無法展現其價值
值得指出三種失敗模式。
所有圖示時刻都使用效果。充滿脈動、呼吸、彈跳圖示的view比靜態view更難閱讀。每個效果都應代表一個特定時刻;到處都是效果就變成噪音。這項紀律是在加入效果前先問自己:「這個效果標記了什麼時刻?」如果答案是「這個圖示存在」,就移除該效果。
與內容衝突的效果。每個項目都有搖擺圖示的列表並不代表「編輯我」;它代表「一切都壞了」。效果必須與使用者流程中的時刻對齊。Wiggle是編輯模式下可編輯網格的正確動詞,而非預設狀態下內容列表的動詞。
未經測試就將效果套用於Liquid Glass表面。Liquid Glass(在Liquid Glass SwiftUI Patterns中介紹)會折射其後方的內容。玻璃下方彈跳或旋轉的圖示會產生移動的折射,可能與底層內容互相競爭。提交前請在實機硬體上測試組合。
預設紀律:每個效果都需主動加入,綁定到特定的對使用者具有意義的時刻,並針對輔助使用與效能進行測試。詞彙很豐富;編輯之眼才是讓它運作的關鍵。
SF Symbols 7(iOS 26)的新增功能
Apple每年的SF Symbols發布通常會新增數千個符號並改進現有符號。針對iOS 26的symbol-effect API,保守的摘要如下:
擴充的variable-color層級。更多現有的層級感知符號搭載了更細緻的variable-color動畫,包括先前在三個層級之間切換、現在在五個層級之間切換的網路與訊號強度符號。
更佳的wiggle和rotate處理。iOS 18新增的動態效果獲得改進,提升了在低階裝置與visionOS上的效能,在3D空間中的動作需要不同的提示。
複合符號的symbol replace轉場。具有多個元件的符號(heart-with-pulse、cloud-with-rain、person-with-clock)替換時比以往更乾淨,降低了相關複合符號之間轉場時的視覺卡頓。
主要功能(上述十項效果)自SF Symbols 5(iOS 17)和6(iOS 18)以來已相當成熟。iOS 26的新增功能是擴充而非重新發明此詞彙。正確的採用方式是深入學習現有的動詞,而非等待下一次發布。
此模式對iOS 26+應用程式的意義
三點要點。
-
這些動詞並非可有可無的裝飾;它們是平台所說的詞彙。使用者在每個Apple應用程式中都看到相同的
.bounce確認效果。採用相同的動詞會讓第三方應用程式感覺像原生;選擇不匹配的自訂動畫則讓應用程式感覺非平台。這些動詞同時是輔助使用、效能與平台一致性的勝利。 -
每個時刻一個效果。使用
.bounce進行確認、使用.replace切換狀態、使用.variableColor顯示即時訊號的view,正確使用了這套詞彙。讓視野中每個圖示都脈動的view則使用得很糟。這項紀律是編輯性的:哪個時刻配得上這個效果? -
信任平台的輔助使用與效能預設值。Symbol effects自動尊重「減少動態效果」並在GPU上以接近零成本執行。開發者本應做的工作(撰寫減少動態效果的條件式、為60 fps調校動畫時間)框架已替您完成。
完整的Apple Ecosystem系列:typed App Intents;MCP servers;路由問題;Foundation Models;runtime與工具LLM區別;三種表面;單一資料來源模式;Two MCP Servers;Apple開發hooks;Live Activities;watchOS執行階段;SwiftUI內部;RealityKit的空間心智模型;SwiftData綱要紀律;Liquid Glass模式;多平台出貨;平台矩陣;Vision framework;我拒絕撰寫的主題。中樞位於Apple Ecosystem Series。如需更廣泛的iOS搭配AI agents內容,請參閱iOS Agent Development guide。
FAQ
.symbolEffect與.contentTransition(.symbolEffect(.replace))有什麼不同?
.symbolEffect(...)在識別未變更的單一符號上執行動畫(鈴鐺仍是「鈴鐺」但會彈跳)。.contentTransition(.symbolEffect(.replace))則在兩個不同符號之間執行精心設計的轉場(鈴鐺變成有斜線的鈴鐺)。前者用於強調狀態;後者用於替換符號識別。
Symbol effects在visionOS上能用嗎?
可以。Symbol effects在visionOS上以相同的方式渲染,系統的動態適應會尊重空間環境。Wiggle和rotate效果在visionOS上經過調校,讓在空間距離下感覺對勁;開發者不需為它們撰寫平台專屬的程式碼。
我可以撰寫自訂的symbol effects嗎?
框架的效果集合是封閉的;開發者不能定義新的效果類型。該集合已足夠豐富,自訂效果很少需要。對於符號詞彙以外的動畫,SwiftUI的原生動畫基元(.animation、withAnimation、Transaction、自訂Animatable views)是正確的工具,但逐幀成本由開發者自行管理。
使用symbol effects是否會影響App Store審核?
不會。Symbol effects是公開的SwiftUI API,審核方式與任何其他SwiftUI使用方式相同。人機介面準則積極鼓勵使用它們;遵循準則的應用程式比為相同目的建構自訂動畫系統的應用程式有更少的審核意外。
為什麼當我變更值時,我的.bounce效果不執行?
三個常見原因。第一,該值必須真的變更識別(@State Int從0變為1,而非重新指派同樣的Int 0)。第二,修飾符順序很重要:.symbolEffect(.bounce, value: foo)必須套用在Image上,而非包裹的Button或HStack上。第三,該效果每次識別變更執行一次;快速變更會合併。對於重複或保持的效果,請使用.repeating或isActive:,而非value:。
參考資料
-
Apple開發者文件:SwiftUI中的
SymbolEffect和.symbolEffect(_:options:value:)。 ↩