import UIKit final class PeriodBloodFlowCoordinator { enum State { case idle, requiredSheet, animating, analysis, complete } private var state: State = .idle private var calendarLocked = false private let baseFillDuration: TimeInterval = 0.125 private let durationUnit: TimeInterval = 0.0053 private let previewHold: TimeInterval = 0.115 private let overlapRatio: Double = 0.35 private let minStaggerGap: TimeInterval = 0.118 private let settleHold: TimeInterval = 0.170 private let settleDuration: TimeInterval = 0.360 func didTapYes(periodRange: PeriodRange) { UIImpactFeedbackGenerator(style: .heavy).impactOccurred() // 先展示白底中间态和黄色开始 icon;血流等弹窗确定后再播放。 showWhitePreviewState(periodRange: periodRange) showStartIcon(periodRange: periodRange) state = .requiredSheet showRequiredPeriodSheet( onConfirm: { [weak self] in self?.confirmRequiredSheet(periodRange: periodRange) }, onCancel: { [weak self] in self?.cancelRequiredSheet() } ) } func didTapNo() { // 点击否不额外调用强震感,仅保留系统控件默认反馈。 cancelPendingFlow() restoreUnrecordedState() state = .idle } private func confirmRequiredSheet(periodRange: PeriodRange) { hideRequiredPeriodSheet() expandTodayRecordList() calendarLocked = true state = .animating playBloodFlow(periodRange: periodRange) { [weak self] in self?.state = .analysis self?.showAnalysisBubble { self?.state = .complete self?.calendarLocked = false } } } private func cancelRequiredSheet() { hideRequiredPeriodSheet() restoreUnrecordedState() state = .idle } func didTapCalendarDate(_ date: Date) { guard !calendarLocked else { return } selectDate(date) } private func duration(for level: Double) -> TimeInterval { baseFillDuration + level * durationUnit } } struct PeriodRange { let dayCount: Int }