WWDC Quick Look 💓 By SwiftGGTeam
SF Symbols in UIKit and AppKit

SF Symbols in UIKit and AppKit

观看原视频

Highlight

SF Symbols 3 为 UIKit 和 AppKit 增加分层、调色板、多色渲染配置,开发者可以用系统符号完成可缩放、适配深色模式、可嵌入文本的彩色图标。

核心内容

过去在 UIKit 和 AppKit 里使用 SF Symbols,最常见的做法是单色符号加 tintColor。播放按钮、导航栏图标、设置页图标都能这样做。问题出现在更复杂的界面里:一个语音邮件操作按钮要同时有白色图标和红色背景,一个健康分类图标要保留系统定义的多色外观,一个酒店设施标签要把图标放进一行文字。

这些场景如果继续用单色符号,开发者经常要换成位图资源,或者手工维护多个符号名称。位图失去 SF Symbols 的尺寸、字重、动态类型和系统外观适配。手工拼接 .circle.fill 这样的名称也容易让代码和设计稿脱节。

Apple 在 SF Symbols 3 里把符号拆成 primary、secondary、tertiary 三层。UIKit 和 AppKit 新增 SymbolConfiguration 颜色配置,让每一层可以按规则上色。分层模式从一个颜色派生透明度,调色板模式显式指定多种颜色,多色模式使用符号内置颜色。

这件事直接解决了界面里的两个麻烦。第一,按钮、列表、控制中心这类组件可以继续使用系统符号,并获得彩色视觉。第二,同一套配置可以和尺寸、字重、文本附件组合,符号在图片视图、按钮和富文本里保持一致。

从单色到分层颜色

单色符号仍然是默认行为。代码只需要创建 UIImageNSImage,再给承载视图设置 tint 或 accent color。它适合播放、返回、关闭这类只有一个视觉层级的图标。

分层颜色用于需要深浅层次的符号。开发者只提供一个主色,系统根据符号图层生成较低透明度的次级颜色。Control Center 里的设备图标就是这个场景:它们需要统一色系,也需要让主体和细节有层次。

调色板让按钮在深色模式下保持设计意图

语音邮件界面里的三个操作按钮需要不同颜色:扬声器按钮使用主题色和灰色,拨号按钮使用白色和主题色,删除按钮使用白色和红色。如果只用单色符号,浅色模式可能看起来正确,切到深色模式时,符号内部的镂空会露出背景色,白色图标的设计意图会丢失。

调色板模式用显式颜色填充符号图层。开发者可以把内部图层固定为白色,把背景或辅助图层绑定到 .tintColor.systemRed。这样,界面样式变化时,按钮仍然按设计稿显示。

多色符号保留系统定义的颜色

有些符号本身已经带有颜色,例如健康、天气或类别图标。UIKit 在 iOS 15 支持多色符号。开发者通过配置表达“优先使用多色版本”,系统会在符号支持时使用内置颜色。

并非所有符号都支持多色。SF Symbols app 的检查器可以查看一个符号支持哪些渲染模式。代码里也可以设置 tint color 作为回退,让不支持多色的符号仍然有合适颜色。

详细内容

单色符号:默认行为仍然有效

01:52)单色模式只有一种颜色,通常来自 image view 的 tint 或 accent color。它是默认渲染模式,不需要额外 SymbolConfiguration

let playImage = UIImage(systemName: "play")

playImageView.image = playImage
playImageView.tintColor = .systemBlue

关键点:

  • UIImage(systemName:) 从系统符号库创建 play 图标。
  • playImageView.image 把符号放进图片视图。
  • playImageView.tintColor 指定单色符号的颜色。
  • 代码没有创建颜色配置,因为单色是默认模式。

分层符号:用一个颜色生成层次

03:00)分层模式使用符号的图层层级。传入的颜色用于 primary layer,secondary 和 tertiary layer 使用降低透明度后的派生颜色。

var image = NSImage(systemSymbolName: "ipad.landscape",
                    accessibilityDescription: "iPad")

let config = NSImage.SymbolConfiguration(hierarchicalColor: .label)

deviceView.image = image
deviceView.symbolConfiguration = config

关键点:

  • NSImage(systemSymbolName:accessibilityDescription:) 创建 AppKit 符号图片,并提供无障碍描述。
  • NSImage.SymbolConfiguration(hierarchicalColor:) 请求分层颜色渲染。
  • .label 作为主色,会随系统文本颜色动态变化。
  • deviceView.symbolConfiguration 把配置应用到图片视图。
  • 如果符号缺少某个层级,对应派生颜色不会被使用。

图像变体:在容器上统一请求形状

04:13)按钮先用基础符号初始化。这里使用 iOS 15 的按钮配置 API,把每个按钮的图片放到配置里。

let speakerConfig = UIButtonConfiguration.plain
speakerConfig.image = UIImage(systemName: "speaker.wave.2")

let callConfig = UIButtonConfiguration.plain
callConfig.image = UIImage(systemName: "phone")

let deleteConfig = UIButtonConfiguration.plain
deleteConfig.image = UIImage(systemName: "trash")

关键点:

  • UIButtonConfiguration.plain 创建无背景按钮配置。
  • speaker.wave.2phonetrash 是三个基础符号名称。
  • 每个配置先只指定图片,颜色稍后用符号配置处理。

04:44)图像变体(image variants)可以在容器视图上设置。变体会沿视图层级向下传播,内部 image view 会拿到这个请求。

actionsView.imageVariant = .circle
actionsView.imageVariant = .circle.fill

关键点:

  • .circle 请求圆形变体,不需要把 .circle 拼到符号名称里。
  • .circle.fill 请求填充圆形变体。
  • 配置放在 actionsView 上,会影响容器里的多个按钮图片。
  • 如果某个符号没有该变体,系统会使用原始图片。

调色板符号:显式控制每一层颜色

05:09)调色板模式接收一个颜色数组。颜色会应用到符号图层层级。这里的扬声器按钮使用视图 tint color 和系统灰色。

let config = UIImage.SymbolConfiguration(paletteColors: [.tintColor, .systemGray2])

speakerConfig.preferredSymbolConfigurationForImage = config
speakerButton.configuration = speakerConfig

关键点:

  • UIImage.SymbolConfiguration(paletteColors:) 创建调色板配置。
  • .tintColor 是 UIKit 动态颜色,会解析为当前视图的 tint color。
  • .systemGray2 为另一个图层提供固定的系统灰色。
  • preferredSymbolConfigurationForImage 把符号配置挂到按钮图片上。
  • speakerButton.configuration 应用完整按钮配置。

05:40)拨号按钮需要白色符号和主题色背景。调色板模式可以把白色写进配置,避免深色模式下镂空区域透出背景。

let config = UIImage.SymbolConfiguration(paletteColors: [.white, .tintColor])

callConfig.preferredSymbolConfigurationForImage = config
callButton.configuration = callConfig

关键点:

  • .white 明确指定一个图层使用白色。
  • .tintColor 让另一个图层跟随按钮所在视图的主题色。
  • 配置绑定在 callConfig 上,最后赋给 callButton

05:56)删除按钮把第二个颜色换成系统红色。危险操作的颜色不依赖视图 tint color。

let config = UIImage.SymbolConfiguration(paletteColors: [.white, .systemRed])

deleteConfig.preferredSymbolConfigurationForImage = config
deleteButton.configuration = deleteConfig

关键点:

  • .white 保证内部图标区域保持白色。
  • .systemRed 给删除操作提供系统红色。
  • 同一套按钮配置流程可以复用到不同操作。

动态 tint color:颜色可以跟随视图解析

06:46.tintColor 是 UIKit 的动态颜色。它可以用于符号图层,也可以用于普通界面属性。

view.backgroundColor = .tintColor
label.textColor = .tintColor
searchField.tokenBackgroundColor = .tintColor
tabBarItem.badgeColor = .tintColor

关键点:

  • view.backgroundColor 可以直接使用动态 tint color。
  • label.textColor 会解析为标签所在环境的 tint color。
  • searchField.tokenBackgroundColortabBarItem.badgeColor 也能使用同一个动态颜色。
  • 动态颜色仍然遵守 UIKit 的解析规则。

多色符号:优先使用符号内置颜色

09:03)如果直接创建系统图片并赋给 cell,得到的是默认单色渲染。

let image = UIImage(systemName: category.iconName)

cell.imageView.image = image

关键点:

  • category.iconName 保存类别对应的 SF Symbol 名称。
  • UIImage(systemName:) 只负责创建符号图片。
  • 没有设置渲染配置时,UIKit 使用默认单色模式。

09:13)要请求多色版本,需要创建 preferringMultiColor 配置并赋给 image view。对于不支持多色的符号,tint color 仍然提供回退颜色。

let image = UIImage(systemName: category.iconName)

let config = UIImage.SymbolConfiguration.preferringMultiColor

let tintColor = category.colorForIcon

cell.imageView.image = image
cell.imageView.preferredSymbolConfiguration = config
cell.imageView.tintColor = tintColor

关键点:

  • UIImage.SymbolConfiguration.preferringMultiColor 请求优先使用多色渲染。
  • category.colorForIcon 给当前类别提供回退 tint color。
  • preferredSymbolConfiguration 把多色偏好应用到 image view。
  • tintColor 会影响单色回退,也会影响带 tint layer 的多色符号。
  • 符号是否支持多色,可以在 SF Symbols app 的检查器里查看。

组合配置:把尺寸和颜色合成一个配置

12:40)一个 image view 只能应用一个符号配置。要同时控制尺寸和颜色,需要先创建多个配置,再用 applying 合并。

let image = UIImage(systemImage: "ipad.and.iphone")
headerView.image = image

let fontConfig = UIImage.SymbolConfiguration(pointSize: 60, scale: .large)
let colorConfig = UIImage.SymbolConfiguration(hierarchicalColor: .systemBlue)
let config = fontConfig.applying(colorConfig)

headerView.preferredSymbolConfiguration = config

关键点:

  • UIImage(systemImage:) 创建 ipad.and.iphone 符号图片。
  • fontConfig 指定 60 点大小和 large scale。
  • colorConfig 指定分层颜色的主色为系统蓝色。
  • fontConfig.applying(colorConfig) 生成同时包含尺寸和颜色的新配置。
  • headerView.preferredSymbolConfiguration 应用最终配置。

富文本符号:把彩色符号嵌入 Attributed String

13:20)彩色符号可以作为文本附件放进 NSAttributedString。酒店房间设施列表就是典型场景:电视图标和文字需要在同一行排版。

let amenitiesString = NSMutableAttributedString(...)

if (room.amenities.contains(.tv)) {
    let config = UIImage.SymbolConfiguration(
                         hierarchicalColor: .systemGreen)
    let tvImage = UIImage(systemImage: "tv",
                          withConfiguration: config)

    let attachment = NSTextAttachment(image: tvImage)
    let attachmentString = NSAttributedString(attachment:
                                               attachment)
    let tvString = attachmentString.mutableCopy()
    tvString.append(NSAttributedString(" TV, "))

    amenitiesString.append(tvString)
}

关键点:

  • NSMutableAttributedString(...) 保存最终要显示的富文本。
  • room.amenities.contains(.tv) 判断房间是否包含电视设施。
  • UIImage.SymbolConfiguration(hierarchicalColor:) 给电视符号指定绿色分层渲染。
  • UIImage(systemImage:withConfiguration:) 创建带配置的符号图片。
  • NSTextAttachment(image:) 把图片变成文本附件。
  • NSAttributedString(attachment:) 把附件包装成富文本片段。
  • tvString.append 在图标后追加文字。
  • amenitiesString.append 把这一段加入完整设施列表。

13:51)显示富文本的标签仍然要设置字体和文字颜色。单色符号会自动使用文本颜色;带颜色配置的符号需要在配置中显式指定颜色。

let amenitiesLabel = UILabel()

amenitiesLabel.textColor = .systemGreen
amenitiesLabel.font = UIFont.systemFont(ofSize: 25)

amenitiesLabel.attributedString = amenitiesString

关键点:

  • UILabel() 创建展示设施文本的标签。
  • textColor 让文字部分使用系统绿色。
  • font 设置 25 点系统字体,符号附件会跟随字符串里的字体尺寸排版。
  • attributedString 把前面构造的富文本交给标签显示。

核心启发

  1. 做什么:给设置页的每一项增加状态化 SF Symbol。 为什么值得做:分层模式可以用一个主题色表达启用状态,调色板模式可以用白色加红色表达危险操作。 怎么开始:在 cell 的 image view 上设置 preferredSymbolConfiguration,普通项用 UIImage.SymbolConfiguration(hierarchicalColor:),删除项用 UIImage.SymbolConfiguration(paletteColors:)

  2. 做什么:把通话、语音、删除这类操作按钮改成圆形填充符号。 为什么值得做:图像变体可以在容器层统一请求 .circle.fill,代码不需要手工维护多个符号名称。 怎么开始:在按钮容器上设置 imageVariant,再给每个 UIButtonConfiguration 配置不同的 preferredSymbolConfigurationForImage

  3. 做什么:为分类列表加入系统多色图标和单色回退。 为什么值得做:多色符号能保留 Apple 为符号设计的颜色;不支持多色的符号仍然可以通过 tint color 保持类别色。 怎么开始:创建 UIImage.SymbolConfiguration.preferringMultiColor,设置到 cell.imageView.preferredSymbolConfiguration,并同步设置 cell.imageView.tintColor

  4. 做什么:在酒店、购物、健康记录等文本说明里嵌入彩色设施图标。 为什么值得做NSTextAttachment 能把 SF Symbol 放进富文本,符号会跟随文字尺寸排版。 怎么开始:用带 hierarchicalColorUIImage 创建 NSTextAttachment,再把附件包装成 NSAttributedString 追加到 NSMutableAttributedString

  5. 做什么:为大尺寸空状态页或页头制作分层插图式图标。 为什么值得做applying 可以把 point size、scale 和颜色合成一个配置,图标在不同尺寸下仍然使用 SF Symbols。 怎么开始:先创建 UIImage.SymbolConfiguration(pointSize:scale:),再创建 UIImage.SymbolConfiguration(hierarchicalColor:),用 fontConfig.applying(colorConfig) 合并后赋给 image view。

关联 Session

评论

GitHub Issues · utterances