mycode/myApp/Keging/IOS/Haptics.swift
songyc macbook eba01b570f fix(keging): 실기기 2차 피드백 — 중단 UI 인라인화·통계 위로 스와이프·진동 3종 Core Haptics 재설계
① 중단 확인: 하단 시트(confirmationDialog) → 버튼 자리에서 펼쳐지는 인라인 확인
   (안내 문구 + 계속하기(그린)/중단하기(레드), scale+opacity 전환)
② 통계: 아래→위 스와이프로 반전, 하단 중앙 손잡이(위 화살표+캡슐), 통통 튀는 스프링
   (dampingFraction 0.62), 통계 상단에 닫기 손잡이 추가, 닫기=아래로 스와이프
③ 진동 3종(기본·톡톡·지이이잉)으로 재설계 — 체이닝 간격도 길다는 피드백:
   - iOS 포그라운드: Core Haptics 정밀 재생(Shared/HapticEnginePlayer) — 기본 0.25s,
     톡톡 0.14s×2 시작 간격 0.28s(요구 0.5s 미만), 지이이잉 1.2s 연속, 세기 1.0
   - iOS 백그라운드 폴백: 시스템 바이브 완료 체이닝(1방/2방/3방 즉시 잇기)
   - 워치: watchOS SDK에 CoreHaptics 부재(실측) — 강한 시스템 햅틱 매핑
     (기본=.notification, 톡톡=.directionUp, 지이이잉=.retry — 내장이라 간격 뭉개짐 없음)
   - 구버전 저장 패턴 자동 이관, 기본값 수축=지이이잉·이완=기본
- 신규 QA 인자 -confirmStop, 도움말 문구·번역(en/ja) 갱신, stale 정리(카탈로그 0/0)
- 검증: Debug·Release·워치 빌드 그린, 화면 QA(메인 하단 손잡이·통계 시트·중단 인라인 레드)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UPmp29DQHY6Ahfss5SRopT
2026-08-30 09:39:37 +09:00

46 lines
1.7 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Haptics.swift
// Keging
//
// (iOS).
// - : Core Haptics (HapticEnginePlayer) ·
// - : .
// ( ) .
// AudioServices , .
//
import AudioToolbox
import UIKit
@MainActor
enum Haptics {
///
private static var generation = 0
static func play(_ pattern: HapticPattern) {
generation += 1
if UIApplication.shared.applicationState == .active,
HapticEnginePlayer.shared.play(pattern) {
return
}
playSystemChain(pattern, generation: generation)
}
///
private static func playSystemChain(_ pattern: HapticPattern, generation: Int) {
let count = switch pattern {
case .basic: 1
case .doubleTap: 2
case .long: 3 //
}
playChain(remaining: count, generation: generation)
}
private static func playChain(remaining: Int, generation: Int) {
guard remaining > 0, generation == Self.generation else { return }
AudioServicesPlaySystemSoundWithCompletion(kSystemSoundID_Vibrate) { @MainActor @Sendable in
playChain(remaining: remaining - 1, generation: generation)
}
}
}