HapticsManager

Swift Versions

Platforms

Licence

HapticsManager 是一个 Swift 包,它提供了一个模块化且易于使用的接口,用于在你的应用程序中实现触觉反馈。它可以与 SwiftUI 无缝集成,使你能够通过可定制的触觉反馈来增强用户体验。

特性

安装

使用 Swift Package Manager 将 HapticsManager 添加到你的 Swift 项目中。

dependencies: [
  .package(url: "https://github.com/markbattistella/HapticsManager", from: "1.0.0")
]

为什么使用它

这个包的工作方式与 SwiftUI 的 .sensoryFeedback API 类似,但通过允许你使用 UserDefaults 全局配置是否启用触觉反馈,从而增加了更多的灵活性。

使用 HapticsManager,你可以轻松确定用户是否应该使用触觉反馈 —— 从而简化你的工作流程并保持熟悉的声明式语法。

主要优点是你可以集中控制 isHapticsEnabled 键,允许或有条件地启用触觉反馈,而无需在每次触发时重写逻辑。.sensoryFeedback 需要你在每次使用时都实现这个逻辑。

用法

有三种使用 HapticsManager 的方法

静态操作

静态操作格式允许你一致且简单地触发触觉反馈。在下面的示例中,每当 isSuccess 状态更改时,就会触发触觉反馈。

@State private var isSuccess: Bool = false

Button("isSuccess: \(isSuccess)") {
  isSuccess.toggle()
}
.hapticFeedback(.notification(.warning), trigger: isSuccess)

带有条件的静态操作

你还可以使用条件来控制何时应触发触觉反馈,从而可以更集中地控制反馈的发生时间。

旧值和新值

enum Phase { case inactive, active, completed }

@State private var phase: Phase = .inactive

Button("Update phase") {
  switch phase {
    case .inactive: phase = .active
    case .active: phase = .completed
    case .completed: phase = .inactive
  }
}
.hapticFeedback(.impact(.medium), trigger: phase) { oldValue, newValue in
  oldValue != .completed && newValue == .completed
}

仅新值

enum Phase { case inactive, active, completed }

@State private var phase: Phase = .inactive

Button("Update phase") {
  switch phase {
    case .inactive: phase = .active
    case .active: phase = .completed
    case .completed: phase = .inactive
  }
}
.hapticFeedback(.impact(.medium), trigger: phase) { newValue in
  newValue == .completed
}

无参数

@State private var phase: Bool = false

Button("Toggle Phase") {
  phase.toggle()
}
.hapticFeedback(.impact(.medium), trigger: phase) {
  // Haptic feedback triggered
}

动态操作

动态操作方法使你可以完全控制反馈类型以及触发反馈的条件。

旧值和新值

enum LoadingState { case ready, success, failure }

@State private var loadingState: LoadingState = .ready

Button("Update loading state") {
  switch loadingState {
    case .ready: loadingState = .success
    case .success: loadingState = .failure
    case .failure: loadingState = .ready
  }
}
.hapticFeedback(trigger: loadingState) { oldValue, newValue in
  switch (oldValue, newValue) {
    case (.failure, .ready):
      return .notification(.warning)
    case (.ready, .success):
      return .notification(.success)
    case (.success, .failure):
      return .notification(.error)
    default:
      return nil
  }
}

仅新值

enum LoadingState { case ready, success, failure }

@State private var loadingState: LoadingState = .ready

Button("Update loading state") {
  switch loadingState {
    case .ready: loadingState = .success
    case .success: loadingState = .failure
    case .failure: loadingState = .ready
  }
}
.hapticFeedback(trigger: loadingState) { newValue in
  switch newValue {
    case .success: return .notification(.success)
    case .failure: return .notification(.error)
    default: return nil
  }
}

无参数

@State private var isLoading: Bool = false

Button("Toggle Loading") {
  isLoading.toggle()
}
.hapticFeedback(trigger: isLoading) {
  return .impact(.heavy)
}

配置触觉设置

HapticsManager 包含一个 .hapticEffectsEnabled UserDefaults 键,允许你根据用户设置动态启用或禁用触觉反馈。

如果你想添加一个设置屏幕来切换触觉反馈,或者你需要一个总体逻辑来控制触觉反馈,例如,将其作为一项高级功能,这将很有帮助。

内置 UserDefaults 套件

该包使用内部公开的 UserDefaults 套件来存储与触觉相关的设置

@main
struct MyAwesomeApp: App {

  init() {
    UserDefaults.haptic.register([
      HapticUserDefaultsKey.hapticEffectsEnabled : true
    ])
  }

  var body: some Scene {
    WindowGroup { ... }
  }
}

或者手动更新它

Button("Turn haptics off") {
  UserDefaults.haptics.set(false, for: HapticUserDefaultKeys.isHapticEnabled)
}

Button("Turn haptics on") {
  UserDefaults.haptics.set(true, for: HapticUserDefaultKeys.isHapticEnabled)
}

重要

尽管你可以将 UserDefaults 注册到任何套件(.standard 或自定义),但该包只会响应内部 .haptic 套件,以防止应用程序不同部分之间发生意外冲突。

扩展触觉反馈类型

如果内置的反馈类型不够用,你可以使用 .custom(CustomHaptic) Feedback case 来创建自定义触觉模式。

创建自定义反馈

要添加自定义触觉反馈类型

  1. 定义一个符合 CustomHaptic 的枚举
enum MyCustomHapticPattern: CustomHaptic {
  case complexSuccess

  func play() {
    switch self {
      case .complexSuccess:
        playComplexSuccessHaptic()
    }
  }
}
  1. 实现 play() 函数以定义你的自定义触觉反馈
extension MyCustomHapticPattern {

  // From HWS: https://www.hackingwithswift.com/books/ios-swiftui/adding-haptic-effects
  func playComplexSuccessHaptic() {
    var events = [CHHapticEvent]()

    for i in stride(from: 0, to: 1, by: 0.1) {
      let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: Float(i))
      let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: Float(i))
      let event = CHHapticEvent(
        eventType: .hapticTransient, 
        parameters: [intensity, sharpness], 
        relativeTime: i
      )
      events.append(event)
    }

    for i in stride(from: 0, to: 1, by: 0.1) {
      let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: Float(1 - i))
      let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: Float(1 - i))
      let event = CHHapticEvent(
        eventType: .hapticTransient, 
        parameters: [intensity, sharpness], 
        relativeTime: 1 + i
      )
      events.append(event)
    }

    do {
      let pattern = try CHHapticPattern(events: events, parameters: [])
      let engine = try CHHapticEngine()
      try engine.start()
      let player = try engine.makePlayer(with: pattern)
      try player.start(atTime: 0)
    } catch {
      print("Failed to play pattern: \(error.localizedDescription).")
    }
  }
}
  1. 在你的应用程序中使用自定义反馈
@State private var isSuccess: Bool = false

Button("isSuccess: \(isSuccess)") {
  isSuccess.toggle()
}
.hapticFeedback(.custom(.complexSuccess), trigger: isSuccess)

贡献

欢迎任何形式的贡献! 请随时提交 pull request 或打开 issue,提出你的建议或改进意见。

许可证

HapticsManager 在 MIT 许可证下发布。 有关更多详细信息,请参见 LICENCE 文件。