SwiftUI 5.0(iOS 17.0)触摸反馈“震荡波”与触发器模式趣谈

概览

要想创作出一款精彩绝伦的 App,绚丽的界面和灵动的动画并不是唯一吸引用户的要素。有时我们还希望让用户真切的感受到操作引发的触觉反馈,直击使用者的灵魂。

所幸的是新版 SwiftUI 原生提供了实现触觉震动反馈的机制。在介绍它之后我们还将进一步展开讨论该机制基于的触发器模式,并"青出于蓝而胜于绿"的设计我们自己的触发器实现。

在本篇博文中,您将学到如下内容:

  • 概览
  • [1. "震荡波"来袭](#1. “震荡波”来袭)
  • [2. 触发器模式](#2. 触发器模式)
  • [3. SwiftUI 触发器模式的其它应用](#3. SwiftUI 触发器模式的其它应用)
  • [4. 自定义触发器模式](#4. 自定义触发器模式)
  • 总结

相信学完本课后,小伙伴们对于 SwiftUI 中触觉反馈与触发器开发模式会有更深刻的领悟,从而能够更加游刃有余的使用它们。

那还等什么呢?We will rock you!!!😉


1. "震荡波"来袭

除了从视觉上强势吸引用户眼球之外,我们的 App 还可以用"更立体"的方式让用户爱不释手。是滴,我们就是要用触觉反馈震动他们"久逢甘露"的双手,用"震荡波"激荡他们的心灵。

震动反馈(Haptic )是 Apple 对于移动设备提供的一种加强用户体验的机制,它最早诞生于 UIKit。它的体验有点类似于之前 iPhone 中 3D Touch 的功能。

Haptic 被广泛应用在 iOS/iPadOS 中,Apple 系统在用户交互中大量使用了震动反馈,比如在锁屏状态下点击 iPhone 屏幕左下角的手电筒按钮:

或者 iPhone 隔空投送完成时给于用户的提示反馈,以及 AppleWatch 上的通知提醒等等。

更多 Haptic 撸码的相关介绍,请小伙伴们移步 Apple 官方开发网站观赏进一步内容:

前面说过 Haptic 最先是在 UIKit 中得到很好支持的,从 SwiftUI 5.0(iOS 17.0)开始苹果终于推出了 SwiftUI 里 Haptic 的原生实现 SensoryFeedback:

如上所示,SensoryFeedback 结构的"借花献佛"是通过视图扩展方法 sensoryFeedback 来完成的:

swift 复制代码
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
@available(visionOS, unavailable)
extension View {
    /// - Parameters:
    ///   - feedback: Which type of feedback to play.
    ///   - trigger: A value to monitor for changes to determine when to play.
    public func sensoryFeedback<T>(_ feedback: SensoryFeedback, trigger: T) -> some View where T : Equatable
    
    /// - Parameters:
    ///   - feedback: Which type of feedback to play.
    ///   - trigger: A value to monitor for changes to determine when to play.
    ///   - condition: A closure to determine whether to play the feedback when
    ///     `trigger` changes.
    public func sensoryFeedback<T>(_ feedback: SensoryFeedback, trigger: T, condition: @escaping (_ oldValue: T, _ newValue: T) -> Bool) -> some View where T : Equatable
    
    /// - Parameters:
    ///   - trigger: A value to monitor for changes to determine when to play.
    ///   - feedback: A closure to determine whether to play the feedback and
    ///     what type of feedback to play when `trigger` changes.
    public func sensoryFeedback<T>(trigger: T, _ feedback: @escaping (_ oldValue: T, _ newValue: T) -> SensoryFeedback?) -> some View where T : Equatable

}

可以看到 sensoryFeedback 方法拥有多个重写形式,但它们都毫无例外的使用了触发器模式(Trigger Mode)。

2. 触发器模式

什么是触发器模式呢?在 Apple 的开发中大家可能对观察者模式早就有所耳闻,触发器模式与此类似也属于苹果开发中的一种设计模式。触发器模式就是让状态的改变触发代码的执行。

在以状态驱动的 SwiftUI 王国中,触发器模式的使用更显得"如鱼得水",仿佛天造地设一般。

swift 复制代码
struct ContentView: View {
    @State private var store = Store()
    
    var body: some View {
        NavigationStack {
            List(store.results, id: \.self) { result in
                Text(result.title)
            }
            .searchable(text: $store.query)
            .sensoryFeedback(.success, trigger: store.results)
        }
    }
}

在上面的示例代码中,我们使用 sensoryFeedback 修改器方法为视图添加了震动反馈。其中可以看到:Haptic 产生的触发器是 store.results 状态,即当用户引起 Store 搜索结果发生改变时,我们纤细指尖才会喜提激荡震动着的触觉洗礼。

除了感受系统内置震动效果对"心灵的冲击"以外,我们还可以让震动更加"变幻莫测":

swift 复制代码
VStack {}
.sensoryFeedback(
    .impact(weight: .heavy, intensity: 0.9),
    trigger: trigger
)

类似的,大家还可以根据状态实际的值来决定到底使用何种 Haptic 效果,比如在下面的代码中我们就根据搜索是否成功来决定采用 .error 还是 .success 震动反馈类型:

swift 复制代码
List(store.results, id: \.self) { result in
        Text(result)
    }
    .searchable(text: $store.query)
    .sensoryFeedback(trigger: store.results) { oldValue, newValue in
        return newValue.isEmpty ? .error : .success
    }

注意,上面所有 Haptic 效果只有在触发器对应状态发生改变时才会产生,所以我们不用担心视图创建时触发器导致不希望的"副作用"。

3. SwiftUI 触发器模式的其它应用

除了 Haptic 对应的实现以外,在 SwiftUI 中还有很多其它功能也大量适配触发器模式。比如 scrollIndicatorsFlash 修改器方法:

scrollIndicatorsFlash 方法用来在指定状态发生改变时来"闪烁"可滚动视图中的滚动条:

swift 复制代码
struct TriggerValueExample: View {
    let messages: [String]
    
    var body: some View {
        List(messages, id: \.self) { message in
            Text(verbatim: message)
        }
        .scrollIndicatorsFlash(trigger: messages)
    }
}

在上面的代码中,当有新消息到来时我们会"闪烁"列表的滚动条以提示用户。

4. 自定义触发器模式

通过上面的介绍,想必大家对于何为触发器模式以及它的工作原理已经了然于心了。触发器模式在 SwiftUI 中被广泛地使用着,那我们能不能"百尺竿头更进一步"打造自己的触发器呢?

答案是肯定的!

正如观察者模式那样,设计模式意味着提供充分可定制的灵活性,除了使用系统框架提供的触发器以外,我们当然可以随心所欲地创建自己的触发器。

假如我们希望在 SwiftUI 中当某一状态发生改变时播放指定的声音,这可以恰如其分的用触发器模式来实现:

swift 复制代码
struct PlaySoundViewModifier<Trigger: Equatable>: ViewModifier {
    let sound: URL
    let trigger: Trigger

    func body(content: Content) -> some View {
        content
            .onChange(of: trigger) {
                if let player = try? AVAudioPlayer(contentsOf: sound) {
                    player.play()
                }
            }
    }
}

extension View {
    func playSound(_ sound: URL, trigger: some Equatable) -> some View {
        self.modifier(PlaySoundViewModifier(sound: sound, trigger: trigger))
    }
}

在上面的示例代码中,我们创建了 PlaySoundViewModifier 修改器方法,并绑定了一个遵守 Equatable 协议,类型为 Trigger 的属性,当 trigger 发生变化时,我们就利用 AVAudioPlayer 对象从容地播放想要的声音文件。

小伙伴们可以这样使用上面创建的 PlaySoundViewModifier 修改器方法:

swift 复制代码
struct SoundFeedbackExample: View {
    let messages: [String]
    
    var body: some View {
        List(messages, id: \.self) { message in
            Text(verbatim: message)
        }
        .playSound(
            Bundle.main.url(forResource: "sound", withExtension: "wav")!,
            trigger: messages
        )
    }
}

看到了吗?触发器模式就是这么单刀直入,让代码实现变得如此直接了当。从此小伙伴们开发兵器库中又多了一件神兵利器,棒棒哒!💯


想要系统学习 Swift 语言的小伙伴们,千万不要错过我的《Swift 语言开发精讲》专栏哦:


总结

在本篇博文中,我们介绍了 SwiftUI 5.0(iOS 17.0)中触觉反馈(Haptic)机制的实现,并由此抛砖引玉讨论了开发模式中的触发器模式,最后我们看到了实现自己心仪的触发器是多么的简单。

感谢观赏,再会!😎

相关推荐
TPCloud14 天前
快速搭建记录核心业务表删除记录的日志审计信息
数据库·oracle·触发器·日志审计
AlfredZhao5 个月前
使用触发器来审计表的DML、DDL操作
oracle·audit·trigger
大熊猫侯佩5 个月前
SwiftUI 5.0(iOS 17)滚动视图的滚动目标行为(Target Behavior)解惑和实战
滚动·scrollview·swiftui 5.0·ios 17·target behavior·惰性容器·滚动对齐
Tech Synapse6 个月前
MySQL 存储函数及调用
数据库·mysql·存储函数·触发器
大熊猫侯佩7 个月前
SwiftUI 5.0(iOS 17)进一步定制 TipKit 外观让撸码如虎添翼
swiftui 5.0·ios 17.0·tipkit·tip·tipviewstyle·自定义 tip 样式·全局调控 tip 外观
Flamesky9 个月前
Jenkins设置定时触发器执行任务
jenkins·trigger·定时
小基基o_O9 个月前
无限自动出兵-入门版【war3地图编辑器】
触发器·war3·魔兽争霸3·地图编辑器
悟道子HD9 个月前
SQL: 触发器/存储过程/游标的操作
数据库·经验分享·sql·sqlserver·存储过程·触发器·游标
Sarapines Programmer10 个月前
【Oracle】玩转Oracle数据库(五):PL/SQL编程
数据库·oracle·存储过程·触发器·oracle数据库·pl/sql编程