0

Core Haptics在我的应用程序中使用来实现使用简单无法完成的自定义振动UIImpactFeedbackGenerator 然后我执行以下操作 -

  1. 实例化引擎
  2. 启动发动机
  3. 实例化 CHHapticPatternPlayer
  4. 启动播放器

播放振动的功能 -

func playContinuousVibration() {
    do {
        let pattern = try continuousVibration()
        try hapticEngine.start()
        let player = try hapticEngine.makePlayer(with: pattern)
        try player.start(atTime: CHHapticTimeImmediate)
        hapticEngine.notifyWhenPlayersFinished { _ in
            return .stopEngine
        }
    } catch {}
}

Firebase 崩溃堆栈跟踪

崩溃:com.apple.root.default-qos EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000010

CoreHaptics __31-[CHHapticEngine handleFinish:]_block_invoke.306 + 276

libdispatch.dylib _dispatch_call_block_and_release + 32

libsystem_pthread.dylib start_wqthread + 8

编辑 1 - 在此处发布整个课程代码,为读者提供更多上下文

final class CustomHapticFeedback {
    private let duration: Int
    private let amplitude: SwFeedbackImpactType?
    private var coreHapticsManager: Any? //Purposely made as Any to avoid using #available keyword everywhere
    
    init(duration: Int, amplitude: String) {
        self.duration = duration
        self.amplitude = SwFeedbackImpactType(rawValue: amplitude)
    }
    
    //This function will perform either UIKit haptic feedback or Core Haptics feedback depending on device and OS compatibility
    func performVibrateAction() {
        if #available(iOS 13.0, *) {
            guard CHHapticEngine.capabilitiesForHardware().supportsHaptics else {
                performUIKitHapticFeedback()
                return
            }
            performCoreHapticFeedback()
        } else {
            performUIKitHapticFeedback()
        }
    }
    
    private func performUIKitHapticFeedback() {
        if let amplitude = amplitude {
            SwHaptic.impact(amplitude.value).generate()
        }
    }
    
    private func performCoreHapticFeedback() {
        if #available(iOS 13.0, *) {
            /*
             Currently Core Haptics is not being used anywhere else apart from Gamification flow hence creating a new instance of
             CoreHapticsManager everytime. Once this is being used in multiple places we can think of persisting it
             */
            coreHapticsManager = CoreHapticsManager(duration: duration, amplitude: amplitude ?? .light)
            if let manager = coreHapticsManager as? CoreHapticsManager {
                manager.playContinuousVibration()
            }
        }
    }
}

@available(iOS 13.0, *)
final class CoreHapticsManager {
    private let hapticEngine: CHHapticEngine
    private let duration: Int
    private let amplitude: SwFeedbackImpactType
    
    init?(duration: Int, amplitude: SwFeedbackImpactType) {
        self.duration = duration
        self.amplitude = amplitude
        let hapticCapability = CHHapticEngine.capabilitiesForHardware()
        guard hapticCapability.supportsHaptics else {
            return nil
        }
        
        do {
            hapticEngine = try CHHapticEngine()
        } catch _ {
            return nil
        }
    }
    
    func playContinuousVibration() {
        do {
            let pattern = try continuousVibration()
            try hapticEngine.start()
            let player = try hapticEngine.makePlayer(with: pattern)
            try player.start(atTime: CHHapticTimeImmediate)
            hapticEngine.notifyWhenPlayersFinished { _ in
                return .stopEngine
            }
        } catch {}
    }
}

@available(iOS 13.0, *)
extension CoreHapticsManager {
    //Below function will play a continuous vibration for a constant period of time
    private func continuousVibration() throws -> CHHapticPattern {
        let hapticIntensity: Float
        switch amplitude {
        case .light:
            hapticIntensity = 0.7
        case .medium:
            hapticIntensity = 0.85
        case .heavy:
            hapticIntensity = 1.0
        }
        let continuousVibrationEvent = CHHapticEvent(
            eventType: .hapticContinuous,
            parameters: [
                CHHapticEventParameter(parameterID: .hapticIntensity, value: hapticIntensity)
            ],
            relativeTime: 0,
            duration: (Double(duration)/1000))
        return try CHHapticPattern(events: [continuousVibrationEvent], parameters: [])
    }
}

编辑2:

enum SwFeedbackImpactType: String {
    case light, medium, heavy
    var value: UIImpactFeedbackGenerator.FeedbackStyle {
        switch self {
        case .light:
            return .light
        case .medium:
            return .medium
        case .heavy:
            return .heavy
        }
    }
}

编辑 3:使 Haptic Player 成为类的成员,而不是在函数内部实例化

@available(iOS 13.0, *)
final class CoreHapticsManager {
    private let hapticEngine: CHHapticEngine
    private let duration: Int
    private let amplitude: SwFeedbackImpactType
    private var player: CHHapticPatternPlayer?
    
    init?(duration: Int, amplitude: SwFeedbackImpactType) {
        self.duration = duration
        self.amplitude = amplitude
        let hapticCapability = CHHapticEngine.capabilitiesForHardware()
        guard hapticCapability.supportsHaptics else {
            return nil
        }
        
        do {
            hapticEngine = try CHHapticEngine()
        } catch _ {
            return nil
        }
    }
    
    func playContinuousVibration() {
        do {
            let pattern = try continuousVibration()
            try hapticEngine.start()
            player = try hapticEngine.makePlayer(with: pattern)
            try player?.start(atTime: CHHapticTimeImmediate)
            hapticEngine.notifyWhenPlayersFinished { _ in
                return .stopEngine
            }
        } catch {}
    }
}
4

1 回答 1

0

我设法像这样玩触觉。我已经删除了测试的额外代码,但我认为您可以在完整代码中重现它,对吗?因为时间关系,我只测试了iOS 13及以上。

import Foundation

import CoreHaptics

@available(iOS 13.0, *)
class HapticManager {
  let hapticEngine: CHHapticEngine

  init?() {
    let hapticCapability = CHHapticEngine.capabilitiesForHardware()
    guard hapticCapability.supportsHaptics else {
      return nil
    }

    do {
      hapticEngine = try CHHapticEngine()
      hapticEngine.isAutoShutdownEnabled = true
    } catch let error {
      print("Haptic engine Creation Error: \(error)")
      return nil
    }
  }
  
  func playPattern() {
    do {
      let pattern = try continuousVibration()
      try hapticEngine.start()
      let player = try hapticEngine.makePlayer(with: pattern)
      try player.start(atTime: CHHapticTimeImmediate)
      hapticEngine.notifyWhenPlayersFinished { _ in
        return .stopEngine
      }
    } catch {
      print("Failed to play pattern: \(error)")
    }
  }
}

@available(iOS 13.0, *)
extension HapticManager {
  private func basicPattern() throws -> CHHapticPattern {

        let pattern = CHHapticEvent(
          eventType: .hapticTransient,
          parameters: [
            CHHapticEventParameter(parameterID: .hapticIntensity, value: 3.0),
            CHHapticEventParameter(parameterID: .hapticSharpness, value: 3.0)
          ],
          relativeTime: 1)

        return try CHHapticPattern(events: [pattern], parameters: [])
  }
    
    private func continuousVibration() throws -> CHHapticPattern {
        let duration = 1000 // ms suppose
        let hapticIntensity: Float
        hapticIntensity = 1.0
        let continuousVibrationEvent = CHHapticEvent(
            eventType: .hapticContinuous,
            parameters: [
                CHHapticEventParameter(parameterID: .hapticIntensity, value: hapticIntensity)
            ],
            relativeTime: 0,
            duration: (Double(duration)/1000))
        return try CHHapticPattern(events: [continuousVibrationEvent], parameters: [])
    }
}

所以为了测试你可以使用 playPattern 并且continuosVibration会被解雇。

但我认为,如果你想让它继续工作,你应该把它放在一个带有这样的触发器的 while 循环中:

func startHaptic() {
    sendHaptics = true
    while sendHaptics == true {
      queue.async {
        hapticManager?.playPattern()
      }
    }
  }

sendHaptics属性必须是全局的,对吗?并在不再需要触觉时切换它。

于 2021-12-27T15:33:52.080 回答