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

在这里插入图片描述

概览

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

在这里插入图片描述

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

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

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

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

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


1. “震荡波”来袭

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

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

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

在这里插入图片描述

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

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

  • Playing a single-tap haptic pattern

在这里插入图片描述

  • Core Haptics

在这里插入图片描述

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

在这里插入图片描述

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

@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 王国中,触发器模式的使用更显得“如鱼得水”,仿佛天造地设一般。

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 搜索结果发生改变时,我们纤细指尖才会喜提激荡震动着的触觉洗礼。

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

VStack {}
.sensoryFeedback(
    .impact(weight: .heavy, intensity: 0.9),
    trigger: trigger
)

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

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 方法用来在指定状态发生改变时来“闪烁”可滚动视图中的滚动条:

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 中当某一状态发生改变时播放指定的声音,这可以恰如其分的用触发器模式来实现:

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 修改器方法:

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 语言开发精讲》专栏哦:

在这里插入图片描述

  • Swift 语言开发精讲

总结

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

感谢观赏,再会!😎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/567632.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HBase的简单学习三

一 过滤器 1.1相关概念 1.过滤器可以根据列族、列、版本等更多的条件来对数据进行过滤&#xff0c; 基于 HBase 本身提供的三维有序&#xff08;行键&#xff0c;列&#xff0c;版本有序&#xff09;&#xff0c;这些过滤器可以高效地完成查询过滤的任务&#xff0c;带有过滤…

Redis中的缓存击穿、缓存穿透、缓存雪崩问题

1.什么是缓存击穿&#xff1f; 客户端恶意访问一个不存在的数据&#xff0c;从而造成穿透缓存&#xff0c;请求直接到达数据库&#xff0c;频繁的发送这一类的请求&#xff0c;直接查询数据库&#xff0c;数据库的压力变大。 1.1如何解决缓存击穿呢&#xff1f; 1&#xff0…

基于harris角点和RANSAC算法的图像拼接matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ....................................................................... I1_harris fu…

【MySQL]】数据库操作指南之数据库的基础操作

&#x1f331;博客主页&#xff1a;青竹雾色间 &#x1f331;系列专栏&#xff1a;MySQL探险日记 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ✨人生如寄&#xff0c;多忧何为 ✨ 文章目录 1. 创建数据库2.数据库的编码集与校验集2.1 编码集 (Character…

嵌入式Python基础1-2

嵌入式Python基础1-2 条件语句 if elif else 随机数random eval while循环 for循环 水仙花数 循环else list 列表常用方法 增删改查 加排序 append remove pop index() 升序sort(&#xff09;降序 sort(reverseTrue) 反转 reverse&#xff08;&#xff09;…

ESP32开发

1、简介 1.1 种类 WIFI模块在PC上做为客户端、服务器&#xff0c;在STM32上做服务器的通讯。在物联网应用开发有重要作用&#xff0c;种类居多&#xff0c;如下图 红色方框的esp8266-01s型号的无限wifi模块就是本章学习的主要对象。 1.2 特点 小巧的尺寸&#xff1a;ESP-01…

SpanBert学习

SpanBERT: Improving Pre-training by Representing and Predicting Spans 核心点 提出了更好的 Span Mask 方案&#xff0c;也再次展示了随机遮盖连续一段字要比随机遮盖掉分散字好&#xff1b;通过加入 Span Boundary Objective (SBO) 训练目标&#xff0c;增强了 BERT 的性…

python自动生成SQL语句自动化

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python自动生成SQL语句自动化 在数据处理和管理中&#xff0c;SQL&#xff08;Structured …

WAF防范原理

目录 一、什么是WAF 二、纵深安全防御 WAF的组网模式 WAF配置全景 WAF端 服务器 攻击端 拦截SQL注入&#xff0c;XSS攻击&#xff0c;木马文件上传 要求&#xff1a; 使用WAF&#xff0c;通过配置策略要求能防御常见的web漏洞攻击&#xff08;要求至少能够防御SQL、XSS、文…

毕业设计注意事项

1.开题 根据学院发的开题报告模板完成&#xff0c;其中大纲部分可参考资料 2.毕设 根据资料中的毕设评价标准&#xff0c;对照工作量 3.论文 3.1 格式问题 非常重要&#xff0c;认真对比资料中我发的模板&#xff0c;格式有问题&#xff0c;答辩输一半&#xff01; 以word…

wireshark RTP分析参数

主要看丢弃和Delta&#xff0c; 丢弃就是丢掉的udp包&#xff0c;所占的比率 Delta是当前udp包接收到的时间减去上一个udp包接收到的时间 根据载荷可以知道正确的delta应该是多少&#xff0c;比如G711A&#xff0c;ptime20&#xff0c;那么delta理论上应该趋近于20. 这里的de…

C++面向对象程序设计 - 运算符重载

函数重载就是对一个已有的函数赋予新的含义&#xff0c;使之实现新的功能。因此一个函数名就可以用来代表不同功能的函数&#xff0c;也就是一名多用。运算符也可以重载&#xff0c;即运算符重载&#xff08;operator overloading&#xff09;。 一、运算符重载的方法 运算符重…

# IDEA2019 如何打开 Run Dashboard 运行仪表面板

IDEA2019 如何打开 Run Dashboard 运行仪表面板 段子手168 1、依次点击 IDEA 上面工具栏 —> 【View】 视图。 —> 【Tool Windows】 工具。 —> 【Run Dashboard】 运行仪表面板。 2、如果 【Tool Windows 】工具包 没有 【Run Dashboard】 运行仪表面板 项 依次…

【好书推荐7】《机器学习平台架构实战》

【好书推荐7】《机器学习平台架构实战》 写在最前面《机器学习平台架构实战》编辑推荐内容简介作者简介目  录前  言本书读者内容介绍充分利用本书下载示例代码文件下载彩色图像本书约定 &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f30c; 2024每日百字篆刻时光&…

STM32系统参数和结构

系列文章目录 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. 基本参数 2. 片上资源&#xff08;外设&#xff09; 3. STM32系列命名规则 4. 系统结构 5. 引脚定义 6. 启动配置 7. 最小系统电路 8. 型号分类和缩写 1. 基本参数 STM32F103C8T6 系列&#…

达梦(DM)数据库表索引

达梦DM数据库表索引 表索引索引准则其他准则 创建索引显式地创建索引其他创建索引语句 使用索引重建索引删除索引 表索引 达梦数据库表索引相关内容比较多&#xff0c;常用的可能也就固定的一些&#xff0c;这里主要说一下常用的索引&#xff0c;从物理存储角度进行分类&#…

B008-方法参数传递可变参数工具类

目录 方法参数传递可变参数冒泡排序Arrays工具类Arrays工具类常用方法 方法参数传递 /*** java中只有值传递* 基本数据类型 传递的是具体的值* 引用数据类型 传递的是地址值*/ public class _01_ParamPass {public static void main(String[] args) {// 调用方法 getSumge…

网络变压器在网络分析仪上能通过测试,装上设备后网速达不到呢?

Hqst华轩盛(石门盈盛)电子导读&#xff1a;今天和大家一起探讨网络变压器在网络分析仪上能通过测试&#xff0c;装上设备后网通设备网速达不到的可能原因及其处理方式 一、出现这种情况可能有以下原因&#xff1a; 1.1. 设备兼容性问题&#xff1a;设备其它元器件与 网络…

Docker容器化技术:概述与安装

目录 一、云基础知识 1、常见的云服务厂商 2、云计算服务模式三种层次 3、什么是虚拟化 4、什么是虚拟机 5、虚拟化产品 5.1 仿真虚拟化产品 5.2 半虚拟化产品 5.3 全虚拟化产品 6、虚拟机架构 6.1 寄居架构 6.2 源生架构 二、认识容器 1、容器的概述 2、容器的…

【Netty】ByteBuf与拆包粘包

ByteBuf 在介绍ByteBuf之前先来一套基础的代码来演示ByteBuf的使用。 package blossom.project.netty;import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled;import java.nio.charset.StandardCharsets;/*** author: ZhangBlossom* date: 2023/12/14 13:37* con…