Core Haptics 是苹果提供的更高级的触觉反馈框架,允许开发者创建复杂且丰富的触觉体验。与简单的 UIImpactFeedbackGenerator 等触觉反馈生成器相比,Core Haptics 提供了更多的灵活性,可以自定义强度、持续时间、频率等参数来设计更细腻的触觉体验。
Core Haptics 的核心概念
1、Haptic Engine
驱动所有触觉反馈的核心。可以通过 CHHapticEngine 来控制反馈的播放、停止和重新启动。
2、Haptic Pattern
定义了一个完整的触觉反馈效果。可以包括多种触觉事件(如点击、振动)和音频事件。
3、Haptic Event
具体的触觉效果,如点击、持续的振动等。可以自定义时间、强度和锐度等参数。
4、Haptic Parameter
控制单个事件的属性,比如强度、频率、持久度。
基本用法
创建并启动CHHapticEngine
首先在Swift文件的顶部附近添加导入 CoreHaptics框架,这是一个用于在支持触觉反馈(Haptics)的设备上生成触觉效果的框架。通过引入这个框架,可以访问用于创建和管理触觉事件的类和方法。
import CoreHaptics
然后我们可以自定义一个HapticManager类,用于封装与触觉引擎相关的功能。这样做可以使触觉效果的创建和管理更加模块化,便于在应用中复用。
class HapticManager { }
接下来我们需要创建一个CHHapticEngine属性,类型为CHHapticEngine?,表示可选的触觉引擎对象。CHHapticEngine是CoreHaptics中的一个核心类,负责生成和管理触觉事件。
使用可选类型(?)是为了在类初始化时可以先不设置具体的引擎对象,并且方便在创建引擎时检查是否成功。——这是负责产生振动的实际对象,因此我们需要在添加触觉效果之前预先创建它。
private var hapticEngine: CHHapticEngine?
一旦主视图出现,我们就会创建它。创建引擎时,可以附加处理程序以帮助在活动停止时恢复活动。
init() {
createEngine()
}
创建createEngine()方法,用于实例化并启动触觉引擎。
private func createEngine() {
do {
hapticEngine = try CHHapticEngine()
try hapticEngine?.start()
} catch {
print("Failed to create the haptic engine: \(error)")
}
}
CHHapticEngine()是CoreHaptics框架中用于创建触觉引擎的构造函数。触觉引擎负责生成、控制和管理触觉模式。使用try的原因为触觉引擎的创建可能会失败,例如在设备不支持触觉反馈或发生其他错误时,try关键词表示需要捕获可能抛出的错误。
try hapticEngine?.start()表示触觉引擎创建成功后,通过调用start()方法来启动引擎。注意,我们创建引擎后,如果没有启动,就不会产生触觉反馈。同样使用try来捕获启动过程中的潜在错误。
当创建或启动引擎时发生错误,程序会进入catch块,打印错误信息。
import CoreHaptics
class HapticManager {
private var hapticEngine: CHHapticEngine?
init() {
createEngine()
}
private func createEngine() {
do {
hapticEngine = try CHHapticEngine()
try hapticEngine?.start()
} catch {
print("Failed to create the haptic engine: \(error)")
}
}
}
触觉事件
简单的瞬时触觉事件
1、创建名为playSimpleHaptic的实例方法
表示在HapticManager对象上调用。该方法没有参数,没有返回值,主要用于播放一个简单的触觉反馈。
func playSimpleHaptic() { }
在触觉事件中,首先需要检查设备是否支持触觉反馈。
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
CHHapticEngine.capabilitiesForHardware()返回一个对象,描述当前设备的触觉功能。
supportsHaptics是一个布尔值,如果当前设备支持触觉反馈,值为true,否则为false。
通过guard语句,如果设备不支持触觉反馈,直接返回,不继续执行后面的代码,这样确保触觉功能只有在设备支持时才会运行。
2、创建一个瞬时触觉事件
let hapticEvent = CHHapticEvent(eventType: .hapticTransient, // 瞬时触觉事件
parameters: [],
relativeTime: 0)
CHHapticEvent表示一个触觉事件,eventType参数指定了时间的类型。
.hapticTransient是事件类型之一,表示这是一个短暂的、瞬时的触觉反馈。它可以表现为一个短促的振动。
CHHapticEvent除了 .hapticTransient以外,还有一种叫 .hapticContinuous的事件类型。
前者用于创建一个短促的、瞬间触发的振动效果。后者用于创建一个持续触觉事件,可以设定持续的事件,提供更长时间的触觉效果。所以,.hapticTransient还需要一个duration参数来指定振动持续的时间长度(以秒为单位)。
parameters: [] 表示该触觉事件没有附加的参数,如强度和锐度。这意味着会使用默认的触觉效果。
relativeTime: 0 表示事件的触发时间相对于触觉模式的开始时间。0 表示立即触发。
3、创建触觉模式并播放
do {
let pattern = try CHHapticPattern(events: [hapticEvent], parameters: [])
let player = try hapticEngine?.makePlayer(with: pattern)
try player?.start(atTime: 0)
} catch {
print("Failed to play haptic: \(error)")
}
代码解析
创建CHHapticPattern
let pattern = try CHHapticPattern(events: [hapticEvent], parameters: [])
CHHapticPattern(events: [hapticEvent], parameters: []) 用于创建一个触觉模式,包含一个或多个触觉事件(此处为 hapticEvent)。
hapticEvent就是刚刚创建的瞬时触觉事件。
parameters: [] 用于指定一些全局的参数,但在这里不设置任何全局参数。
使用 try 来处理可能抛出的错误,如果无法创建触觉模式,会进入 catch 块。
创建CHHpaticPlayer
let player = try hapticEngine?.makePlayer(with: pattern)
hapticEngine?.makePlayer(with: pattern) 创建一个 CHHapticPlayer 对象,可以根据定义的模式播放触觉事件。
CHHapticPlayer 是一个实际播放触觉事件的对象。 同样使用 try 来捕获潜在的错误。
启动触觉事件
try player?.start(atTime: 0)
从指定的时间点开始触觉事件。0表示理解开始。如果成功,设备会产生一个瞬时的触觉反馈。
4、调用瞬时触觉事件
class HapticManager {
private var hapticEngine: CHHapticEngine?
init() {
createEngine()
}
private func createEngine() {
do {
hapticEngine = try CHHapticEngine()
try hapticEngine?.start()
} catch {
print("Failed to create the haptic engine: \(error)")
}
}
func playSimpleHaptic() {
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
let hapticEvent = CHHapticEvent(eventType: .hapticTransient, // 瞬时触觉事件
parameters: [],
relativeTime: 0)
do {
let pattern = try CHHapticPattern(events: [hapticEvent], parameters: [])
let player = try hapticEngine?.makePlayer(with: pattern)
try player?.start(atTime: 0)
} catch {
print("Failed to play haptic: \(error)")
}
}
}
自定义触觉事件
在简单的瞬时触发事件上,我们进阶学习自定义的触发事件。
1、创建名为playCustomHaptic的实例方法
表示在HapticManager对象上调用。该方法没有参数,没有返回值,主要用于播放一个简单的触觉反馈。
func playCustomHaptic() { }
在触觉事件中,首先需要检查设备是否支持触觉反馈。
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
CHHapticEngine.capabilitiesForHardware()返回一个对象,描述当前设备的触觉功能。
supportsHaptics是一个布尔值,如果当前设备支持触觉反馈,值为true,否则为false。
通过guard语句,如果设备不支持触觉反馈,直接返回,不继续执行后面的代码,这样确保触觉功能只有在设备支持时才会运行。
2、自定义触觉参数
我们需要创建CHHapticEventParameter来定义触觉反馈的特性。
let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1.0) // 最高强度
let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5) // 中等锐度
.hapticIntensity: 控制触觉事件的强度(从 0.0 到 1.0),1.0 是最强的震动效果。
.hapticSharpness: 控制触觉事件的锐度(从 0.0 到 1.0),0.0 是模糊的震动,1.0 是清脆的震动。这里设置为中等锐度。
3、创建一个自定义触觉事件
let event = CHHapticEvent(eventType: .hapticContinuous,
parameters: [intensity, sharpness],
relativeTime: 0.1,
duration: 1.0)
CHHapticEvent表示一个触觉事件,eventType参数指定了时间的类型。
本次用到的就是.hapticContinuous的事件类型,它用于创建一个持续触觉事件,可以设定持续的事件,提供更长时间的触觉效果。
parameters: [intensity, sharpness] 表示该触觉事件使用前面自定义的触觉参数,强度为1.0,锐度为0.5.
relativeTime: 0.1 表示事件的触发时间相对于触觉模式的开始时间。0.1表示0.1秒后开始。
duration:1.0 表示该触觉事件持续1秒。
4、创建触觉模式并播放
do {
let pattern = try CHHapticPattern(events: [event], parameters: [])
let player = try hapticEngine?.makePlayer(with: pattern)
try player?.start(atTime: 0)
} catch {
print("Failed to play custom haptic: \(error)")
}
代码解析
创建CHHapticPattern
let pattern = try CHHapticPattern(events: [event], parameters: [])
CHHapticPattern(events: [event], parameters: []) 用于创建一个触觉模式,包含一个或多个触觉事件(此处为 event)。
event就是刚刚创建的自定义触觉事件。
parameters: [] 用于指定一些全局的参数,但在这里不设置任何全局参数。
使用 try 来处理可能抛出的错误,如果无法创建触觉模式,会进入 catch 块。
创建CHHpaticPlayer
let player = try hapticEngine?.makePlayer(with: pattern)
hapticEngine?.makePlayer(with: pattern) 创建一个 CHHapticPlayer 对象,可以根据定义的模式播放触觉事件。
CHHapticPlayer 是一个实际播放触觉事件的对象。
同样使用 try 来捕获潜在的错误。
启动触觉事件
try player?.start(atTime: 0)
从指定的时间点开始触觉事件。0表示理解开始。如果成功,设备会产生一个瞬时的触觉反馈。
5、调用瞬时触觉事件
class HapticManager {
private var hapticEngine: CHHapticEngine?
init() {
createEngine()
}
private func createEngine() {
do {
hapticEngine = try CHHapticEngine()
try hapticEngine?.start()
} catch {
print("Failed to create the haptic engine: \(error)")
}
}
func playCustomHaptic() {
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1.0) // 最高强度
let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5) // 中等锐度
let event = CHHapticEvent(eventType: .hapticContinuous,
parameters: [intensity, sharpness],
relativeTime: 0.1,
duration: 1.0)
do {
let pattern = try CHHapticPattern(events: [event], parameters: [])
let player = try hapticEngine?.makePlayer(with: pattern)
try player?.start(atTime: 0)
} catch {
print("Failed to play custom haptic: \(error)")
}
}
}
总结
playSimpleHaptic 方法的作用是在支持触觉反馈的设备上播放一个简单的瞬时触觉事件。它首先检查设备是否支持触觉反馈,然后创建一个简单的触觉事件并播放。通过这种封装,我们可以在应用的特定场景(比如按钮点击、状态变化等)触发这个方法,为用户提供即时的触觉反馈。
playCustomHaptic 方法的作用是在支持触觉反馈的设备上播放一个自定义的触觉事件。它相比playSimpleHaptic方法,我们可以在触觉参数中进一步控制触觉事件的强度、锐度以及持续时间。
通过这种封装,我们可以在应用的特定场景(比如按钮点击、状态变化等)触发这个方法,为用户提供即时的触觉反馈。
完整代码
import SwiftUI
import CoreHaptics
class HapticManager {
private var hapticEngine: CHHapticEngine?
init() {
createEngine()
}
private func createEngine() {
do {
hapticEngine = try CHHapticEngine()
try hapticEngine?.start()
} catch {
print("Failed to create the haptic engine: \(error)")
}
}
func playSimpleHaptic() {
guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else { return }
let hapticEvent = CHHapticEvent(eventType: .hapticTransient, // 瞬时触觉事件
parameters: [],
relativeTime: 0)
do {
let pattern = try CHHapticPattern(events: [hapticEvent], parameters: [])
let player = try hapticEngine?.makePlayer(with: pattern)
try player?.start(atTime: 0)
} catch {
print("Failed to play haptic: \(error)")
}
}
func playCustomHaptic() {
let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: 1.0) // 最高强度
let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.5) // 中等锐度
let event = CHHapticEvent(eventType: .hapticContinuous,
parameters: [intensity, sharpness],
relativeTime: 0.1,
duration: 1.0)
do {
let pattern = try CHHapticPattern(events: [event], parameters: [])
let player = try hapticEngine?.makePlayer(with: pattern)
try player?.start(atTime: 0)
} catch {
print("Failed to play custom haptic: \(error)")
}
}
}
struct Touch: View {
let hapticManager = HapticManager()
var body: some View {
VStack(spacing: 20) {
Button("Play Simple Haptic") {
hapticManager.playSimpleHaptic()
}
Button("Play Custom Haptic") {
hapticManager.playCustomHaptic()
}
}
.padding()
}
}
#Preview {
Touch()
}