SF Symbols
SF Symbols是苹果在19年推出的矢量图标库,SF代表San Francisco,是苹果官方系统字体名称。经过多个版本的持续迭代,目前已经囊括了20多个分类共计6000多个图标。苹果也推出了一个叫SF Symbols的App,方便使用者预览和选择符合要求的图标(注意:图标并不是支持所有的iOS系统版本,所以需要注意所选择的图标支持的版本,如果选择了只有高版本才有的图标,在低版本上不能正常显示)。
本文主要是想介绍下SF Symbols在Swift UI里的应用。和加载我们自己添加到Assets里的图片类似,系统为Image提供了一个带systemName的重载,如下所示就可以展示一个彩虹图标了。
swift 代码解读复制代码Image(systemName: "rainbow")
如名字所示,SF Symbols可以很完美的和文字结合在一起,具有和文字一样的行为,比如调整字体大小,粗细,颜色,很方便地和文字对齐等等,
在SwiftUI里,可以直接通过font
,foregroundStyle
等modifier来调整SF Symbols的样式
swift 代码解读复制代码Image(systemName: "progress.indicator")
.font(Font.system(size: 20, weight: .bold))
.foregroundStyle(.cyan)
Render Mode
SF Symbols图标在设计时本身是分图层设计的,不同的图层可以有不一样的颜色或者不同的视觉深度以突出某些部分。所以从iOS 15起,func symbolRenderingMode(_ mode: SymbolRenderingMode=?) -> Image
方法来为SF Symbols提供不同的展示形式。SymbolRenderingMode主要有以下四种形式:
- 单色模式(monochrome)
这个模式比较好理解,所有的图层都会使用相同的颜色来渲染,使用者可以通过foregroundStyle
来改变颜色
swift 代码解读复制代码Image(systemName: "cloud.sun.rain.fill")
.symbolRenderingMode(.monochrome)
.foregroundStyle(.yellow)
- 调色板模式(palette)
采用这种模式可以让图标的不同图层按不同的颜色来绘制。SF Symbols的图标最多包含三个图层,以上面的cloud.sun.rain.fill为例, 云朵是第一层(primary layer), 太阳和光线部分是第二层(secondary layer), 雨滴是第三层(tertiary layer)。如果我们想快速预览最终的样式,也可以去SF Symbols App里选择某个图标,并设置不同的渲染模式,以及颜色,快速查看最终的样式。
在SwiftUI里,使用者可以通过foregroundStyle
的几个重载版本来实现。
swift 代码解读复制代码Image(systemName: "cloud.sun.rain.fill")
.symbolRenderingMode(.palette)
.foregroundStyle(.yellow, .blue, .red)
在使用这个方法时,需要设置的颜色数量和图层数量不一致的情况
- 图层数量比颜色数量多:多出来的图层会用颜色列表里的最后一个颜色参数来渲染
- 颜色数量比图层数量多:多出来的颜色参数会被忽略掉
- 分层模式(hierarchical)
采用这种模式只能设置一种颜色,多余的颜色会被自动忽略掉。但是系统会在不同的图层上应用不同的透明度,从而让第一层更突出
swift 代码解读复制代码Image(systemName: "cloud.sun.rain.fill")
.symbolRenderingMode(.hierarchical)
.foregroundStyle(.yellow)
- 多色模式(multicolor)
这个模式可能是会让使用者产生误解的模式。苹果认为,图标代表的某些概念应该有特定的颜色,比如彩虹就应该是七彩色,删除应该是红色,添加应该是绿色,当使用者用多色模式时,图标里代表这些概念的图层会呈现为他们应该具有的颜色。
从上图可以看出,选择了多色模式后,有些图标的图层不会受前景色设置的影响,而有的图标则会受影响。所以在实际使用时,最好先去SF Symbol App预览下最终的效果。
swift 代码解读复制代码Image(systemName: "cloud.sun.rain.fill")
.symbolRenderingMode(.multicolor)
Variable Color
从iOS 16起,SF Symbols提供了可变颜色(Variable Color)的特性,通过额外的一个从值从0到1的参数来使图标可以表示不同的阶段或状态
在SwiftUI里,使用者可以通过在初始化Image传递额外的variableValue参数来实现
swift 代码解读复制代码HStack{
Image(systemName: "chart.bar.fill", variableValue: 0.3)
Image(systemName: "chart.bar.fill", variableValue: 0.6)
Image(systemName: "chart.bar.fill", variableValue: 1.0)
}
以上代码分别对应下图中三种不同的状态。实际使用场景中,使用者可以用来表示网络强弱等等
Design Variants
可能大家在浏览图标的时候会发现有些图标会有实心的,带斜线的等不同的变体。系统主要提供了以下几种变体:
类型 | 对比 |
---|---|
SymbolVariants.fill | |
SymbolVariants.circle | |
SymbolVariants.slash | |
SymbolVariants.rectangle | |
SymbolVariants.square |
在SwiftUI里,使用者可以通过symbolVariant
modifier来应用想要的变体。可以在一次调用中传递多个变体.symbolVariant(.circle.slash)
也可以分成多次调用.symbolVariant(.circle).symbolVariant(.slash)
。如果所选图标不支持某种变体,则该图标会保持不变
Animation
从iOS 17开始,系统为SF Symbols引入了动画效果,使用者也可以在SF Symbols App里预览动画的效果,比如
系统提供了常见的一些动画效果:
- pulse
- bounce
- variableColor
- scale
- replace
- appear
- disappear
- wiggle(iOS 18)
- rotate(iOS 18)
- breathe(iOS 18)
每种动效会对应一个具体的effect结构体,以bounce为例:
swift 代码解读复制代码public struct BounceSymbolEffect : SymbolEffect {
/// Returns a copy of the effect requesting an animation that
/// bounces upwards.
public var up: BounceSymbolEffect { get }
/// Returns a copy of the effect requesting an animation that
/// bounces downwards.
public var down: BounceSymbolEffect { get }
/// Returns a copy of the effect requesting an animation that
/// applies separately to each motion group.
public var byLayer: BounceSymbolEffect { get }
/// Returns a copy of the effect requesting an animation that
/// applies to all motion groups simultaneously.
public var wholeSymbol: BounceSymbolEffect { get }
}
其他的动画效果也有类似的属性,如果不太清楚这些属性的作用,可以去SF Symbols App体验下就大体明白了
当使用者选择了某种动画效果后,效果的配置都会在下方展示出来。通过预览能够知道up是放大的效果,down是缩小的效果,byLayer则是图标不同层会有分开动的效果,wholeSymbol是整个图标一起动的效果。
如果仔细观察会发现up, down等的类型也是BounceSymbolEffect,所以效果是可以通过链式调用来进行组合的,比如.bounce.down.byLayer(在SF Symbols App预览完动效点击右侧的复制按钮可以直接复制动效代码)。
swift 代码解读复制代码Image(systemName: "heart.fill")
.symbolEffect(.bounce.down.byLayer)
在SwiftUI里实际应用SF Symbols的动效时会比上面的代码稍微麻烦一些。这需要从动画的分类说起。
系统将动效分成了四个大类:
- 离散动效:有开始和结束的动效,比如图标的一次呼吸动画等
- 无限动效:动效一直运行,直到被手动停止
- 转场动效:图标在显示或隐藏时的动效
- 内容转场动效:图标在两种状态之间切换的动效
前面介绍的动效里很多都是可以同时归属于多个分类的,比如一次呼吸动效是离散动效,但是一直循环的则是无限动效。这个从动效的代码定义上也可以看到:
swift 代码解读复制代码@available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, visionOS 1.0, *)
extension PulseSymbolEffect : IndefiniteSymbolEffect {
}
@available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, visionOS 1.0, *)
extension PulseSymbolEffect : DiscreteSymbolEffect {
}
在SwiftUI使用这些动效时,上面四个大类使用起来也有些区别:
- 离散动效
swift 代码解读复制代码extension: View {
public func symbolEffect<T, U>(_ effect: T, options: SymbolEffectOptions = .default, value: U) -> some View where T : DiscreteSymbolEffect, T : SymbolEffect, U : Equatable
}
要为图标添加一个离散动效,可以像下面这样:
swift 代码解读复制代码struct DiscreteEffect: View {
@State private var isAnimating: Bool = false
var body: some View {
Image(systemName: "heart")
.symbolVariant(.fill)
.font(.largeTitle)
.symbolEffect(.pulse, value: isAnimating)
Button("Toggle Animation") {
isAnimating.toggle()
}
.buttonStyle(.bordered)
}
}
- 无限动效
swift 代码解读复制代码extension: View
public func symbolEffect<T>(_ effect: T, options: SymbolEffectOptions = .default, isActive: Bool = true) -> some View where T : IndefiniteSymbolEffect, T : SymbolEffect
}
要为图标添加一个无限动效,可以像下面这样:
swift 代码解读复制代码struct IndefiniteEffect: View {
@State private var isActive: Bool = false
var body: some View {
Image(systemName: "heart")
.symbolVariant(.fill)
.font(.largeTitle)
.symbolEffect(.pulse, isActive: isActive)
Button("Toggle Animation") {
isActive.toggle()
}
.buttonStyle(.bordered)
}
}
- 转场动效
swift 代码解读复制代码extension Transition {
public static func symbolEffect<T>(_ effect: T, options: SymbolEffectOptions = .default) -> SymbolEffectTransition where T : SymbolEffect, T : TransitionSymbolEffect
}
转场动效主要是appear和disappear,要实现图标的转场动效可以像下面这样:
swift 代码解读复制代码struct IndefiniteEffect: View {
@State private var isActive: Bool = false
var body: some View {
if isActive {
Image(systemName: "heart")
.symbolVariant(.fill)
.font(.largeTitle)
.transition(.symbolEffect(.appear))
}
Button("Toggle Animation") {
isActive.toggle()
}
.buttonStyle(.bordered)
}
}
在这里有个细节需要注意,appear和disappear也属于无限动效,所以也可以用无限动效的实现方式加到图标上,两种方式的区别在于离散动效不会移除图标,只是隐藏/展示,不影响布局,而转场动效会真的移除/添加图标,会影响布局。可以看到的时候上面例子中,按钮的位置会上下移动。
- 内容转场动效
swift 代码解读复制代码public static func symbolEffect<T>(_ effect: T, options: SymbolEffectOptions = .default) -> ContentTransition where T : ContentTransitionSymbolEffect, T : SymbolEffect
这个现在就是指replace动效了,想实现replace效果,可以像下面这样:
swift 代码解读复制代码struct ContentTransitionEffect: View {
@State private var isOn: Bool = false
var body: some View {
Image(systemName: isOn ? "lightswitch.on" : "lightswitch.off")
.symbolVariant(.fill)
.font(.largeTitle)
.contentTransition(.symbolEffect(.replace))
Button("Toggle Animation") {
isOn.toggle()
}
.buttonStyle(.bordered)
}
}
总结
SF Symbols提供了大量的图标,可以和文字完美的融合在一起。每个图标都有多种标准化的变体,多种渲染模式,可以满足不同的设计场景。另外图标还支持了常见的动画效果,对于提升交互体验有一定的帮助。总的来说,SF Symbols还是很不错的一个工具。
评论记录:
回复评论: