docs:Plan + Design 扩展 Phase 1.14 为 5 子项(含 AppIcon / LaunchScreen / progress / splash UI)
原 Phase 1.14 仅含"WebContainer 16:9 letterbox + 串接整链路",
漏掉了用户实际能看到的三件事:AppIcon / LaunchScreen 启动图 / zip
下载进度条。msext 上线时这三件都有,新外壳上线必须对齐。按用户偏好
方案 A 合并到 Phase 1.14,避免做两遍 WebContainer。
- Plan §5 Phase 1.14 拆为 1.14.a–e 五个子项
- 1.14.a AppIcon:拷贝 docs/res/Images.xcassets/AppIcon-1.appiconset
(9 个 png + Contents.json) 到 ylgamehall/Assets.xcassets
- 1.14.b LaunchScreen:Default-568h@2x~iphone.png → SplashImage
imageset;改写 LaunchScreen.storyboard 横屏铺满 UIImageView
- 1.14.c LobbyZipUpgrader 加 onProgress 回调(@Sendable (Double) -> Void)
- 1.14.d WebContainerViewController:splash 覆盖层(启动图 +
UIProgressView + 状态文字)+ 16:9 letterbox + 启动流水线串接
- 1.14.e webView didFinish 淡出 splash(0.3s 动画 + removeFromSuperview)
- Plan §8 进度追踪同步:5 个子项独立勾选条目
- Design §6.3.5 WebContainer 调用串完整重写:
- Splash UI 6 状态切换表(拼命启动中... / 拉取配置中... /
下载更新中 XX% / 加载大厅... / 淡出)
- runBootPipeline 完整 swift 骨架:含 .shortText 短文本响应处理 /
showmessage 阻塞处理 / IPA 升级 alert / zip 升级 onProgress 回调
- 所有失败 / 阻塞终态都用 UIAlertController(非 splash 文字):
短文本 / showmessage / IPA 升级 / 网络错误 / zip 下载失败
各场景的按钮文案 + tap 行为表
This commit is contained in:
@@ -287,12 +287,21 @@ Contract Design Plan(本文档)
|
||||
|
||||
##### 1.E 集成 + 烟雾测试
|
||||
|
||||
- [ ] **1.14** 实现 `Source/WebView/WebContainerViewController.swift`(基类)
|
||||
- [ ] **1.14.a** AppIcon:从 `docs/res/Images.xcassets/AppIcon-1.appiconset` 拷贝 9 个 png + Contents.json 到 `ylgamehall/Assets.xcassets/AppIcon.appiconset`,替换 Xcode 默认空模板。验收:Home Screen 显示真实图标
|
||||
- [ ] **1.14.b** LaunchScreen:把 `docs/res/Res/Default-568h@2x~iphone.png` 拷到 `ylgamehall/Assets.xcassets/SplashImage.imageset`;改写 `LaunchScreen.storyboard` 为单一 UIImageView 横屏铺满(contentMode = .scaleAspectFill,背景 .black)。验收:启动瞬间看到启动图而非黑屏
|
||||
- [ ] **1.14.c** `LobbyZipUpgrader` 加 progress 回调
|
||||
- `upgradeIfNeeded(resolved:onProgress:) async throws -> Outcome`,`onProgress: @Sendable (Double) -> Void` 报告 0.0…1.0
|
||||
- 用 `URLSession.download` 配 `URLSessionTaskDelegate.urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)` 上报
|
||||
- [ ] **1.14.d** 实现 `Source/WebView/WebContainerViewController.swift`(基类)
|
||||
- 持有 `BridgedWebView` + `BridgeBus`
|
||||
- 16:9 比例布局:屏幕比 < 16:9 用宽度撑满上下黑边;屏幕比 > 16:9 用高度撑满左右黑边
|
||||
- `viewDidLoad` 内串:`ResourceUnzipper.ensureReady() → RemoteConfigClient.fetch → VersionResolver.resolve → LobbyZipUpgrader.upgradeIfNeeded → loadFileURL(lobbyIndex)`
|
||||
- **Splash 状态层**:与 LaunchScreen 同款启动图 + UIProgressView + 状态文字标签("拼命启动中..." / "下载更新中 XX%" / "网络异常重试中")
|
||||
- `viewDidLoad` 内串:`ResourceUnzipper.ensureReady() → RemoteConfigClient.fetch → VersionResolver.resolve → LobbyZipUpgrader.upgradeIfNeeded(onProgress: 更新 UIProgressView) → loadFileURL(lobbyIndex)`
|
||||
- 无升级路径走快路(< 200 ms),有升级路径显示 progress UI
|
||||
- 实现 `webViewWebContentProcessDidTerminate:` 退避 reload(Design §3.6)
|
||||
- [ ] **1.14.e** WebView 加载完成后淡出 splash
|
||||
- `webView(_:didFinish:)` 内 `UIView.animate(duration: 0.3)` splash.alpha = 0 → `removeFromSuperview`
|
||||
- 期间 webView 已渲染好 H5 大厅,无白屏闪烁
|
||||
- [ ] **1.15** 实现 `Source/Bridge/Handlers/VibratorHandler.swift`
|
||||
- `register(_ bridge)` 注册 `vibrator` handler → `AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)` + responseCallback `"vibrator"`
|
||||
- [ ] **1.16** `SceneDelegate` → `WebContainerViewController` 作 rootViewController
|
||||
@@ -785,7 +794,11 @@ H5 调 `OpenurlTitleData` 打开弹层 WebView,弹层内 H5 用 `window.settin
|
||||
- [x] 1.11 VersionResolver(chulishengji 双子树合并算法,纯函数)
|
||||
- [x] 1.12 LocalVersionReader(version.xml 解析 + ATS 全局放行)
|
||||
- [x] 1.13 LobbyZipUpgrader(actor,原子 rename 升级;端到端实测 260→261)
|
||||
- [ ] 1.14 WebContainerViewController(16:9 letterbox + 串接整链路)
|
||||
- [ ] 1.14.a AppIcon 替换 Xcode 默认空模板
|
||||
- [ ] 1.14.b LaunchScreen 改启动图(Default-568h@2x 横屏铺满)
|
||||
- [ ] 1.14.c LobbyZipUpgrader 加 progress 回调
|
||||
- [ ] 1.14.d WebContainerViewController(含 splash UI 状态层 + 16:9 letterbox + 启动流水线串接)
|
||||
- [ ] 1.14.e WebView didFinish 后淡出 splash
|
||||
- [ ] 1.15 VibratorHandler
|
||||
- [ ] 1.16 SceneDelegate → WebContainerViewController
|
||||
- [ ] 1.17 Demo H5 联调 + 真机验证升级路径
|
||||
|
||||
@@ -1236,36 +1236,118 @@ public actor LobbyZipUpgrader {
|
||||
}
|
||||
```
|
||||
|
||||
#### 6.3.5 WebContainer 调用串
|
||||
#### 6.3.5 WebContainer 调用串 + Splash UI 状态机(Phase 1.14)
|
||||
|
||||
WebContainerViewController 同时承担两层:
|
||||
- **下层**:BridgedWebView 嵌入 + 16:9 letterbox(屏幕比 < 16:9 上下黑边;> 16:9 左右黑边)
|
||||
- **上层(Splash 覆盖)**:与 LaunchScreen.storyboard 同款启动图(横屏铺满)+ UIProgressView + 状态文字标签
|
||||
- 启动瞬间:LaunchScreen 静态图(iOS 系统级,0 延迟)
|
||||
- viewDidLoad:WebContainer 把自己的 Splash 覆盖在 BridgedWebView 之上(两者 alpha 0/1 由状态切换)
|
||||
- 启动流水线推进时:updateSplash(text: ..., progress: ...) 更新文字 + 进度条
|
||||
- WKWebView didFinish 后:UIView.animate(duration: 0.3) splash.alpha = 0 → removeFromSuperview
|
||||
|
||||
```swift
|
||||
// Source/WebView/WebContainerViewController.swift(Phase 1.14)
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .black
|
||||
|
||||
setupBridgedWebView() // 16:9 letterbox 嵌入
|
||||
setupSplashOverlay() // 启动图 + 进度条 + 文字标签
|
||||
|
||||
bridgedWebView.webView.navigationDelegate = self
|
||||
|
||||
Task { @MainActor in
|
||||
try await ResourceUnzipper.shared.ensureReady() // 首装解压 Bundle 内 zip
|
||||
let config = try await RemoteConfigClient.shared.fetch()
|
||||
let resolved = VersionResolver.resolve(
|
||||
config: config,
|
||||
agentId: BundleConfig.shared.agent,
|
||||
channelId: BundleConfig.shared.channel,
|
||||
marketId: BundleConfig.shared.market,
|
||||
gameId: BundleConfig.shared.gameId
|
||||
)
|
||||
if resolved.appVersion > LocalVersionReader.localAppVersion {
|
||||
// 弹窗 + Safari 外链 → resolved.appDownload
|
||||
return showAppUpgradeAlert(resolved.appDownload)
|
||||
await runBootPipeline()
|
||||
}
|
||||
_ = try await LobbyZipUpgrader.shared.upgradeIfNeeded(
|
||||
remoteGameVersion: resolved.gameVersion,
|
||||
remoteGameZip: resolved.gameZip
|
||||
}
|
||||
|
||||
private func runBootPipeline() async {
|
||||
do {
|
||||
updateSplash(text: "拼命启动中...", progress: nil)
|
||||
try await ResourceUnzipper.shared.ensureReady()
|
||||
|
||||
updateSplash(text: "拉取配置中...", progress: nil)
|
||||
let outcome = try await RemoteConfigClient.shared.fetch()
|
||||
|
||||
switch outcome {
|
||||
case .shortText(let msg):
|
||||
// 运营杀手锏 #1:弹 alert,App 永停(msext NewRootVC.m:1239-1243 契约)
|
||||
showBlockingAlert(msg)
|
||||
return
|
||||
case .parsed(let cfg):
|
||||
let bc = BundleConfig.shared
|
||||
let resolved = VersionResolver.resolve(
|
||||
config: cfg,
|
||||
agentId: bc.agent,
|
||||
channelId: bc.channel,
|
||||
marketId: bc.market,
|
||||
gameId: bc.gameId
|
||||
)
|
||||
|
||||
// 运营杀手锏 #2:showmessage 非空 → 弹 alert + 完全阻塞
|
||||
if let msg = resolved.showmessage, !msg.isEmpty {
|
||||
showBlockingAlert(msg)
|
||||
return
|
||||
}
|
||||
|
||||
// IPA 升级:弹窗 + Safari 外链(msext NewRootVC.m:1510-1520)
|
||||
if resolved.appVersion > LocalVersionReader.localAppVersion,
|
||||
let dl = resolved.appDownload {
|
||||
showIPAUpgradeAlert(dl)
|
||||
return
|
||||
}
|
||||
|
||||
// H5 zip 升级:进度条实时更新
|
||||
_ = try await LobbyZipUpgrader.shared.upgradeIfNeeded(
|
||||
resolved: resolved,
|
||||
onProgress: { @MainActor [weak self] p in
|
||||
self?.updateSplash(
|
||||
text: String(format: "下载更新中 %d%%", Int(p * 100)),
|
||||
progress: p
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
updateSplash(text: "加载大厅...", progress: nil)
|
||||
bridgedWebView.webView.loadFileURL(
|
||||
SandboxPaths.lobbyIndex,
|
||||
allowingReadAccessTo: SandboxPaths.lobbyRoot
|
||||
)
|
||||
// didFinish 回调里淡出 splash(见 WKNavigationDelegate 扩展)
|
||||
} catch {
|
||||
showFatalAlert(error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WKNavigationDelegate
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
UIView.animate(withDuration: 0.3, animations: { [weak self] in
|
||||
self?.splash.alpha = 0
|
||||
}, completion: { [weak self] _ in
|
||||
self?.splash.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**Splash UI 状态切换表**:
|
||||
|
||||
| 阶段 | 文字 | progress | 持续时长(典型) |
|
||||
|------|------|----------|---------------|
|
||||
| viewDidLoad 起 | "拼命启动中..." | 隐藏 | ~50 ms |
|
||||
| 首装解压 zip 中 | "拼命启动中..." | 隐藏 | ~50-500 ms |
|
||||
| 拉远端配置中 | "拉取配置中..." | 隐藏 | ~500-2000 ms |
|
||||
| H5 zip 下载中 | "下载更新中 XX%" | 0.0-1.0 | ~1000-3000 ms |
|
||||
| WebView 加载 H5 中 | "加载大厅..." | 隐藏 | ~500-1000 ms |
|
||||
| WebView didFinish | (淡出消失)| - | 300 ms 动画 |
|
||||
|
||||
**所有失败 / 阻塞终态都用 UIAlertController(非 splash 文字)**:
|
||||
- 短文本响应:弹 alert,标题 `gamehallname + 提醒`,单按钮"确定",无 tap 处理(永停)
|
||||
- showmessage 非空:同上
|
||||
- IPA 升级:弹 alert,单按钮"确定"→ openURL(appDownload),永停
|
||||
- 网络错误 / 解析失败:弹 alert,单按钮"重试",点击重新 runBootPipeline()
|
||||
- zip 下载失败:弹 alert,单按钮"重试" + 单按钮"使用旧版本"(回退用 IPA 内 Bundle zip)
|
||||
```
|
||||
|
||||
#### 6.3.6 与 msext 历史实现的差异(不要照抄的部分)
|
||||
|
||||
Reference in New Issue
Block a user