SwiftUI Tips: SwiftUI 中如何地道的发起震动(iOS 17+)

主要内容翻译自: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 中的震动逻辑要保持一致。
相关推荐
东坡肘子4 天前
当 Build in Public 重新转入地下 -- 肘子的 Swift 周报 #157
人工智能·swiftui·swift
OliverQiu5 天前
开源一个 macOS 小工具:集中管理 ChatGPT 的网络启动配置
macos·chatgpt·swiftui·mac·proxy·codex·dmg
传奇开心果编程8 天前
【SwiftUI提高练中学】第9课 安全与隐私
swiftui·移动开发·swift·编程语言理论·开发范式·编程框架·声明式ui
传奇开心果编程9 天前
【声明式UI开发实用技术学与练】第8课 状态提升与下放
学习·flutter·react native·ui·swiftui·composer
东坡肘子9 天前
Swift Server,又多了一个 Google -- 肘子的 Swift 周报 #156
人工智能·swiftui·swift
传奇开心果编程10 天前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程10 天前
【SwiftUI提高练中学】第8课 网络层架构:拦截器、重试、缓存与离线优先
学习·macos·ui·ios·swiftui
传奇开心果编程10 天前
【现代声明式UI学与练】第7课 组件通信——父传子、子传父、跨层级通信、状态提升与 Context
学习·flutter·react native·ui·swiftui·android jetpack
tink11 天前
告别 Xcode IDE:用 VS Code + SweetPad + XcodeGen 开发 iOS 应用的完整指南
ios·swiftui·xcode
传奇开心果编程11 天前
【现代声明式UI学与练】第9课 性能优化——渲染优化、列表优化、内存优化、启动优化
学习·flutter·react native·ui·性能优化·swiftui·android jetpack