mcpField
A
axiom-sf-symbols

axiom-sf-symbols

github.com/charleswiltgen设计与 UI1.2k134 次安装今天更新
代码仓库

关于 axiom-sf-symbols

面向 SwiftUI 开发者的 SF Symbols 使用指南,涵盖渲染模式、符号效果和自定义符号制作。还介绍 SF Symbols 7 的 Draw 动画、效果选择及无障碍相关注意事项。

主要功能

  1. 01四种符号渲染模式
  2. 02离散与持续符号效果
  3. 03过渡和内容替换效果
  4. 04SF Symbols 7 Draw 动画
  5. 05自定义符号制作与无障碍

适用场景

  1. 01为图标选择渲染模式
  2. 02为按钮或加载状态添加动画
  3. 03排查符号颜色和效果问题
  4. 04制作支持 Draw 动画的自定义符号

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-sf-symbols

安装后在 Claude Code 中输入/axiom-sf-symbols确认已加载。

详细介绍

SF Symbols — 效果、渲染与自定义符号

何时使用此技能

适用于:

  • 在渲染模式(Monochrome、Hierarchical、Palette、Multicolor)之间进行选择
  • 实现符号效果或动画(Bounce、Pulse、Scale、Wiggle、Rotate、Breathe、Draw)
  • 使用 SF Symbols 7 的 Draw On/Off 动画
  • 在 SF Symbols 应用中创建自定义符号
  • 排查符号颜色、效果未播放或字重不匹配的问题
  • 确定哪种效果适合特定的 UX 目的
  • 处理符号动画的辅助功能(Reduce Motion)
相关技能
  • 使用 axiom-sf-symbols-ref 获取完整的 API 参考,包括所有修饰符、UIKit 对应项和平台可用性矩阵
  • 使用 axiom-swiftui-animation-ref 获取通用 SwiftUI 动画参考(非符号专用)
  • 使用 axiom-hig-ref 获取更广泛的图标设计指南

示例提示词

1. “我的 SF Symbol 显示为单一纯色,但我想让它通过多种色调呈现层次感。该如何解决?”

此技能介绍如何选择渲染模式:使用 Hierarchical 通过单一颜色呈现层次感,使用 Palette 为各图层指定颜色

2. “我想让下载按钮在点按时播放动画,下载过程中显示旋转指示器,完成后再动画变成勾号。”

此技能介绍效果选择:使用 Bounce 提供点按反馈,使用 Breathe/Pulse 表示进行中,使用 Replace 配合内容过渡效果表示完成

3. “我尝试使用 SF Symbols 7 中新增的 Draw 动画,但效果没有播放。”

此技能介绍 Draw On/Off 的实现、播放模式、iOS 26 要求和常见故障排查方法

4. “如何创建支持所有渲染模式和新增 Draw 动画的自定义符号?”

此技能介绍自定义符号的创作流程、模板图层,以及使用引导点进行 Draw 标注


第 1 部分:渲染模式决策树

SF Symbols 支持 4 种渲染模式。正确的选择取决于你的设计意图。

快速决策
Need depth from ONE color?           → Hierarchical
Need specific colors per layer?      → Palette
Want Apple's curated colors?         → Multicolor
Just need a tinted icon?             → Monochrome (default)
Monochrome

默认模式。所有图层都使用相同的颜色(你的 foregroundStyle)进行渲染。

Image(systemName: "cloud.rain.fill")
    .foregroundStyle(.blue)
// All layers are blue

适用场景:简单的着色图标、与文本颜色匹配的图标、工具栏项目、标签栏项目。

Hierarchical

使用从单一颜色派生的不同不透明度渲染各图层。主要图层完全不透明;次要和第三级图层的透明度依次增加。无需指定多种颜色即可呈现层次感。

Image(systemName: "cloud.rain.fill")
    .symbolRenderingMode(.hierarchical)
    .foregroundStyle(.blue)
// Cloud is full blue, rain drops are lighter blue

适用场景:希望呈现视觉层次感,同时保持图标与单一色调协调一致时。这是制作精致 UI 时最常见的选择。

Palette

为每个图层指定明确的颜色。与 Hierarchical 不同,它不会自动派生不透明度,你可以直接控制每个图层的颜色。

Image(systemName: "cloud.rain.fill")
    .symbolRenderingMode(.palette)
    .foregroundStyle(.blue, .cyan)
// Cloud is blue, rain drops are cyan

适用场景:品牌图标、颜色具有特定含义的状态指示器,以及需要精确控制颜色的设计。

注意事项:如果提供的颜色数量少于图层数量,多出的图层会重复使用最后一种颜色。如果符号有 3 个图层,而你只提供 2 种颜色,那么第三个图层会使用第二种颜色。

Multicolor

使用 Apple 为每个符号预先定义的配色方案。颜色固定,无法自定义。

Image(systemName: "cloud.rain.fill")
    .symbolRenderingMode(.multicolor)
// Cloud is white, rain drops are blue (Apple's design)

适用场景:天气指示器、文件类型图标,或任何适合采用 Apple 精心设计方案的场合。并非所有符号都支持 Multicolor;不支持的符号会回退到 Monochrome。

常见错误
错误影响修正方法
在 Multicolor 模式下使用 .foregroundColor()覆盖 Apple 的颜色移除前景色修饰符
使用 Palette 时只设置 1 种颜色外观与 Monochrome 相同为每个图层提供颜色
假设所有符号都支持 Multicolor回退到 Monochrome先在 SF Symbols 应用中检查
图层需要表达不同含义时仍使用 Hierarchical颜色无法传达语义改用 Palette

第 2 部分:符号效果系统

符号效果让 SF Symbols 通过动态效果焕发生机。每种效果都属于四种行为类别之一。

效果类别
类别触发条件持续时间使用场景
离散值发生变化单次点击反馈、事件通知
无限isActive 布尔值持续运行,直到停止加载状态、持续进行的流程
过渡视图插入或移除单次按指定样式显示或消失
内容过渡符号切换单次将一个符号替换为另一个符号
不同 UX 目的对应的效果
User tapped something               → Bounce (discrete)
Something changed, draw attention    → Wiggle (discrete, iOS 18+)
Ongoing process/loading              → Pulse, Breathe, or Variable Color (indefinite)
Rotation indicates progress          → Rotate (indefinite, iOS 18+)
Show/hide symbol                     → Appear/Disappear (transition)
Swap between two symbols             → Replace (content transition)
Symbol enters with hand-drawn style  → Draw On (iOS 26+)
Symbol exits with hand-drawn style   → Draw Off (iOS 26+)
Progress indicator along path        → Variable Draw (iOS 26+)
Scale up/down for emphasis           → Scale (indefinite)
离散效果

值发生变化时触发一次。符号执行动画后恢复静止状态。

弹跳

最常见的离散效果。短暂而富有弹性的动画。

@State private var downloadCount = 0

Image(systemName: "arrow.down.circle")
    .symbolEffect(.bounce, value: downloadCount)

每次 downloadCount 发生变化时,都会触发动画。

方向选项:.bounce.up、.bounce.down

摇摆(iOS 18+)

水平摇晃,以引起对符号的注意。

Image(systemName: "bell.fill")
    .symbolEffect(.wiggle, value: notificationCount)

方向选项:.wiggle.left、.wiggle.right、.wiggle.forward、.wiggle.backward

.forward 和 .backward 会遵循阅读方向,适用于支持 RTL 的场景。

旋转(作为离散效果,iOS 18+)

值变化触发时,旋转一次。

Image(systemName: "arrow.trianglehead.2.clockwise")
    .symbolEffect(.rotate, value: refreshCount)

选项:.rotate.clockwise、.rotate.counterClockwise

按图层旋转:某些符号只旋转特定图层(例如,风扇叶片旋转,但外壳保持不动)。使用 .rotate.byLayer 可启用此效果。

无限效果

当 isActive 为 true 时持续运行。当 isActive 变为 false 时停止。

脉冲

轻微的透明度脉冲效果,适用于“等待”状态。

Image(systemName: "network")
    .symbolEffect(.pulse, isActive: isConnecting)
可变颜色

依次突出显示符号的各个图层,营造“逐渐填充”或“循环变化”的效果。

Image(systemName: "wifi")
    .symbolEffect(.variableColor.iterative, isActive: isSearching)

变体:

  • .variableColor.iterative — 一次突出显示一个图层
  • .variableColor.cumulative — 逐步填充图层
  • .variableColor.reversing — 来回循环
  • 组合使用:.variableColor.iterative.reversing
缩放

放大或缩小符号。

Image(systemName: "mic.fill")
    .symbolEffect(.scale.up, isActive: isRecording)
呼吸(iOS 18+)

平滑、有节奏的缩放动画,让符号看起来像在呼吸。

Image(systemName: "heart.fill")
    .symbolEffect(.breathe, isActive: isMonitoring)

变体:.breathe.plain(仅缩放)、.breathe.pulse(缩放 + 透明度)

旋转(作为无限效果,iOS 18+)

持续旋转,适用于处理状态指示器。

Image(systemName: "gear")
    .symbolEffect(.rotate, isActive: isProcessing)
效果选项

所有效果都可通过 options 参数接受 SymbolEffectOptions。

// Repeat 3 times
.symbolEffect(.bounce, options: .repeat(3), value: count)

// Double speed
.symbolEffect(.pulse, options: .speed(2.0), isActive: true)

// Repeat continuously
.symbolEffect(.variableColor, options: .repeat(.continuous), isActive: true)

// Non-repeating (run once)
.symbolEffect(.breathe, options: .nonRepeating, isActive: true)

// Combine options
.symbolEffect(.bounce, options: .repeat(5).speed(1.5), value: count)
过渡效果

用于基于符号的视图在视图层级中出现或消失时。

if showSymbol {
    Image(systemName: "checkmark.circle.fill")
        .transition(.symbolEffect(.appear))
}

可用过渡效果:.appear、.disappear

变体:.appear.up、.appear.down、.disappear.up、.disappear.down

内容过渡

用于为符号之间的切换添加动画。应用于容器,而非符号本身。

@State private var isFavorite = false

Button {
    isFavorite.toggle()
} label: {
    Image(systemName: isFavorite ? "star.fill" : "star")
        .contentTransition(.symbolEffect(.replace))
}

替换变体:

  • .replace.downUp — 旧符号向下移动,新符号向上移动
  • .replace.upUp — 两个符号都向上移动
  • .replace.offUp — 旧符号淡出,新符号向上移动
魔法替换

当两个符号具有相同的结构(例如 star 和 star.fill,或 pause.fill 和 play.fill)时,Replace 会自动执行魔法替换:过渡时变形共享元素,同时过渡不同部分。在 iOS 18+ 中,.replace 的默认行为就是魔法替换。若要显式控制:

// Explicit Magic Replace with fallback
.contentTransition(.symbolEffect(.replace.magic(fallback: .replace.downUp)))

第 3 部分:SF Symbols 7 — 绘制动画(iOS 26+)

绘制动画模拟用笔绘制符号时的自然过程。这是 SF Symbols 7 的标志性新功能。

绘制出现和绘制消失

绘制出现会逐笔“绘制”符号,使其显示出来。 绘制消失会通过“擦除”符号,使其消失。

// Draw On — symbol draws in when isComplete becomes true
Image(systemName: "checkmark.circle")
    .symbolEffect(.drawOn, isActive: isComplete)

// Draw Off — symbol draws out when isHidden becomes true
Image(systemName: "star.fill")
    .symbolEffect(.drawOff, isActive: isHidden)
播放模式

控制多层符号的绘制动画方式:

// By Layer (default) — staggered timing, layers overlap
Image(systemName: "square.and.arrow.up")
    .symbolEffect(.drawOn.byLayer, isActive: showIcon)

// Whole Symbol — all layers draw simultaneously
Image(systemName: "square.and.arrow.up")
    .symbolEffect(.drawOn.wholeSymbol, isActive: showIcon)

// Individually — sequential, each layer completes before next starts
Image(systemName: "square.and.arrow.up")
    .symbolEffect(.drawOn.individually, isActive: showIcon)

各模式的适用场景:

  • 按图层(默认):感觉最自然,适用于大多数符号
  • 整个符号:适用于符号应作为一个整体出现,而不是分部分出现的情况
  • 分别播放:适用于需要分别突出显示每个图层的情况(讲故事、引导入门)
绘制关闭方向

绘制关闭支持控制动画正向还是反向播放:

// Forward (default) — follows the draw path
.symbolEffect(.drawOff.nonReversed, isActive: isHidden)

// Reversed — erases in reverse order of how it was drawn
.symbolEffect(.drawOff.reversed, isActive: isErasing)
可变绘制

可变绘制使用 SymbolVariableValueMode.draw,根据 0.0 到 1.0 的数值部分绘制符号的笔画路径,非常适合进度指示器。

Image(systemName: "thermometer.high", variableValue: temperature)
    .symbolVariableValueMode(.draw) // iOS 26+

与传统的可变颜色(为每个图层设置不透明度)对比:

Image(systemName: "wifi", variableValue: signalStrength)
    .symbolVariableValueMode(.color) // iOS 17+ (default behavior)

限制:一个符号可以同时支持可变颜色和可变绘制,但渲染时只能启用一种模式。设置不受支持的模式不会产生可见效果。

渐变渲染

SF Symbols 7 引入了 SymbolColorRenderingMode,可使用单一源颜色生成渐变填充。

Image(systemName: "star.fill")
    .symbolColorRenderingMode(.gradient) // iOS 26+
    .foregroundStyle(.red)
模式描述
.flat纯色填充(默认)
.gradient从源颜色生成的轴向渐变

渐变适用于所有渲染模式,在较大尺寸下效果最佳。

使用绘制效果实现魔法替换

在某些符号对之间使用 .contentTransition(.symbolEffect(.replace)) 时,系统现在会将退出符号的绘制关闭与进入符号的绘制开启结合起来。如果有共用的外框(例如圆形轮廓),则会保留该外框,同时让内部元素通过绘制动画进行过渡。

// Automatic Draw-enhanced Magic Replace
Image(systemName: isComplete ? "checkmark.circle.fill" : "circle")
    .contentTransition(.symbolEffect(.replace))
自定义符号绘制注释

若要在自定义符号上启用绘制动画,请在 SF Symbols 应用中为路径添加注释:

  1. 打开 SF Symbols 7 中的自定义符号
  2. 选择一个路径图层
  3. 添加引导点以定义绘制方向:
    • 起点(空心圆):绘制开始的位置
    • 终点(实心圆):绘制结束的位置
    • 转角点(菱形):方向发生急剧变化的位置
    • 双向点:启用从中心向外绘制
    • 附着点:连接不参与绘制的装饰元素
  4. 最低要求:每条路径至少有两个引导点(起点和终点)
  5. 使用 SF Symbols 应用中的预览面板进行测试

按住 Option 并拖动引导点,可精确调整位置。使用上下文菜单配置方向和端帽。


第 4 部分:反模式

错误的渲染模式
模式问题修正方法
使用 1 种颜色的调色板等同于单色模式,浪费 API 调用使用单色模式或提供多种颜色
为品牌图标使用多色模式无法自定义 Apple 固定的颜色使用包含品牌颜色的调色板
硬编码 .foregroundColor(.blue)忽略深色模式、动态字体和辅助功能使用带语义颜色的 .foregroundStyle()
为状态指示器使用分层模式图层不具备不同含义使用带语义颜色的调色板
错误的效果选择
模式问题修正方法
为加载状态使用弹跳仅播放一次,无法传达“持续进行中”使用脉冲、呼吸或可变颜色
为轻点反馈使用脉冲效果太弱,无法确认操作使用弹跳
为非机械符号使用连续旋转用于有机形状时显得不自然为有机符号使用呼吸效果
为短暂状态变化使用绘制开启对频繁切换来说过于夸张使用替换或缩放
缺少 iOS 版本检查
// ❌ Crashes on iOS 17
Image(systemName: "bell")
    .symbolEffect(.wiggle, value: count) // Wiggle requires iOS 18+

// ✅ Safe version check
Image(systemName: "bell")
    .modifier(BellEffectModifier(count: count))

struct BellEffectModifier: ViewModifier {
    let count: Int
    func body(content: Content) -> some View {
        if #available(iOS 18, *) {
            content.symbolEffect(.wiggle, value: count)
        } else {
            content.symbolEffect(.bounce, value: count)
        }
    }
}
忽略减少动态效果设置

符号效果会自动遵循辅助功能中的减少动态效果设置,大多数效果会被抑制或简化。但是,如果你使用效果来传达关键信息(而非仅作装饰),请提供替代方案:

// Variable Color conveys WiFi strength — provide text fallback
Image(systemName: "wifi")
    .symbolEffect(.variableColor, isActive: isSearching)
    .accessibilityLabel("Searching for WiFi networks")

不要关闭减少动态效果设置,也不要尝试强制播放效果。系统对此的处理是正确的。

缺少无障碍标签
// ❌ VoiceOver says "star.fill"
Image(systemName: "star.fill")

// ✅ VoiceOver says "Favorite"
Image(systemName: "star.fill")
    .accessibilityLabel("Favorite")

使用 .contentTransition(.symbolEffect(.replace)) 替换符号时,请更新无障碍标签,使其与当前状态匹配:

Image(systemName: isFavorite ? "star.fill" : "star")
    .contentTransition(.symbolEffect(.replace))
    .accessibilityLabel(isFavorite ? "Remove from favorites" : "Add to favorites")

第 5 部分:故障排除

效果未播放

症状:已应用 .symbolEffect() 修饰符,但看不到动画。

  1. 检查 iOS 版本 — Bounce/Pulse/Scale 需要 iOS 17+,Wiggle/Rotate/Breathe 需要 iOS 18+,Draw 需要 iOS 26+
  2. 检查减少动态效果 — 设置 > 辅助功能 > 动态效果 > 减少动态效果。开启后,大多数效果会被抑制
  3. 检查触发类型 — 离散效果需要发生变化的 value:。持续效果需要 isActive: true。过渡效果需要视图实际进入或离开视图层级
  4. 检查符号兼容性 — 并非所有符号都支持所有效果。打开 SF Symbols 应用,选择符号,然后检查 Animation 检查器
  5. 检查是否存在冲突效果 — 同一视图上的多个 .symbolEffect() 修饰符可能会冲突。请使用单个效果,或通过选项组合效果
渲染模式下颜色不正确

症状:符号颜色与预期外观不符。

  1. 检查渲染模式 — 如果设置了 .foregroundStyle,但只看到一种颜色,可能需要 .symbolRenderingMode(.palette) 或 .hierarchical
  2. 检查 .tint 与 .foregroundStyle — 在 UIKit 中,tintColor 会影响 Monochrome 和 Hierarchical。对于 Palette,请使用 UIImage.SymbolConfiguration(paletteColors:)
  3. 检查 Multicolor 支持 — 并非所有符号都有 Multicolor 变体。不支持的符号会回退到 Monochrome
  4. 检查环境 — 父视图中的 .foregroundStyle 可能会覆盖你的渲染模式。请直接在 Image 上应用 .symbolRenderingMode()
自定义符号字重不匹配

症状:自定义符号与文本或其他符号放在一起时显得太细或太粗。

  1. 检查模板字重 — 自定义符号需要与 9 种 SF Pro 字重匹配的字重变体。通过 SF Symbols 应用导出时会处理这一点
  2. 检查 .font() 对齐 — 符号的字重遵循应用的字体字重。如果使用 .font(.title),请确保自定义符号具有合适的字重变体
  3. 检查缩放 — .imageScale(.small/.medium/.large) 会影响整体大小。请使用 .font() 匹配字重
自定义符号的 Draw 动画不起作用

症状:自定义符号应用了 .symbolEffect(.drawOn),但没有播放绘制动画。

  1. 检查引导点 — 自定义符号需要添加 Draw 注释,每条路径至少包含 2 个引导点(起点 + 终点)
  2. 检查 SF Symbols 应用版本 — Draw 注释需要 SF Symbols 7+
  3. 检查路径结构 — 引导点必须放置在描边路径上,而不是填充区域上。需要绘制动画的地方,请将填充区域转换为描边
  4. 检查图层结构 — 每个可添加注释的图层都需要自己的引导点

第 6 部分:压力场景

场景 1:“直接用静态图片就行,符号没必要”

背景:设计师提供了 PNG 图标。开发者考虑用它们替代 SF Symbols。

重要原因:静态 PNG 无法适配动态字体、粗体文本、深色模式或无障碍设置,也不支持符号效果。

专业回应:“SF Symbols 会随文本缩放,支持 9 种字重,能自动适配深色模式和粗体文本,还无需自定义代码即可实现动画。PNG 则需要 @1x/@2x/@3x 变体、手动处理深色模式、手动缩放以适配动态字体,以及自定义动画代码。花 10 分钟找到合适的 SF Symbol,可以省下数小时的资源管理时间。”

跳过的时间成本:管理资源需 2–4 小时,外加持续维护;而找到合适的符号只需 10 分钟。

场景 2:“动画以后再加”

背景:冲刺周期即将结束。PM 认为动画只是锦上添花,可以稍后再做。

原因:事后补加符号效果需要重构状态管理。由 value: 变化触发的效果需要从一开始就采用合适的状态架构。

专业回应:“添加 .symbolEffect(.bounce, value: count) 只需一行。之后再重构状态以支持它,就得进行重构。我现在就加上这个效果——真的只需添加一个修饰符。”

场景 3:“绘制动画在我们的自定义符号上看起来很别扭”

背景:自定义符号的绘制动画效果不佳——路径的绘制顺序或方向出乎意料。

原因:绘制注释需要有意放置引导点。没有引导点时,系统只能猜测,而且经常猜错。

修复方法:在 SF Symbols 7 应用中打开自定义符号,为每条路径明确定义起点、终点和方向,并添加引导点。测试每种字重变体。请参阅上文的“自定义符号绘制注释”部分。


资源

WWDC:2023-10257、2023-10258、2024-10188、2025-337

文档:/symbols、/symbols/symboleffect、/symbols/symbolrenderingmode、/swiftui/image/symboleffect(:options:value:)、/swiftui/image/symbolrenderingmode(:)

技能:axiom-sf-symbols-ref、axiom-hig-ref、axiom-swiftui-animation-ref


最后更新:基于 WWDC 2023/10257-10258、WWDC 2024/10188、WWDC 2025/337 版本:iOS 17+(效果)、iOS 18+(Wiggle/Rotate/Breathe)、iOS 26+(Draw On/Off、Variable Draw、Gradients)

评论

支持 Markdown