主要内容翻译自:SwiftUI Sensory Feedback
在 iOS 17 中,SwiftUI 增加了一个地道的 modifier 来提供震动反馈。
SwiftUI 经典情况再现。好消息:我们新增了一个 modifier 实现了 UIKit 的一个功能,更加优雅。坏消息:从最新的版本的 iOS 开始支持。
在 iOS 17 之前的情况
在 iOS 17 之前,震动反馈通过 UIFeedbackGenerator
的子类实现(UIImpactFeedbackGenerator、UISelectionFeedbackGenerator. UINotificationFeedbackGenerator
)。
使用的方式大概这样:
swift
struct OldFeedback: View {
@State var value: Bool = true
let generator = UISelectionFeedbackGenerator()
var body: some View {
Toggle("Switch", isOn: $value)
.onChange(of: value, {
generator.selectionChanged()
})
}
}
iOS 17:Sensory Feedback
在 iOS 17 中引入了原生的 sensoryFeedback
modifier:
swift
struct Feedback: View {
@State var value: Bool = true
var body: some View {
Toggle("Switch", isOn: $value)
.sensoryFeedback(.selection, trigger: value)
}
}
这个方法还很贴心的添加了尾随的判断闭包,类似 filter。因为我们可能不是每次值的变换都震动,可以把筛选条件加到闭包里:
swift
struct Feedback: View {
@State var value: Bool = true
var body: some View {
Toggle("Switch", isOn: $value)
.sensoryFeedback(.selection, trigger: value,
condition: { oldValue, newValue in
if newValue == true {
return true
} else {
return false
}
})
}
}
还有一个签名是可以通过观察的值来返回不同的震动:
swift
struct Feedback: View {
@State var value: Bool = true
var body: some View {
Toggle("Switch", isOn: $value)
.sensoryFeedback(trigger: value) { oldValue, newValue in
if newValue == true {
return SensoryFeedback.selection
} else {
return SensoryFeedback.warning
}
}
}
}
不同的平台的区别
SwiftUI 天生就支持苹果全平台。震动反馈又具有平台特性(比如在 mac 上就是触摸板的震动,在手表上是 taptic engine),因此震动选项 SensoryFeedback
的值也是区分平台的。小知识:iPad 上没有震动。
watchOS only
- start: Activity started
- stop: Activity stopped
start 和 stop 通常用来表示一个活动的开始和结束(比如手表上的计时)。
macOS only
- alignment: a dragged item is in alignment with another item.
- levelChange: indicates movement between discrete levels of pressure. For example, holding a fast-forward button.
这两个震动都是针对 mac 的触摸板的。alignment 场景是当你拖动一个元素时,要提醒对齐状态时给与的小震动。levelChange 场景是按压的力度,比如长按一个元素可以开启排序,可以给出一个类似于重按的反馈。
watchOS & iOS
剩下的选项都是 watchOS 和 iOS 通用的。
震动选项有这些:
- decrease: Important value decreased below a significant threshold
- increase: Important value increased above a significant threshold
特别要提一下的是 decrease、increase。这两种震动文档上写的是 watchOS only,但是目前在 iOS 也可以正常震动。
- selection: A UI element's values are changing.
- success: A task completed successfully
- warning: A task produced a warning
- error: A task produced an error
- impact: A physical impact when UI elements collide.
自定义震动参数
在 impact
这个选项中,还额外给了两个参数来调节震动:
- weight:调节震动的强度,有light、medium、heavy三档。你甚至可以在这个基础上再设置强度,他真的我哭死。
- flexibility:震感曲线,可以理解为弹簧的软硬,震动是清脆还是柔软一点,有rigid、solid、soft三档。你甚至可以在这个基础上再设置强度,他真的我哭死。
代码演示:
swift
struct Feedback: View {
@State var value: Bool = true
var body: some View {
Toggle("Switch", isOn: $value)
.sensoryFeedback(trigger: value) { oldValue, newValue in
if newValue == true {
return SensoryFeedback.impact(weight: .light, intensity: 1.5)
} else {
return SensoryFeedback.impact(flexibility: .rigid, intensity: 2.0)
}
}
}
}
HIG 使用建议
苹果在 HIG 中对如何(Human Interface Guidelines - Playing haptics)使用震动有提到一些建议:
- 震动的类型和系统定义一致。比如系统提供了选中的震动,用户在系统其他地方也很熟悉选中的震动,这个时候 app 里就建议使用和系统行为一致的震动方式。
- 不用过度使用。
- 可以允许用户关闭震动。
- 整个 app 中的震动逻辑要保持一致。