完整支撑 Phase 3.E prepareaudio handler 的录音子链路:用户按住 H5 麦克风按钮 → Native 录音 → 松手停止 → 触发完成回调。 ## 新增模块 - `RecordingAwareWindow`:UIWindow 子类,提供 touch 旁路监听通道 (static touchObserver closure)。默认行为与 UIWindow 完全一致,仅 RecordingPresenter 注册 observer 后才触发额外行为。daoqi msext CustomWindow.m + nScreenTouch 通知同款思路,改用 closure 注入 - `VoiceRecorder`:AVAudioRecorder + AudioSession + 0.1s 计时器封装。 ObservableObject + @Published(暴露 averagePower / elapsed 给 SwiftUI)。 录音参数 8kHz/16bit/mono PCM 严格对齐 daoqi(AMR-NB 编码硬要求)。 60s 上限对齐 daoqi kDefaultMaxRecordTime - `RecordingOverlay`:SwiftUI 浮层(UI 内部实现,按 CLAUDE.md「原则 B 原生 内部自由」与 daoqi 不一致)。半透明黑底胶囊 + SF Symbols 麦克风 + 音量 光晕动画 + 倒计时(剩 10s 显示秒数,对齐 daoqi UI 逻辑) - `RecordingPresenter`:组装入口。挂载 overlay 到 hostVC 全屏覆盖 + 注册 touchObserver。提供 `start(on:fileName:onFinish:)` 单一 API + `cancelActive()` 强制取消 ## SceneDelegate 改动 UIWindow → RecordingAwareWindow 实例化。变量类型仍为 UIWindow? (多态),sendEvent 走子类版本。零行为变化(observer 未注册时)。 ## 为何必须走 UIWindow 子类 H5 麦克风按钮按下触发 prepareaudio → 录音浮层在按住状态下中途弹出。 iOS touch 事件流是 view hit-test 链,**in-flight touch 不传给中途加入 的 view**,浮层 view 自身收不到 touchesEnded,用户松手时检测不到。 唯一可靠路径是 UIWindow.sendEvent 旁路截获(UIWindow 接收所有 touch event 无论 in-flight 还是新 touch)。daoqi msext 也是这条路。 ## Combine 引入 VoiceRecorder 用 ObservableObject + @Published 给 SwiftUI 数据绑定, 需要 import Combine。CLAUDE.md「避免 Combine」精神是禁用 Publisher 链 替代 async/await,不是禁止 ObservableObject(iOS 15.6 无 @Observable 宏可替代)。已在 VoiceRecorder.swift 注释说明。 ## 验证 BuildProject 通过 0 错误,全部新文件 XcodeRefreshCodeIssuesInFile 0 诊断。
46 lines
1.9 KiB
Swift
46 lines
1.9 KiB
Swift
//
|
||
// SceneDelegate.swift
|
||
// ylgamehall
|
||
//
|
||
|
||
import UIKit
|
||
|
||
final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||
|
||
var window: UIWindow?
|
||
|
||
func scene(_ scene: UIScene,
|
||
willConnectTo session: UISceneSession,
|
||
options connectionOptions: UIScene.ConnectionOptions) {
|
||
guard let windowScene = scene as? UIWindowScene else { return }
|
||
// 使用 RecordingAwareWindow(UIWindow 子类)以便录音浮层旁路监听 touch
|
||
// 事件。默认行为与 UIWindow 完全一致,仅在 RecordingPresenter 注册
|
||
// touchObserver 后才有额外行为。详见 Source/Audio/RecordingAwareWindow.swift
|
||
let window = RecordingAwareWindow(windowScene: windowScene)
|
||
|
||
// 用 UINavigationController 承载大厅,便于 Phase 6 push 子游戏 / Phase 7 push 弹层。
|
||
// 栈深约束 ≤ 3(Design §2.4.3),由 AppCoordinator 在 push 处守门。
|
||
let lobby = WebContainerViewController()
|
||
let nav = UINavigationController(rootViewController: lobby)
|
||
nav.setNavigationBarHidden(true, animated: false)
|
||
nav.interactivePopGestureRecognizer?.isEnabled = false
|
||
|
||
AppCoordinator.shared.navigationController = nav
|
||
|
||
window.rootViewController = nav
|
||
self.window = window
|
||
window.makeKeyAndVisible()
|
||
}
|
||
|
||
/// 第三方 SDK 回调入口(微信授权 / 微信分享回包等)。
|
||
/// 顺序硬约束:QQ → 微信(msext AppDelegate.m 同款);QQ 走 URL Scheme 不依赖 SDK,
|
||
/// 这里直接调微信 handler — 未来若 QQ 升级为 SDK 路径则在前面插一条。
|
||
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
||
for ctx in URLContexts {
|
||
// Phase 4.E 微信回调(canImport 守卫:未链接时 no-op)
|
||
if WeChatSDK.handleOpenURL(ctx.url) { continue }
|
||
// 未来:抖音 / 其它 SDK 回调依次判
|
||
}
|
||
}
|
||
}
|