Loading...
Loading...
Compare original and translation side by side
axiom-sf-symbolsaxiom-swiftui-animation-refaxiom-sf-symbolsaxiom-swiftui-animation-ref// Basic display
Image(systemName: "star.fill")
// With Label (icon + text)
Label("Favorites", systemImage: "star.fill")
// Font sizing — symbol scales with text
Image(systemName: "star.fill")
.font(.title)
// Image scale — relative sizing without changing font
Image(systemName: "star.fill")
.imageScale(.large) // .small, .medium, .large
// Explicit point size
Image(systemName: "star.fill")
.font(.system(size: 24))
// Weight — matches SF Pro font weights
Image(systemName: "star.fill")
.fontWeight(.bold) // .ultraLight through .black
// Symbol variant — programmatic .fill, .circle, .square, .slash
Image(systemName: "person")
.symbolVariant(.circle.fill) // Renders person.circle.fill
// Variable value — 0.0 to 1.0, controls symbol fill level
Image(systemName: "speaker.wave.3.fill", variableValue: 0.5)// Basic display
Image(systemName: "star.fill")
// With Label (icon + text)
Label("Favorites", systemImage: "star.fill")
// Font sizing — symbol scales with text
Image(systemName: "star.fill")
.font(.title)
// Image scale — relative sizing without changing font
Image(systemName: "star.fill")
.imageScale(.large) // .small, .medium, .large
// Explicit point size
Image(systemName: "star.fill")
.font(.system(size: 24))
// Weight — matches SF Pro font weights
Image(systemName: "star.fill")
.fontWeight(.bold) // .ultraLight through .black
// Symbol variant — programmatic .fill, .circle, .square, .slash
Image(systemName: "person")
.symbolVariant(.circle.fill) // Renders person.circle.fill
// Variable value — 0.0 to 1.0, controls symbol fill level
Image(systemName: "speaker.wave.3.fill", variableValue: 0.5)// Basic display
let image = UIImage(systemName: "star.fill")
imageView.image = image
// Configuration — point size and weight
let config = UIImage.SymbolConfiguration(pointSize: 24, weight: .bold)
let image = UIImage(systemName: "star.fill", withConfiguration: config)
// Configuration — text style (scales with Dynamic Type)
let config = UIImage.SymbolConfiguration(textStyle: .title1)
let image = UIImage(systemName: "star.fill", withConfiguration: config)
// Configuration — scale
let config = UIImage.SymbolConfiguration(scale: .large) // .small, .medium, .large
// Combine configurations
let sizeConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .bold, scale: .large)
// Variable value
let image = UIImage(systemName: "speaker.wave.3.fill", variableValue: 0.5)// Basic display
let image = UIImage(systemName: "star.fill")
imageView.image = image
// Configuration — point size and weight
let config = UIImage.SymbolConfiguration(pointSize: 24, weight: .bold)
let image = UIImage(systemName: "star.fill", withConfiguration: config)
// Configuration — text style (scales with Dynamic Type)
let config = UIImage.SymbolConfiguration(textStyle: .title1)
let image = UIImage(systemName: "star.fill", withConfiguration: config)
// Configuration — scale
let config = UIImage.SymbolConfiguration(scale: .large) // .small, .medium, .large
// Combine configurations
let sizeConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .bold, scale: .large)
// Variable value
let image = UIImage(systemName: "speaker.wave.3.fill", variableValue: 0.5)// Basic display
let image = NSImage(systemSymbolName: "star.fill", accessibilityDescription: "Favorite")
// Configuration
let config = NSImage.SymbolConfiguration(pointSize: 24, weight: .bold)
let configured = image?.withSymbolConfiguration(config)// Basic display
let image = NSImage(systemSymbolName: "star.fill", accessibilityDescription: "Favorite")
// Configuration
let config = NSImage.SymbolConfiguration(pointSize: 24, weight: .bold)
let configured = image?.withSymbolConfiguration(config)// Monochrome (default)
Image(systemName: "cloud.rain.fill")
.foregroundStyle(.blue)
// Hierarchical — depth from single color
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.hierarchical)
.foregroundStyle(.blue)
// Palette — explicit color per layer
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .blue)
// For 3-layer symbols:
.foregroundStyle(.red, .white, .blue)
// Multicolor — Apple's curated colors
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.multicolor)
// Preferred rendering mode — uses symbol's preferred mode
// Falls back gracefully if the symbol doesn't support it
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.monochrome) // explicit monochrome// Monochrome (default)
Image(systemName: "cloud.rain.fill")
.foregroundStyle(.blue)
// Hierarchical — depth from single color
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.hierarchical)
.foregroundStyle(.blue)
// Palette — explicit color per layer
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.white, .blue)
// For 3-layer symbols:
.foregroundStyle(.red, .white, .blue)
// Multicolor — Apple's curated colors
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.multicolor)
// Preferred rendering mode — uses symbol's preferred mode
// Falls back gracefully if the symbol doesn't support it
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.monochrome) // explicit monochrome| Value | Description |
|---|---|
| Single color for all layers (default) |
| Single color with automatic opacity per layer |
| Explicit color per layer via |
| Apple's fixed curated colors |
| 值 | 描述 |
|---|---|
| 所有图层使用单一颜色(默认) |
| 单一颜色,各图层自动调整透明度以体现层次 |
| 通过 |
| 使用Apple预设的专属配色 |
// Hierarchical
let config = UIImage.SymbolConfiguration(hierarchicalColor: .systemBlue)
imageView.preferredSymbolConfiguration = config
// Palette
let config = UIImage.SymbolConfiguration(paletteColors: [.white, .systemBlue])
imageView.preferredSymbolConfiguration = config
// Multicolor
let config = UIImage.SymbolConfiguration.preferringMulticolor()
imageView.preferredSymbolConfiguration = config
// Monochrome — just set tintColor
imageView.tintColor = .systemBlue// Hierarchical
let config = UIImage.SymbolConfiguration(hierarchicalColor: .systemBlue)
imageView.preferredSymbolConfiguration = config
// Palette
let config = UIImage.SymbolConfiguration(paletteColors: [.white, .systemBlue])
imageView.preferredSymbolConfiguration = config
// Multicolor
let config = UIImage.SymbolConfiguration.preferringMulticolor()
imageView.preferredSymbolConfiguration = config
// Monochrome — just set tintColor
imageView.tintColor = .systemBluelet sizeConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .bold)
let colorConfig = UIImage.SymbolConfiguration(paletteColors: [.white, .blue, .gray])
let combined = sizeConfig.applying(colorConfig)
imageView.preferredSymbolConfiguration = combinedlet sizeConfig = UIImage.SymbolConfiguration(pointSize: 24, weight: .bold)
let colorConfig = UIImage.SymbolConfiguration(paletteColors: [.white, .blue, .gray])
let combined = sizeConfig.applying(colorConfig)
imageView.preferredSymbolConfiguration = combinedSymbolEffect| Protocol | Trigger | Modifier | Loop |
|---|---|---|---|
| | | No |
| | | Yes |
| View lifecycle | | No |
| Symbol change | | No |
SymbolEffect| 协议 | 触发方式 | 修改器 | 是否循环 |
|---|---|---|---|
| | | 否 |
| | | 是 |
| 视图生命周期 | | 否 |
| 符号变更 | | 否 |
// Strip all symbol effects from a view hierarchy
Image(systemName: "star.fill")
.symbolEffectsRemoved() // Removes all effects
.symbolEffectsRemoved(false) // Re-enables effects// Strip all symbol effects from a view hierarchy
Image(systemName: "star.fill")
.symbolEffectsRemoved() // Removes all effects
.symbolEffectsRemoved(false) // Re-enables effects// Speed multiplier
.symbolEffect(.bounce, options: .speed(2.0), value: count)
// Repeat count
.symbolEffect(.bounce, options: .repeat(3), value: count)
// Continuous repeat
.symbolEffect(.pulse, options: .repeat(.continuous), isActive: true)
// Non-repeating (for indefinite effects, run once then hold)
.symbolEffect(.breathe, options: .nonRepeating, isActive: true)
// Combined
.symbolEffect(.wiggle, options: .repeat(5).speed(1.5), value: count)// Speed multiplier
.symbolEffect(.bounce, options: .speed(2.0), value: count)
// Repeat count
.symbolEffect(.bounce, options: .repeat(3), value: count)
// Continuous repeat
.symbolEffect(.pulse, options: .repeat(.continuous), isActive: true)
// Non-repeating (for indefinite effects, run once then hold)
.symbolEffect(.breathe, options: .nonRepeating, isActive: true)
// Combined
.symbolEffect(.wiggle, options: .repeat(5).speed(1.5), value: count)DiscreteSymbolEffect// Discrete — triggers on value change
Image(systemName: "arrow.down.circle")
.symbolEffect(.bounce, value: downloadCount)
// Directional
.symbolEffect(.bounce.up, value: count)
.symbolEffect(.bounce.down, value: count)
// By Layer — different layers bounce at different times
.symbolEffect(.bounce.byLayer, value: count)
// Whole Symbol — entire symbol bounces together
.symbolEffect(.bounce.wholeSymbol, value: count)imageView.addSymbolEffect(.bounce)
// With options:
imageView.addSymbolEffect(.bounce, options: .repeat(3))DiscreteSymbolEffect// Discrete — triggers on value change
Image(systemName: "arrow.down.circle")
.symbolEffect(.bounce, value: downloadCount)
// Directional
.symbolEffect(.bounce.up, value: count)
.symbolEffect(.bounce.down, value: count)
// By Layer — different layers bounce at different times
.symbolEffect(.bounce.byLayer, value: count)
// Whole Symbol — entire symbol bounces together
.symbolEffect(.bounce.wholeSymbol, value: count)imageView.addSymbolEffect(.bounce)
// With options:
imageView.addSymbolEffect(.bounce, options: .repeat(3))DiscreteSymbolEffectIndefiniteSymbolEffect// Indefinite — continuous while active
Image(systemName: "network")
.symbolEffect(.pulse, isActive: isConnecting)
// Discrete — triggers once on value change
.symbolEffect(.pulse, value: errorCount)
// By Layer
.symbolEffect(.pulse.byLayer, isActive: true)
// Whole Symbol
.symbolEffect(.pulse.wholeSymbol, isActive: true)imageView.addSymbolEffect(.pulse)
imageView.removeSymbolEffect(ofType: PulseSymbolEffect.self)DiscreteSymbolEffectIndefiniteSymbolEffect// Indefinite — continuous while active
Image(systemName: "network")
.symbolEffect(.pulse, isActive: isConnecting)
// Discrete — triggers once on value change
.symbolEffect(.pulse, value: errorCount)
// By Layer
.symbolEffect(.pulse.byLayer, isActive: true)
// Whole Symbol
.symbolEffect(.pulse.wholeSymbol, isActive: true)imageView.addSymbolEffect(.pulse)
imageView.removeSymbolEffect(ofType: PulseSymbolEffect.self)DiscreteSymbolEffectIndefiniteSymbolEffect// Iterative — highlights one layer at a time
Image(systemName: "wifi")
.symbolEffect(.variableColor.iterative, isActive: isSearching)
// Cumulative — progressively fills layers
.symbolEffect(.variableColor.cumulative, isActive: true)
// Reversing — cycles back and forth
.symbolEffect(.variableColor.iterative.reversing, isActive: true)
// Hide inactive layers (dims non-highlighted layers)
.symbolEffect(.variableColor.iterative.hideInactiveLayers, isActive: true)
// Dim inactive layers (slightly reduces opacity of non-highlighted)
.symbolEffect(.variableColor.iterative.dimInactiveLayers, isActive: true)imageView.addSymbolEffect(.variableColor.iterative)
imageView.removeSymbolEffect(ofType: VariableColorSymbolEffect.self)DiscreteSymbolEffectIndefiniteSymbolEffect// Iterative — highlights one layer at a time
Image(systemName: "wifi")
.symbolEffect(.variableColor.iterative, isActive: isSearching)
// Cumulative — progressively fills layers
.symbolEffect(.variableColor.cumulative, isActive: true)
// Reversing — cycles back and forth
.symbolEffect(.variableColor.iterative.reversing, isActive: true)
// Hide inactive layers (dims non-highlighted layers)
.symbolEffect(.variableColor.iterative.hideInactiveLayers, isActive: true)
// Dim inactive layers (slightly reduces opacity of non-highlighted)
.symbolEffect(.variableColor.iterative.dimInactiveLayers, isActive: true)imageView.addSymbolEffect(.variableColor.iterative)
imageView.removeSymbolEffect(ofType: VariableColorSymbolEffect.self)IndefiniteSymbolEffect// Scale up
Image(systemName: "mic.fill")
.symbolEffect(.scale.up, isActive: isRecording)
// Scale down
.symbolEffect(.scale.down, isActive: isMuted)
// By Layer
.symbolEffect(.scale.up.byLayer, isActive: true)
// Whole Symbol
.symbolEffect(.scale.up.wholeSymbol, isActive: true)imageView.addSymbolEffect(.scale.up)
imageView.removeSymbolEffect(ofType: ScaleSymbolEffect.self)IndefiniteSymbolEffect// Scale up
Image(systemName: "mic.fill")
.symbolEffect(.scale.up, isActive: isRecording)
// Scale down
.symbolEffect(.scale.down, isActive: isMuted)
// By Layer
.symbolEffect(.scale.up.byLayer, isActive: true)
// Whole Symbol
.symbolEffect(.scale.up.wholeSymbol, isActive: true)imageView.addSymbolEffect(.scale.up)
imageView.removeSymbolEffect(ofType: ScaleSymbolEffect.self)DiscreteSymbolEffectIndefiniteSymbolEffect// Discrete
Image(systemName: "bell.fill")
.symbolEffect(.wiggle, value: notificationCount)
// Directional
.symbolEffect(.wiggle.left, value: count)
.symbolEffect(.wiggle.right, value: count)
.symbolEffect(.wiggle.forward, value: count) // RTL-aware
.symbolEffect(.wiggle.backward, value: count) // RTL-aware
.symbolEffect(.wiggle.up, value: count)
.symbolEffect(.wiggle.down, value: count)
.symbolEffect(.wiggle.clockwise, value: count)
.symbolEffect(.wiggle.counterClockwise, value: count)
// Custom angle
.symbolEffect(.wiggle.custom(angle: .degrees(15)), value: count)
// By Layer
.symbolEffect(.wiggle.byLayer, value: count)imageView.addSymbolEffect(.wiggle)DiscreteSymbolEffectIndefiniteSymbolEffect// Discrete
Image(systemName: "bell.fill")
.symbolEffect(.wiggle, value: notificationCount)
// Directional
.symbolEffect(.wiggle.left, value: count)
.symbolEffect(.wiggle.right, value: count)
.symbolEffect(.wiggle.forward, value: count) // 支持RTL布局
.symbolEffect(.wiggle.backward, value: count) // 支持RTL布局
.symbolEffect(.wiggle.up, value: count)
.symbolEffect(.wiggle.down, value: count)
.symbolEffect(.wiggle.clockwise, value: count)
.symbolEffect(.wiggle.counterClockwise, value: count)
// Custom angle
.symbolEffect(.wiggle.custom(angle: .degrees(15)), value: count)
// By Layer
.symbolEffect(.wiggle.byLayer, value: count)imageView.addSymbolEffect(.wiggle)DiscreteSymbolEffectIndefiniteSymbolEffect// Indefinite rotation
Image(systemName: "gear")
.symbolEffect(.rotate, isActive: isProcessing)
// Direction
.symbolEffect(.rotate.clockwise, isActive: true)
.symbolEffect(.rotate.counterClockwise, isActive: true)
// By Layer — only specific layers rotate (e.g., fan blades)
.symbolEffect(.rotate.byLayer, isActive: true)imageView.addSymbolEffect(.rotate)
imageView.removeSymbolEffect(ofType: RotateSymbolEffect.self)DiscreteSymbolEffectIndefiniteSymbolEffect// Indefinite rotation
Image(systemName: "gear")
.symbolEffect(.rotate, isActive: isProcessing)
// Direction
.symbolEffect(.rotate.clockwise, isActive: true)
.symbolEffect(.rotate.counterClockwise, isActive: true)
// By Layer — only specific layers rotate (e.g., fan blades)
.symbolEffect(.rotate.byLayer, isActive: true)imageView.addSymbolEffect(.rotate)
imageView.removeSymbolEffect(ofType: RotateSymbolEffect.self)DiscreteSymbolEffectIndefiniteSymbolEffect// Basic breathe
Image(systemName: "heart.fill")
.symbolEffect(.breathe, isActive: isMonitoring)
// Plain — scale only
.symbolEffect(.breathe.plain, isActive: true)
// Pulse — scale + opacity variation
.symbolEffect(.breathe.pulse, isActive: true)
// By Layer
.symbolEffect(.breathe.byLayer, isActive: true)imageView.addSymbolEffect(.breathe)
imageView.removeSymbolEffect(ofType: BreatheSymbolEffect.self)DiscreteSymbolEffectIndefiniteSymbolEffect// Basic breathe
Image(systemName: "heart.fill")
.symbolEffect(.breathe, isActive: isMonitoring)
// Plain — scale only
.symbolEffect(.breathe.plain, isActive: true)
// Pulse — scale + opacity variation
.symbolEffect(.breathe.pulse, isActive: true)
// By Layer
.symbolEffect(.breathe.byLayer, isActive: true)imageView.addSymbolEffect(.breathe)
imageView.removeSymbolEffect(ofType: BreatheSymbolEffect.self)TransitionSymbolEffect// SwiftUI transition
if showSymbol {
Image(systemName: "checkmark.circle.fill")
.transition(.symbolEffect(.appear))
}
if showSymbol {
Image(systemName: "xmark.circle.fill")
.transition(.symbolEffect(.disappear))
}
// Directional
.transition(.symbolEffect(.appear.up))
.transition(.symbolEffect(.appear.down))
.transition(.symbolEffect(.disappear.up))
.transition(.symbolEffect(.disappear.down))
// By Layer
.transition(.symbolEffect(.appear.byLayer))
// Whole Symbol
.transition(.symbolEffect(.appear.wholeSymbol))// Make symbol appear
imageView.addSymbolEffect(.appear)
// Make symbol disappear
imageView.addSymbolEffect(.disappear)
// Appear after disappear
imageView.addSymbolEffect(.appear) // re-shows hidden symbolTransitionSymbolEffect// SwiftUI transition
if showSymbol {
Image(systemName: "checkmark.circle.fill")
.transition(.symbolEffect(.appear))
}
if showSymbol {
Image(systemName: "xmark.circle.fill")
.transition(.symbolEffect(.disappear))
}
// Directional
.transition(.symbolEffect(.appear.up))
.transition(.symbolEffect(.appear.down))
.transition(.symbolEffect(.disappear.up))
.transition(.symbolEffect(.disappear.down))
// By Layer
.transition(.symbolEffect(.appear.byLayer))
// Whole Symbol
.transition(.symbolEffect(.appear.wholeSymbol))// Make symbol appear
imageView.addSymbolEffect(.appear)
// Make symbol disappear
imageView.addSymbolEffect(.disappear)
// Appear after disappear
imageView.addSymbolEffect(.appear) // re-shows hidden symbolContentTransitionSymbolEffect// SwiftUI content transition
Image(systemName: isFavorite ? "star.fill" : "star")
.contentTransition(.symbolEffect(.replace))
// Directional variants
.contentTransition(.symbolEffect(.replace.downUp))
.contentTransition(.symbolEffect(.replace.upUp))
.contentTransition(.symbolEffect(.replace.offUp))
// By Layer
.contentTransition(.symbolEffect(.replace.byLayer))
// Whole Symbol
.contentTransition(.symbolEffect(.replace.wholeSymbol))
// Magic Replace — default in iOS 18+, morphs shared elements
// Automatic for structurally related pairs: star ↔ star.fill, pause.fill ↔ play.fill
.contentTransition(.symbolEffect(.replace))
// Explicit Magic Replace with fallback for unrelated symbols
.contentTransition(.symbolEffect(.replace.magic(fallback: .replace.downUp)))// Change symbol with Replace transition
let newImage = UIImage(systemName: "star.fill")
imageView.setSymbolImage(newImage!, contentTransition: .replace)
// Directional
imageView.setSymbolImage(newImage!, contentTransition: .replace.downUp)ContentTransitionSymbolEffect// SwiftUI content transition
Image(systemName: isFavorite ? "star.fill" : "star")
.contentTransition(.symbolEffect(.replace))
// Directional variants
.contentTransition(.symbolEffect(.replace.downUp))
.contentTransition(.symbolEffect(.replace.upUp))
.contentTransition(.symbolEffect(.replace.offUp))
// By Layer
.contentTransition(.symbolEffect(.replace.byLayer))
// Whole Symbol
.contentTransition(.symbolEffect(.replace.wholeSymbol))
// Magic Replace — default in iOS 18+, morphs shared elements
// Automatic for structurally related pairs: star ↔ star.fill, pause.fill ↔ play.fill
.contentTransition(.symbolEffect(.replace))
// Explicit Magic Replace with fallback for unrelated symbols
.contentTransition(.symbolEffect(.replace.magic(fallback: .replace.downUp)))// Change symbol with Replace transition
let newImage = UIImage(systemName: "star.fill")
imageView.setSymbolImage(newImage!, contentTransition: .replace)
// Directional
imageView.setSymbolImage(newImage!, contentTransition: .replace.downUp)// Indefinite — draws in while active
Image(systemName: "checkmark.circle")
.symbolEffect(.drawOn, isActive: isComplete)
// Playback modes
.symbolEffect(.drawOn.byLayer, isActive: isActive)
.symbolEffect(.drawOn.wholeSymbol, isActive: isActive)
.symbolEffect(.drawOn.individually, isActive: isActive)
// With options
.symbolEffect(.drawOn, options: .speed(2.0), isActive: isActive)
.symbolEffect(.drawOn, options: .nonRepeating, isActive: isActive)// Indefinite — draws in while active
Image(systemName: "checkmark.circle")
.symbolEffect(.drawOn, isActive: isComplete)
// Playback modes
.symbolEffect(.drawOn.byLayer, isActive: isActive)
.symbolEffect(.drawOn.wholeSymbol, isActive: isActive)
.symbolEffect(.drawOn.individually, isActive: isActive)
// With options
.symbolEffect(.drawOn, options: .speed(2.0), isActive: isActive)
.symbolEffect(.drawOn, options: .nonRepeating, isActive: isActive)// Indefinite — draws out while active
Image(systemName: "star.fill")
.symbolEffect(.drawOff, isActive: isHidden)
// Playback modes
.symbolEffect(.drawOff.byLayer, isActive: isActive)
.symbolEffect(.drawOff.wholeSymbol, isActive: isActive)
.symbolEffect(.drawOff.individually, isActive: isActive)
// Direction control
.symbolEffect(.drawOff.nonReversed, isActive: isActive) // follows draw path forward
.symbolEffect(.drawOff.reversed, isActive: isActive) // erases in reverse order// Indefinite — draws out while active
Image(systemName: "star.fill")
.symbolEffect(.drawOff, isActive: isHidden)
// Playback modes
.symbolEffect(.drawOff.byLayer, isActive: isActive)
.symbolEffect(.drawOff.wholeSymbol, isActive: isActive)
.symbolEffect(.drawOff.individually, isActive: isActive)
// Direction control
.symbolEffect(.drawOff.nonReversed, isActive: isActive) // 沿绘制路径正向擦除
.symbolEffect(.drawOff.reversed, isActive: isActive) // 沿绘制路径反向擦除// Draw On
imageView.addSymbolEffect(.drawOn)
// Draw Off
imageView.addSymbolEffect(.drawOff)
// Remove
imageView.removeSymbolEffect(ofType: DrawOnSymbolEffect.self)// Draw On
imageView.addSymbolEffect(.drawOn)
// Draw Off
imageView.addSymbolEffect(.drawOff)
// Remove
imageView.removeSymbolEffect(ofType: DrawOnSymbolEffect.self)SymbolVariableValueMode// Variable Draw — draws stroke proportional to value (iOS 26+)
Image(systemName: "thermometer.high", variableValue: temperature)
.symbolVariableValueMode(.draw)
// Variable Color — sets layer opacity based on threshold (iOS 17+, default)
Image(systemName: "wifi", variableValue: signalStrength)
.symbolVariableValueMode(.color)SymbolVariableValueMode// Variable Draw — draws stroke proportional to value (iOS 26+)
Image(systemName: "thermometer.high", variableValue: temperature)
.symbolVariableValueMode(.draw)
// Variable Color — sets layer opacity based on threshold (iOS 17+, default)
Image(systemName: "wifi", variableValue: signalStrength)
.symbolVariableValueMode(.color)| Case | Description |
|---|---|
| Sets opacity of each variable layer on/off based on threshold (existing behavior) |
| Changes drawn length of each variable layer based on range |
| 枚举值 | 描述 |
|---|---|
| 根据阈值设置每个变量图层的显示/隐藏(现有默认行为) |
| 根据数值范围改变每个变量图层的绘制长度 |
SymbolColorRenderingMode// Gradient fill — system generates axial gradient from source color
Image(systemName: "heart.fill")
.symbolColorRenderingMode(.gradient)
.foregroundStyle(.red)
// Works with any rendering mode
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.hierarchical)
.symbolColorRenderingMode(.gradient)
.foregroundStyle(.blue)SymbolColorRenderingMode// Gradient fill — system generates axial gradient from source color
Image(systemName: "heart.fill")
.symbolColorRenderingMode(.gradient)
.foregroundStyle(.red)
// Works with any rendering mode
Image(systemName: "cloud.rain.fill")
.symbolRenderingMode(.hierarchical)
.symbolColorRenderingMode(.gradient)
.foregroundStyle(.blue)| Case | Description |
|---|---|
| Solid color fill (default) |
| Axial gradient generated from source color |
| 枚举值 | 描述 |
|---|---|
| 纯色填充(默认) |
| 从源颜色生成轴向渐变 |
struct PlayPauseButton: View {
@State private var isPlaying = false
var body: some View {
Button {
isPlaying.toggle()
} label: {
Image(systemName: isPlaying ? "pause.fill" : "play.fill")
.contentTransition(.symbolEffect(.replace))
}
.accessibilityLabel(isPlaying ? "Pause" : "Play")
}
}struct PlayPauseButton: View {
@State private var isPlaying = false
var body: some View {
Button {
isPlaying.toggle()
} label: {
Image(systemName: isPlaying ? "pause.fill" : "play.fill")
.contentTransition(.symbolEffect(.replace))
}
.accessibilityLabel(isPlaying ? "Pause" : "Play")
}
}struct DownloadButton: View {
@State private var state: DownloadState = .idle
var symbolName: String {
switch state {
case .idle: "arrow.down.circle"
case .downloading: "stop.circle"
case .complete: "checkmark.circle.fill"
}
}
var body: some View {
Button {
advanceState()
} label: {
Image(systemName: symbolName)
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.pulse, isActive: state == .downloading)
}
}
}struct DownloadButton: View {
@State private var state: DownloadState = .idle
var symbolName: String {
switch state {
case .idle: "arrow.down.circle"
case .downloading: "stop.circle"
case .complete: "checkmark.circle.fill"
}
}
var body: some View {
Button {
advanceState()
} label: {
Image(systemName: symbolName)
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.pulse, isActive: state == .downloading)
}
}
}struct FavoriteButton: View {
@Binding var isFavorite: Bool
@State private var bounceValue = 0
var body: some View {
Button {
isFavorite.toggle()
bounceValue += 1
} label: {
Image(systemName: isFavorite ? "star.fill" : "star")
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.bounce, value: bounceValue)
.foregroundStyle(isFavorite ? .yellow : .gray)
}
}
}struct FavoriteButton: View {
@Binding var isFavorite: Bool
@State private var bounceValue = 0
var body: some View {
Button {
isFavorite.toggle()
bounceValue += 1
} label: {
Image(systemName: isFavorite ? "star.fill" : "star")
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.bounce, value: bounceValue)
.foregroundStyle(isFavorite ? .yellow : .gray)
}
}
}.svg.svg| Point Type | Visual | Purpose |
|---|---|---|
| Start | Open circle | Where drawing begins |
| End | Closed circle | Where drawing ends |
| Corner | Diamond | Sharp direction change |
| Bidirectional | Double arrow | Center-outward drawing |
| Attachment | Link icon | Non-drawing decorative connection |
| 点类型 | 视觉标识 | 用途 |
|---|---|---|
| Start | 空心圆 | 绘制起始位置 |
| End | 实心圆 | 绘制结束位置 |
| Corner | 菱形 | 尖锐方向变化点 |
| Bidirectional | 双箭头 | 从中心向外绘制 |
| Attachment | 链接图标 | 非绘制装饰性连接 |
.svgImage("custom.symbol.name")Image(systemName: "custom.symbol.name", bundle: .module).svgImage("custom.symbol.name")Image(systemName: "custom.symbol.name", bundle: .module)| Feature | iOS | macOS | watchOS | tvOS | visionOS |
|---|---|---|---|---|---|
| Monochrome | 13+ | 11+ | 6+ | 13+ | 1+ |
| Hierarchical | 15+ | 12+ | 8+ | 15+ | 1+ |
| Palette | 15+ | 12+ | 8+ | 15+ | 1+ |
| Multicolor | 15+ | 12+ | 8+ | 15+ | 1+ |
| Variable Value | 16+ | 13+ | 9+ | 16+ | 1+ |
| 功能 | iOS | macOS | watchOS | tvOS | visionOS |
|---|---|---|---|---|---|
| 单色 | 13+ | 11+ | 6+ | 13+ | 1+ |
| 层次化 | 15+ | 12+ | 8+ | 15+ | 1+ |
| 调色板 | 15+ | 12+ | 8+ | 15+ | 1+ |
| 多色 | 15+ | 12+ | 8+ | 15+ | 1+ |
| 变量值 | 16+ | 13+ | 9+ | 16+ | 1+ |
| Effect | Category | iOS | macOS | watchOS | tvOS | visionOS |
|---|---|---|---|---|---|---|
| Bounce | Discrete | 17+ | 14+ | 10+ | 17+ | 1+ |
| Pulse | Discrete/Indefinite | 17+ | 14+ | 10+ | 17+ | 1+ |
| Variable Color | Discrete/Indefinite | 17+ | 14+ | 10+ | 17+ | 1+ |
| Scale | Indefinite | 17+ | 14+ | 10+ | 17+ | 1+ |
| Appear | Transition | 17+ | 14+ | 10+ | 17+ | 1+ |
| Disappear | Transition | 17+ | 14+ | 10+ | 17+ | 1+ |
| Replace | Content Transition | 17+ | 14+ | 10+ | 17+ | 1+ |
| Wiggle | Discrete/Indefinite | 18+ | 15+ | 11+ | 18+ | 2+ |
| Rotate | Discrete/Indefinite | 18+ | 15+ | 11+ | 18+ | 2+ |
| Breathe | Discrete/Indefinite | 18+ | 15+ | 11+ | 18+ | 2+ |
| Draw On | Indefinite | 26+ | Tahoe+ | 26+ | 26+ | 26+ |
| Draw Off | Indefinite | 26+ | Tahoe+ | 26+ | 26+ | 26+ |
| Variable Draw | Value-based | 26+ | Tahoe+ | 26+ | 26+ | 26+ |
| Gradient Fill | Rendering | 26+ | Tahoe+ | 26+ | 26+ | 26+ |
| 效果 | 分类 | iOS | macOS | watchOS | tvOS | visionOS |
|---|---|---|---|---|---|---|
| 弹跳 | 离散型 | 17+ | 14+ | 10+ | 17+ | 1+ |
| 脉冲 | 离散型/无限循环 | 17+ | 14+ | 10+ | 17+ | 1+ |
| 可变颜色 | 离散型/无限循环 | 17+ | 14+ | 10+ | 17+ | 1+ |
| 缩放 | 无限循环 | 17+ | 14+ | 10+ | 17+ | 1+ |
| 出现 | 转场型 | 17+ | 14+ | 10+ | 17+ | 1+ |
| 消失 | 转场型 | 17+ | 14+ | 10+ | 17+ | 1+ |
| 替换 | 内容转场 | 17+ | 14+ | 10+ | 17+ | 1+ |
| 摇摆 | 离散型/无限循环 | 18+ | 15+ | 11+ | 18+ | 2+ |
| 旋转 | 离散型/无限循环 | 18+ | 15+ | 11+ | 18+ | 2+ |
| 呼吸 | 离散型/无限循环 | 18+ | 15+ | 11+ | 18+ | 2+ |
| 绘制出现 | 无限循环 | 26+ | Tahoe+ | 26+ | 26+ | 26+ |
| 绘制消失 | 无限循环 | 26+ | Tahoe+ | 26+ | 26+ | 26+ |
| 可变绘制 | 基于值 | 26+ | Tahoe+ | 26+ | 26+ | 26+ |
| 渐变填充 | 渲染 | 26+ | Tahoe+ | 26+ | 26+ | 26+ |
| Category | What It Does | How to Trigger |
|---|---|---|
| Discrete | One-shot animation, returns to rest | |
| Indefinite | Loops while active | |
| Transition | Plays on view insert/remove | |
| Content Transition | Plays when symbol changes | |
| 分类 | 功能 | 触发方式 |
|---|---|---|
| 离散型 | 一次性动画,结束后回到初始状态 | |
| 无限循环型 | 激活时持续循环 | |
| 转场型 | 在视图插入/移除时播放 | |
| 内容转场型 | 在符号变更时播放 | |
// Add indefinite effect
imageView.addSymbolEffect(.pulse)
imageView.addSymbolEffect(.breathe)
imageView.addSymbolEffect(.rotate)
imageView.addSymbolEffect(.variableColor.iterative)
imageView.addSymbolEffect(.scale.up)
// Add with options
imageView.addSymbolEffect(.bounce, options: .repeat(3))
imageView.addSymbolEffect(.pulse, options: .speed(2.0))
// Add with completion handler
imageView.addSymbolEffect(.bounce, options: .default) { context in
// Called when effect finishes
print("Bounce complete")
}// Add indefinite effect
imageView.addSymbolEffect(.pulse)
imageView.addSymbolEffect(.breathe)
imageView.addSymbolEffect(.rotate)
imageView.addSymbolEffect(.variableColor.iterative)
imageView.addSymbolEffect(.scale.up)
// Add with options
imageView.addSymbolEffect(.bounce, options: .repeat(3))
imageView.addSymbolEffect(.pulse, options: .speed(2.0))
// Add with completion handler
imageView.addSymbolEffect(.bounce, options: .default) { context in
// Called when effect finishes
print("Bounce complete")
}// Remove specific effect type
imageView.removeSymbolEffect(ofType: PulseSymbolEffect.self)
imageView.removeSymbolEffect(ofType: ScaleSymbolEffect.self)
imageView.removeSymbolEffect(ofType: RotateSymbolEffect.self)
// Remove all effects
imageView.removeAllSymbolEffects()
// Remove with options
imageView.removeSymbolEffect(ofType: PulseSymbolEffect.self, options: .default)
// Remove with completion
imageView.removeSymbolEffect(ofType: PulseSymbolEffect.self) { context in
print("Pulse removed")
}// Remove specific effect type
imageView.removeSymbolEffect(ofType: PulseSymbolEffect.self)
imageView.removeSymbolEffect(ofType: ScaleSymbolEffect.self)
imageView.removeSymbolEffect(ofType: RotateSymbolEffect.self)
// Remove all effects
imageView.removeAllSymbolEffects()
// Remove with options
imageView.removeSymbolEffect(ofType: PulseSymbolEffect.self, options: .default)
// Remove with completion
imageView.removeSymbolEffect(ofType: PulseSymbolEffect.self) { context in
print("Pulse removed")
}// Replace with content transition
let newImage = UIImage(systemName: "pause.fill")!
imageView.setSymbolImage(newImage, contentTransition: .replace)
// Directional replace
imageView.setSymbolImage(newImage, contentTransition: .replace.downUp)
imageView.setSymbolImage(newImage, contentTransition: .replace.upUp)
imageView.setSymbolImage(newImage, contentTransition: .replace.offUp)
// With options
imageView.setSymbolImage(newImage, contentTransition: .replace, options: .speed(2.0))// Replace with content transition
let newImage = UIImage(systemName: "pause.fill")!
imageView.setSymbolImage(newImage, contentTransition: .replace)
// Directional replace
imageView.setSymbolImage(newImage, contentTransition: .replace.downUp)
imageView.setSymbolImage(newImage, contentTransition: .replace.upUp)
imageView.setSymbolImage(newImage, contentTransition: .replace.offUp)
// With options
imageView.setSymbolImage(newImage, contentTransition: .replace, options: .speed(2.0))// Effects also work on UIBarButtonItem
barButtonItem.addSymbolEffect(.bounce)
barButtonItem.addSymbolEffect(.pulse, isActive: isLoading)
barButtonItem.removeSymbolEffect(ofType: PulseSymbolEffect.self)// Effects also work on UIBarButtonItem
barButtonItem.addSymbolEffect(.bounce)
barButtonItem.addSymbolEffect(.pulse, isActive: isLoading)
barButtonItem.removeSymbolEffect(ofType: PulseSymbolEffect.self)// SwiftUI
Image(systemName: "star.fill")
.accessibilityLabel("Favorite")
// UIKit
let image = UIImage(systemName: "star.fill")
imageView.accessibilityLabel = "Favorite"
imageView.isAccessibilityElement = true
// Label automatically provides accessibility
Label("Settings", systemImage: "gear")
// VoiceOver reads: "Settings"// SwiftUI
Image(systemName: "star.fill")
.accessibilityLabel("Favorite")
// UIKit
let image = UIImage(systemName: "star.fill")
imageView.accessibilityLabel = "Favorite"
imageView.isAccessibilityElement = true
// Label automatically provides accessibility
Label("Settings", systemImage: "gear")
// VoiceOver reads: "Settings"UIAccessibility.isReduceMotionEnabled// If the pulsing conveys connection status, provide a text label
Image(systemName: "wifi")
.symbolEffect(.pulse, isActive: isConnecting)
.accessibilityLabel(isConnecting ? "Connecting to WiFi" : "WiFi connected")UIAccessibility.isReduceMotionEnabled// 如果脉冲效果表示连接状态,请提供文本标签
Image(systemName: "wifi")
.symbolEffect(.pulse, isActive: isConnecting)
.accessibilityLabel(isConnecting ? "Connecting to WiFi" : "WiFi connected").font().font(.system(size: 24))// ✅ Scales with Dynamic Type
Image(systemName: "star.fill")
.font(.title)
// ❌ Fixed size, does not scale
Image(systemName: "star.fill")
.font(.system(size: 24)).font().font(.system(size: 24))// ✅ 随动态类型缩放
Image(systemName: "star.fill")
.font(.title)
// ❌ 固定尺寸,不缩放
Image(systemName: "star.fill")
.font(.system(size: 24))struct NotificationBell: View {
let count: Int
var body: some View {
Image(systemName: count > 0 ? "bell.badge.fill" : "bell.fill")
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.wiggle, value: count)
.symbolRenderingMode(.palette)
.foregroundStyle(count > 0 ? .red : .primary, .primary)
}
}struct NotificationBell: View {
let count: Int
var body: some View {
Image(systemName: count > 0 ? "bell.badge.fill" : "bell.fill")
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.wiggle, value: count)
.symbolRenderingMode(.palette)
.foregroundStyle(count > 0 ? .red : .primary, .primary)
}
}struct WiFiIndicator: View {
let strength: Double // 0.0 to 1.0
let isSearching: Bool
var body: some View {
Image(systemName: "wifi", variableValue: strength)
.symbolEffect(.variableColor.iterative, isActive: isSearching)
.symbolRenderingMode(.hierarchical)
.accessibilityLabel(
isSearching ? "Searching for WiFi" :
"WiFi strength: \(Int(strength * 100))%"
)
}
}struct WiFiIndicator: View {
let strength: Double // 0.0 to 1.0
let isSearching: Bool
var body: some View {
Image(systemName: "wifi", variableValue: strength)
.symbolEffect(.variableColor.iterative, isActive: isSearching)
.symbolRenderingMode(.hierarchical)
.accessibilityLabel(
isSearching ? "Searching for WiFi" :
"WiFi strength: \(Int(strength * 100))%"
)
}
}struct RecordButton: View {
@State private var isRecording = false
var body: some View {
Button {
isRecording.toggle()
} label: {
Image(systemName: isRecording ? "stop.circle.fill" : "record.circle")
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.breathe.pulse, isActive: isRecording)
.font(.largeTitle)
.foregroundStyle(isRecording ? .red : .primary)
}
.accessibilityLabel(isRecording ? "Stop recording" : "Start recording")
}
}struct RecordButton: View {
@State private var isRecording = false
var body: some View {
Button {
isRecording.toggle()
} label: {
Image(systemName: isRecording ? "stop.circle.fill" : "record.circle")
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.breathe.pulse, isActive: isRecording)
.font(.largeTitle)
.foregroundStyle(isRecording ? .red : .primary)
}
.accessibilityLabel(isRecording ? "Stop recording" : "Start recording")
}
}struct TaskCheckbox: View {
@State private var isComplete = false
var body: some View {
Button {
isComplete.toggle()
} label: {
Image(systemName: isComplete ? "checkmark.circle.fill" : "circle")
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.drawOn, isActive: isComplete)
.font(.title2)
.foregroundStyle(isComplete ? .green : .secondary)
}
.accessibilityLabel(isComplete ? "Completed" : "Not completed")
}
}struct TaskCheckbox: View {
@State private var isComplete = false
var body: some View {
Button {
isComplete.toggle()
} label: {
Image(systemName: isComplete ? "checkmark.circle.fill" : "circle")
.contentTransition(.symbolEffect(.replace))
.symbolEffect(.drawOn, isActive: isComplete)
.font(.title2)
.foregroundStyle(isComplete ? .green : .secondary)
}
.accessibilityLabel(isComplete ? "Completed" : "Not completed")
}
}