diff --git a/ylgamehall/Source/WebView/BridgedWebView.swift b/ylgamehall/Source/WebView/BridgedWebView.swift new file mode 100644 index 0000000..13d35ce --- /dev/null +++ b/ylgamehall/Source/WebView/BridgedWebView.swift @@ -0,0 +1,89 @@ +// +// BridgedWebView.swift +// ylgamehall +// +// WKWebView + WebViewJavascriptBridge.js 注入 + BridgeBus 一体封装。 +// 详见 docs/H5-Native-Implementation-Design.md §3.3 / 契约 §4.1。 +// + +import UIKit +import WebKit + +/// 内嵌 H5 的 WebView 组件,已挂好桥与必要配置。WebContainerViewController(Phase 1.10) +/// 通过把本 view 嵌入自己的内容区,再加上 16:9 letterbox / 生命周期管理。 +@MainActor +public final class BridgedWebView: UIView { + + public let webView: WKWebView + public let bridge: BridgeBus + + public init() { + // ── WKWebViewConfiguration(契约 §4.1)───────────────── + let configuration = WKWebViewConfiguration() + configuration.defaultWebpagePreferences.allowsContentJavaScript = true + configuration.preferences.javaScriptCanOpenWindowsAutomatically = false + configuration.preferences.minimumFontSize = 10 + configuration.allowsInlineMediaPlayback = true + configuration.processPool = SharedProcessPool.shared + + // ── 在 documentStart 注入 WebViewJavascriptBridge.js ───── + // .atDocumentStart 保证 H5 业务代码运行时 window.WebViewJavascriptBridge 已就绪 + if let url = Bundle.main.url(forResource: "WebViewJavascriptBridge", + withExtension: "js"), + let source = try? String(contentsOf: url, encoding: .utf8) { + let userScript = WKUserScript( + source: source, + injectionTime: .atDocumentStart, + forMainFrameOnly: true + ) + configuration.userContentController.addUserScript(userScript) + } else { + // 编译期保证文件存在;运行时若缺,留 print 便于排查 + print("[BridgedWebView] ERROR: WebViewJavascriptBridge.js 未在 Bundle 找到") + } + + // ── 创建 WKWebView + BridgeBus ───────────────────────── + let webView = WKWebView(frame: .zero, configuration: configuration) + let bridge = BridgeBus(webView: webView, + controller: configuration.userContentController) + + self.webView = webView + self.bridge = bridge + + super.init(frame: .zero) + + // ── ScrollView 配置(契约 §4.1,禁手势滚动 / 弹性 / 自动 inset)── + let scroll = webView.scrollView + scroll.bounces = false + scroll.isScrollEnabled = false // ⚠️ 漏则 H5 上下滑动失控(契约硬约束) + scroll.contentInsetAdjustmentBehavior = .never + scroll.showsVerticalScrollIndicator = false + scroll.showsHorizontalScrollIndicator = false + + // ── 布局:WebView 撑满 self ───────────────────────────── + webView.translatesAutoresizingMaskIntoConstraints = false + addSubview(webView) + NSLayoutConstraint.activate([ + webView.topAnchor.constraint(equalTo: topAnchor), + webView.bottomAnchor.constraint(equalTo: bottomAnchor), + webView.leadingAnchor.constraint(equalTo: leadingAnchor), + webView.trailingAnchor.constraint(equalTo: trailingAnchor) + ]) + + backgroundColor = .black + } + + @available(*, unavailable) + public required init?(coder: NSCoder) { + fatalError("BridgedWebView does not support init(coder:)") + } +} + +// MARK: - 跨 WebView 共享的进程池 + +/// 大厅 / 子游戏 / 弹层共用同一 WKProcessPool,共享 Cookie / 资源缓存, +/// 启动加速。详见 Design §3.5。 +@MainActor +public enum SharedProcessPool { + public static let shared = WKProcessPool() +}