Swiftgram/submodules/Display/Source/HapticFeedback.swift
2024-01-26 01:53:43 +04:00

241 lines
7.2 KiB
Swift

import Foundation
import UIKit
import AudioToolbox
import CoreHaptics
public enum ImpactHapticFeedbackStyle: Hashable {
case light
case medium
case heavy
case soft
case rigid
case veryLight
case click05
case click06
}
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
private final class HapticFeedbackImpl {
private lazy var impactGenerator: [ImpactHapticFeedbackStyle : UIImpactFeedbackGenerator] = {
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
return [.light: UIImpactFeedbackGenerator(style: .light),
.medium: UIImpactFeedbackGenerator(style: .medium),
.heavy: UIImpactFeedbackGenerator(style: .heavy),
.soft: UIImpactFeedbackGenerator(style: .soft),
.rigid: UIImpactFeedbackGenerator(style: .rigid),
.veryLight: UIImpactFeedbackGenerator(),
.click05: UIImpactFeedbackGenerator(),
.click06: UIImpactFeedbackGenerator()]
} else {
return [.light: UIImpactFeedbackGenerator(style: .light),
.medium: UIImpactFeedbackGenerator(style: .medium),
.heavy: UIImpactFeedbackGenerator(style: .heavy)]
}
}()
private lazy var selectionGenerator: UISelectionFeedbackGenerator? = {
return UISelectionFeedbackGenerator()
}()
private lazy var notificationGenerator: UINotificationFeedbackGenerator? = {
return UINotificationFeedbackGenerator()
}()
func prepareTap() {
if let selectionGenerator = self.selectionGenerator {
selectionGenerator.prepare()
}
}
func tap() {
if let selectionGenerator = self.selectionGenerator {
selectionGenerator.selectionChanged()
}
}
func prepareImpact(_ style: ImpactHapticFeedbackStyle) {
if let impactGenerator = self.impactGenerator[style] {
impactGenerator.prepare()
}
}
func impact(_ style: ImpactHapticFeedbackStyle) {
if let impactGenerator = self.impactGenerator[style] {
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
switch style {
case .click05:
impactGenerator.impactOccurred(intensity: 0.3)
case .click06:
impactGenerator.impactOccurred(intensity: 0.4)
case .veryLight:
impactGenerator.impactOccurred(intensity: 0.3)
default:
impactGenerator.impactOccurred()
}
} else {
impactGenerator.impactOccurred()
}
}
}
func success() {
if let notificationGenerator = self.notificationGenerator {
notificationGenerator.notificationOccurred(.success)
} else {
AudioServicesPlaySystemSound(1520)
}
}
func prepareError() {
if let notificationGenerator = self.notificationGenerator {
notificationGenerator.prepare()
}
}
func error() {
if let notificationGenerator = self.notificationGenerator {
notificationGenerator.notificationOccurred(.error)
} else {
AudioServicesPlaySystemSound(1521)
}
}
func warning() {
AudioServicesPlaySystemSound(1102)
// if let notificationGenerator = self.notificationGenerator {
// notificationGenerator.notificationOccurred(.warning)
// } else {
//
// }
}
@objc dynamic func f() {
}
}
public final class HapticFeedback {
private var impl: AnyObject?
public init() {
}
deinit {
let impl = self.impl
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0, execute: {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
if let impl = impl as? HapticFeedbackImpl {
impl.f()
}
}
})
}
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
private func withImpl(_ f: (HapticFeedbackImpl) -> Void) {
if self.impl == nil {
self.impl = HapticFeedbackImpl()
}
f(self.impl as! HapticFeedbackImpl)
}
public func prepareTap() {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.withImpl { impl in
impl.prepareTap()
}
}
}
public func tap() {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.withImpl { impl in
impl.tap()
}
}
}
public func prepareImpact(_ style: ImpactHapticFeedbackStyle = .medium) {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.withImpl { impl in
impl.prepareImpact(style)
}
}
}
public func impact(_ style: ImpactHapticFeedbackStyle = .medium) {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.withImpl { impl in
impl.impact(style)
}
}
}
public func success() {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.withImpl { impl in
impl.success()
}
}
}
public func prepareError() {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.withImpl { impl in
impl.prepareError()
}
}
}
public func error() {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.withImpl { impl in
impl.error()
}
}
}
public func warning() {
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
self.withImpl { impl in
impl.warning()
}
}
}
}
@available(iOS 13.0, *)
public final class ContinuousHaptic {
private let engine: CHHapticEngine
private let player: CHHapticPatternPlayer
public init(duration: Double) throws {
self.engine = try CHHapticEngine()
var events: [CHHapticEvent] = []
for i in 0 ... 10 {
let t = CGFloat(i) / 10.0
let intensity = CHHapticEventParameter(parameterID: .hapticIntensity, value: Float((1.0 - t) * 0.1 + t * 1.0))
let sharpness = CHHapticEventParameter(parameterID: .hapticSharpness, value: 0.3)
let eventDuration: Double
if i == 10 {
eventDuration = 100.0
} else {
eventDuration = duration
}
let event = CHHapticEvent(eventType: .hapticContinuous, parameters: [intensity, sharpness], relativeTime: Double(i) / 10.0 * duration, duration: eventDuration)
events.append(event)
}
let pattern = try CHHapticPattern(events: events, parameters: [])
self.player = try self.engine.makePlayer(with: pattern)
try self.engine.start()
try self.player.start(atTime: 0)
}
deinit {
self.engine.stop(completionHandler: nil)
}
}