大多数浏览器和
Developer App 均支持流媒体播放。
-
在 App 中为符号添加动画效果
使用动画符号为你的 App 增添趣味。探索如何使用具有统一 API 的新符号框架来创建和配置符号效果。了解如何使用 SwiftUI、AppKit 和 UIKit 在用户界面中轻松实现符号动画。探索无缝集成新动画和其他 App 内容的技巧和诀窍。为了充分理解本节内容,建议你先观看讲座“SF Symbols 5 中的新功能”。
资源
相关视频
WWDC23
-
下载
♪ ♪
Anant:大家好 欢迎观看课程 “在 App 中为符号添加动画” 我是 Anant 是一名 UIKit 工程师 SF Symbols 是 Apple 界面的一个标志性部分
菜单、工具栏 和边栏中的符号都很美观 因为用户熟悉符号 所以符号可以 直观地表现 App 的功能 在 iOS 17 和 macOS Sonoma 中 我们用动画来增强符号效果 为 App 注入前所未有的活力 我会先介绍新的符号动画 也叫“符号效果” 然后 我会为你介绍 如何使用 SwiftUI、 UIKit 和 AppKit 中 新的 API 来为 App 添加符号效果 最后 我会提供一些 能给符号效果锦上添花的小技巧
我们开始吧
iOS 17 和 macOS Sonoma 引入了一系列通用动画 可以应用到任何符号图像 包括自定义符号 这些动画包括 弹跳、脉冲、可变颜色、 缩放、出现、消失和替换
建议你观看课程 “SF Symbols 5 的新功能” 深入了解有关动画的内容 包括使用动画设计界面的最佳实践
在 API 中 我们称这些动画为“符号效果” 而新的 Symbols 框架 是动画的主要组成部分 当你使用 SwiftUI、AppKit 或 UIKit 构建 App 时 可免费使用 Symbols 框架 Symbols 框架有一个很酷的功能 那就是每个效果 都有一个简单的以点分隔的名称 因此 如果要创建一个弹跳效果 只需在代码中 简单地写上“.bounce”
这种以点分隔的命名方式 也适用于效果的配置方式 例如 可以指定符号的弹跳方向 是向上还是向下 但大多数情况下 你不需要指定任何内容 框架将自动选择最合适的方向 某些效果具有多个配置选项 例如 可变颜色效果有三种不同的设置 通过链接配置选项 你可以轻松配置特定的效果 这些效果名称 是真实的 Swift 代码 没有附带的字符串 Xcode 会自动补全 名称的每个部分 并且如果效果配置错误 你在编译时会收到错误消息 使用 SF Symbols App 是探索所有新动画的最佳方法 在新动画的标签页中 你可以了解 每个效果的所有可用配置选项 你甚至可以复制 以点分隔的效果名称 直接在代码中使用 将不同的效果类型 和配置选项相结合后 可以生成各种动画效果 所有这些效果 其实都包含一小组行为
比如 弹跳效果 是在符号上播放一次动画 这是离散行为 另一方面 添加一个 缩放效果会改变符号的大小 并会无限保持这种状态 缩放效果可支持无限行为 与离散效果不同 无限效果只有在 明确移除时才会结束
出现效果和消失效果支持过渡行为 这两种效果可让符号过渡进出视图
最后 替换效果是一种内容过渡 会从一种符号变成另一种符号
这 4 种不同的行为是: 离散、无限、过渡以及内容过渡 在 Symbols 框架中 每种行为都对应一种协议 效果通过遵守这些协议 来声明它们所支持的行为
下面是所有可用效果的分类 以及它们支持的行为 我会在本课程中详细介绍 请注意 效果的行为 决定了可以与哪些 UI 框架的 API 一起使用 说到 UI 框架的 API 我们来看看如何在 SwiftUI、 UIKit 和 AppKit App 中 添加这些炫酷效果
在 SwiftUI 中 有个新的 视图修饰符:symbolEffect
只需添加修饰符 并传入所需的效果即可 这里 我传入了 variableColor 此符号现在正播放 默认的可变颜色动画
在 AppKit 和 UIKit 中 也可以轻松实现 只需在图像视图上使用新的 addSymbolEffect 方法 来添加可变颜色效果即可 我可以使用点语法 来配置可变颜色效果 在这里 我将效果更改为 variableColor.iterative.reversing 得到了不同的可变颜色动画 它生动表现了 我的 App 正在连接网络 甚至还可以组合不同的效果 在这里 我添加了 scale.up 效果 现在 符号同时在进行可变颜色动画 和缩放
这些 API 提供了一种简单的方式 用于对符号图像添加无限效果 刚才介绍过 无限效果会无限期地改变符号 直到移除效果
因此 借助 symbolEffect 修饰符 我就可以应用一个可变颜色效果 它会持续播放动画 但我还需要一种控制效果的方法 当 App 成功连接到网络后 我希望该动画停止播放
可以通过添加布尔型的 isActive 参数来实现控制 在这里 我只在 App 正在连接互联网时应用该效果 App 完成连接后 符号动画会同时结束
在 AppKit 和 UIKit 中 使用 removeSymbolEffect 方法 可终止无限效果 那要如何终止负责执行 一次性动画的离散效果呢? 我之前提到了弹跳的例子 App 可能会触发 反弹效果来响应某些事件
在 SwiftUI 中 可以使用相同的 symbolEffect 修饰符 来添加离散效果 但我还必须 向 SwiftUI 提供一个值 每当该值发生变化时 SwiftUI 就会触发离散效果
我们来添加一个按钮 按下它时 符号就会弹跳 按钮的处理程序只需要 增加 bounceValue SwiftUI 会检测到 bounceValue 的变化 并触发弹跳效果 在 AppKit 和 UIKit 中 可以通过向图像视图 添加弹跳效果来实现该触发 因为弹跳只支持离散行为 所以 添加效果只会执行一次弹跳 我们不需要之后移除效果
现在 假设我不希望符号只弹跳一次 想要它弹跳两次 该怎么办? SwiftUI、AppKit 和 UIKit 支持 options 参数 我可以在其中指定重复次数 现在 触发该效果时 符号会弹跳两次 弹跳并不是 唯一具有离散行为的效果 我开始时介绍的两个效果: 脉冲和可变颜色 支持无限行为 也支持离散行为 换句话说 这两个效果 可以播放一次性的动画 就像弹跳效果一样 所以 我可以将之前的弹跳示例 改为 variableColor 可变颜色效果 会切换为使用离散行为 因为它是以非重复的方式应用的
现在 按下该按钮 会执行两个可变颜色循环
接下来 我们来了解一下内容过渡效果 替换效果就是一个主要示例 它可以在两个不同的 符号图像之间进行动画切换 这里有一个图像 可在暂停符号和播放符号之间切换
SwiftUI 有个新的 contentTransition 类型 叫做 symbolEffect 可以与替换效果一起使用 如果将图片放在一个按钮内 且该按钮会切换显示的符号 那么 变化现在就有了动画效果 在 AppKit 和 UIKit 中 可以使用新的 setSymbolImage 方法 通过符号内容过渡来变换图像
最后 介绍一下 出现效果和消失效果 二者使用独特的动画效果 来显示和隐藏符号 只有这两种效果被归为过渡效果 在深入讨论这个之前 需要先介绍一下平行空间 不过 不用担心 平行空间并没有看起来那么复杂 在一个空间中 虽然图像消失了 但图像视图仍然在层次结构中 也就是说 布局没有变化 正方形和圆形之间的距离保持不变 而在另一个平行空间中 层次结构中图像视图的 添加或删除是真实的 周围视图的布局会因此发生变化
更棒的是 出现和消失效果都支持这两种行为
第一种行为可以实现 是因为 出现和消失都属于无限效果
你已经知道如何使用无限效果了 在 SwiftUI 中 使用 .symbolEffect 修饰符 并传入 .disappear 当 isMoonHidden 的值 更新时 就会应用消失效果
在 AppKit 和 UIKit 中 使用 addSymbolEffect 并传入 .disappear 或 .appear
要注意的是 无限效果 并不会改变布局 只会改变图像视图中 符号的呈现方式
这就是刚刚说的第一种行为 那么 要如何切换到 周围布局发生变化的平行空间呢?
这就需要用到过渡行为 过渡效果可以 与 SwiftUI 内置的 过渡修饰符一起使用 可以对视图层次结构中 视图的插入或移除进行动画处理
我们来把之前的代码 转换为使用过渡行为 我不用根据条件应用消失效果 而是根据条件 在视图层次结构中添加符号
然后 我会添加一个过渡修饰符 SwiftUI 中有一个新的过渡类型 没错 就是 symbolEffect 通过传入 .disappear 可以对符号进行 添加和移除的动画处理
你还可以使用一种独特的 过渡效果 叫自动化 此效果将自动执行 最合适该符号的过渡动画
如果你不使用 SwiftUI 那就需要在层次结构中 手动添加或移除图像视图 UIKit 提供了 效果的完成处理程序 可以帮到你 只需添加一个消失效果 当效果完成时 可从层次结构中移除图像视图 就是这些内容 以上就是 SwiftUI、AppKit 和 UIKit 中的符号效果 现在 各位已了解了基础知识 接下来 我会介绍一些 进阶技巧 可帮助你在 App 中 深入运用符号效果 UIImageView 上新的 UIKit 方法 也可用于 UIBarButtonItem 便捷的符号动画操作 让工具栏焕发生机 在 iOS 17 上 一些 UIKit 控件 还具有内置的符号动画
例如 UISlider 现在 会在拇指划到 轨道的两端时弹跳图像 你可以使用 UIControl 和 UIBarButtonItem 上 新的 isSymbolAnimationEnabled 属性 来控制是否播放这些动画
在 SwiftUI 中 禁用符号效果时 还有一些事项需要额外注意
与 SwiftUI 中的其他修饰符一样 symbolEffect 修饰符 会通过视图层次结构传播 也就是说 可以通过在父视图上添加修饰符 来将效果应用于多个图像 使用 symbolEffectsRemoved 修饰符 可以防止视图继承符号效果 某些符号效果 如出现、消失和缩放 会通过动画改变符号的外观 你可能会希望让符号在初始状态下 缩放或消失 且不带动画效果 在 SwiftUI 中 可以使用已禁用动画的事务 来实现这一点 在此示例中 我用它来应用一个 无动画的 scale.up 效果
在 AppKit 和 UIKit 中 使用 addSymbolEffect 的 animated 参数 能够以无动画的方式应用效果 最后 我们来了解一下变量值
iOS 16 和 macOS Ventura 引入了变量值 作为符号的另一个维度 代表了诸如音量水平 和信号强度等概念
在 iOS 17 和 macOS Sonoma 中 在任意变量值之间 实现渐变效果已变得非常简单
在 SwiftUI 中 你无需任何操作 这里有一个 Wi-Fi 符号 其变量值基于某个状态 在本例中是当前信号强度 随着信号强度的变化 Wi-Fi 符号会自动更新 并同时在变量值之间进行动画显示 在 AppKit 和 UIKit 中 可使用自动符号内容过渡 它会检测新的符号图像 是否有不同的变量值 如果有的话 就会淡入淡出到新的值
非常感谢你今天观看本课程 有很多方法 可以对符号进行动画处理 欢迎使用 SF Symbols App 来发现更多可能 探索 Symbols 框架 并尝试在 SwiftUI、 AppKit 和 UIKit 中 使用新的符号效果 API 最后 欢迎采用动画效果 让 App 界面更加赏心悦目
还可以观看其他有关符号的课程 了解符号动画的 人机界面指南 以及更新自定义符号 来支持所有效果的方法 感谢观看 祝你编码顺利
-
-
6:02 - Symbol effects in SwiftUI
// Symbol effects in SwiftUI Image(systemName: "wifi.router") .symbolEffect(.variableColor.iterative.reversing) .symbolEffect(.scale.up)
-
6:02 - Symbol effects in AppKit and UIKit
let imageView: NSImageView = ... imageView.addSymbolEffect(.variableColor.iterative.reversing) imageView.addSymbolEffect(.scale.up)
-
6:49 - Indefinite symbol effects in SwiftUI
struct ContentView: View { @State var isConnectingToInternet: Bool = true var body: some View { Image(systemName: "wifi.router") .symbolEffect( .variableColor.iterative.reversing, isActive: isConnectingToInternet ) } }
-
7:09 - Indefinite symbol effects in AppKit and UIKit
let imageView: NSImageView = ... imageView.addSymbolEffect(.variableColor.iterative.reversing) // Later, remove the effect imageView.removeSymbolEffect(ofType: .variableColor)
-
8:26 - Discrete symbol effects in SwiftUI
struct ContentView: View { @State var bounceValue: Int = 0 var body: some View { VStack { Image(systemName: "antenna.radiowaves.left.and.right") .symbolEffect( .bounce, options: .repeat(2), value: bounceValue ) Button("Animate") { bounceValue += 1 } } } }
-
8:26 - Discrete symbol effects in AppKit and UIKit
let imageView: NSImageView = ... // Bounce imageView.addSymbolEffect(.bounce, options: .repeat(2))
-
9:40 - Content transition symbol effects in SwiftUI
struct ContentView: View { @State var isPaused: Bool = false var body: some View { Button { isPaused.toggle() } label: { Image(systemName: isPaused ? "pause.fill" : "play.fill") .contentTransition(.symbolEffect(.replace.offUp)) } } }
-
9:57 - Content transition symbol effects in AppKit and UIKit
let imageView: UIImageView = ... imageView.image = UIImage(systemName: "play.fill") // Change the image with a Replace effect let pauseImage = UIImage(systemName: "pause.fill")! imageView.setSymbolImage(pauseImage, contentTransition: .replace.offUp)
-
11:14 - Indefinite Appear and Disappear symbol effects in SwiftUI
struct ContentView: View { @State var isMoonHidden: Bool = false var body: some View { HStack { RoundedRectangle(cornerRadius: 5) Image(systemName: "moon.stars") .symbolEffect(.disappear, isActive: isMoonHidden) Circle() } } }
-
11:30 - Indefinite Appear and Disappear symbol effects in AppKit and UIKit
let imageView: UIImageView = ... imageView.image = UIImage(systemName: "moon.stars") imageView.addSymbolEffect(.disappear) // Re-appear the symbol imageView.addSymbolEffect(.appear)
-
12:38 - Transition symbol effects in SwiftUI
struct ContentView: View { @State var isMoonHidden: Bool = false var body: some View { HStack { RoundedRectangle(cornerRadius: 5) if !isMoonHidden { Image(systemName: "moon.stars") .transition(.symbolEffect(.disappear.down)) } Circle() } } }
-
12:59 - Appear and Disappear symbol effects in UIKit with completion handler
let imageView: UIImageView = ... imageView.image = UIImage(systemName: "moon.stars") imageView.addSymbolEffect(.disappear) { context in if let imageView = context.sender as? UIImageView, context.isFinished { imageView.removeFromSuperview() } }
-
14:19 - Symbol effect propagation in SwiftUI
VStack { Image(systemName: "figure.walk") .symbolEffectsRemoved() Image(systemName: "car") Image(systemName: "tram") } .symbolEffect(.pulse)
-
14:55 - Effects without animation in SwiftUI
struct ContentView: View { @State var isScaledUp: Bool = false var body: some View { Image(systemName: "iphone.radiowaves.left.and.right") .symbolEffect(.scale.up, isActive: isScaledUp) .onAppear { var transaction = Transaction() transaction.disablesAnimations = true withTransaction(transaction) { isScaledUp = true } } } }
-
15:06 - Effects without animation in AppKit and UIKit
// Effects without animation in AppKit and UIKit let imageView: UIImageView = ... imageView.image = UIImage(systemName: "iphone.radiowaves.left.and.right") imageView.addSymbolEffect(.disappear, animated: false)
-
15:44 - Variable value animations in SwiftUI
struct ContentView: View { @State var signalLevel: Double = 0.5 var body: some View { Image(systemName: "wifi", variableValue: signalLevel) } }
-
16:07 - Variable value animations in AppKit and UIKit
let imageView: UIImageView = ... imageView.image = UIImage(systemName: "wifi", variableValue: 1.0) // Animate to a different Wi-Fi level let currentSignalImage = UIImage( systemName: "wifi", variableValue: signalLevel )! imageView.setSymbolImage(currentSignalImage, contentTransition: .automatic)
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。