85 lines
5.1 KiB
Markdown
85 lines
5.1 KiB
Markdown
|
|
# 完成页迭代方案 — 审查报告(禁止应用)
|
|||
|
|
|
|||
|
|
**审查对象**:左滑进完成页、右滑回最后一节;无顶部返回;无打字机金句;底部「回到我的内容」回到技能页 Tab。
|
|||
|
|
**结论**:只做审查与完整代码输出,不修改仓库文件。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 一、需求与方案对照
|
|||
|
|
|
|||
|
|
| 需求 | 方案 | 审查结论 |
|
|||
|
|
|------|------|----------|
|
|||
|
|
| 不想要点开页面导航栏的返回按钮 | 移除顶部导航栏和返回按钮,仅顶部留白 | ✅ 一致 |
|
|||
|
|
| 右滑回到最后一个小节 | `.enableSwipeBack()` + `SwipeBackEnabler` 强制开启侧滑返回 | ⚠️ 见下文「侧滑返回」 |
|
|||
|
|
| 最后小节左滑进完成页,右滑回最后小节 | 播放器最后一节挂 `LastPageSwipeModifier` 左滑 push;完成页右滑 = 系统 pop | ✅ 一致 |
|
|||
|
|
| 不要打字机金句 | 移除打字机区域 | ✅ 一致 |
|
|||
|
|
| 底部「回到我的内容」回到技能页 Tab | 方案写的是 `navigationPath = NavigationPath()` | ❌ **逻辑错误**,见下 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 二、关键问题:底部按钮语义
|
|||
|
|
|
|||
|
|
**需求**:底部「回到我的内容」要回到**技能页 Tab**(即技能 Tab 根:课程列表)。
|
|||
|
|
|
|||
|
|
**方案中的实现**:`handleReturnToRoot()` 里写的是 `navigationPath = NavigationPath()`,即清空**当前传入的 path**。
|
|||
|
|
|
|||
|
|
- 若从**技能 Tab**进入:传的是 `growthPath`,清空后 = 技能 Tab 根 ✅
|
|||
|
|
- 若从**发现 Tab**进入:传的是 `homePath`,清空后 = **发现 Tab 根**,不会切到技能 Tab ❌
|
|||
|
|
- 若从**我的 Tab**进入:传的是 `profilePath`,清空后 = **我的 Tab 根**,不会切到技能 Tab ❌
|
|||
|
|
|
|||
|
|
因此:**仅清空当前 path 无法满足「无论从哪个 Tab 进,都回到技能页 Tab」**。
|
|||
|
|
|
|||
|
|
**正确做法**:底部按钮应调用 **`navStore.switchToGrowthTab()`**(切到技能 Tab + 清空 `growthPath`),与当前线上 CompletionView 的「继续学习」一致。
|
|||
|
|
- CompletionView 需保留 **`@EnvironmentObject var navStore`**
|
|||
|
|
- 不需要为「回到我的内容」传入 **`@Binding var navigationPath`**(调用方无需改传参)
|
|||
|
|
|
|||
|
|
**完整代码中已按此修正**:底部按钮调用 `navStore.switchToGrowthTab()`,不传 `navigationPath`。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 三、侧滑返回(SwipeBackEnabler)
|
|||
|
|
|
|||
|
|
- 方案用 **`SwipeBackEnabler`**(`UIViewControllerRepresentable`)在 `.background` 里查找 `navigationController` 并打开 `interactivePopGestureRecognizer`,以在隐藏导航栏时恢复右滑返回。
|
|||
|
|
- **风险**:`makeUIViewController` 返回的是一颗裸 `UIViewController()`,其 `navigationController` 在部分时机可能仍为 nil(例如尚未挂到 NavigationStack 上),`DispatchQueue.main.async` 能缓解但无法完全保证。若遇真机偶发无效,可考虑:
|
|||
|
|
- 在 `updateUIViewController` 中轮询/延迟再取一次 `navigationController`,或
|
|||
|
|
- 使用 `UINavigationController` 子类 / 注入方式保证拿到的必为当前栈。
|
|||
|
|
- **结论**:实现可保留,建议在真机多场景(从三个 Tab 进入完成页后右滑)验证;若失效再加强时机或注入方式。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 四、VerticalScreenPlayerView 与占位页
|
|||
|
|
|
|||
|
|
- 方案采用「**不使用占位页**,仅最后一节左滑手势 push 完成页」:TabView 只渲染 `ForEach(allCourseNodes)`,不再多一页 `CompletionPlaceholderPage`。
|
|||
|
|
- **影响**:
|
|||
|
|
- 进入完成页**仅剩**「在最后一节左滑」这一种方式;「滑到下一空白页再进完成页」的路径被移除。
|
|||
|
|
- 从完成页右滑或 dismiss 后,播放器不再存在「当前是占位页、需在 onAppear 里切回最后一节」的状态,可删除 `onAppear` 里对 `currentNodeId == "wg://completion"` 的处理。
|
|||
|
|
- **与现有调用方**:NoteTreeView / NoteListView 不传 `navigationPath`,`triggerCompletionNavigation` 里需 `guard let path = navigationPath else { return }`,从笔记进的播放器仍不会 push 完成页,行为不变。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 五、CompletionView 视觉与接口
|
|||
|
|
|
|||
|
|
- **视觉**:从当前 3D 翻转卡片 + 打字机,改为「赛博印章」:圆环 + 未盖章「完」/ 盖章后「已完成的第 N 节」+ 底部「回到我的内容」。
|
|||
|
|
- **接口**:
|
|||
|
|
- 保留:`courseId`, `courseTitle`, `completedLessonCount`。
|
|||
|
|
- 不增加 `@Binding var navigationPath`(底部用 `navStore.switchToGrowthTab()`)。
|
|||
|
|
- 保留 `@EnvironmentObject var navStore`。
|
|||
|
|
- **调用方**:GrowthView / ProfileView / DiscoveryView 的 `.completion` 分支**无需**新增参数,仍为三参构造。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 六、审查结论汇总
|
|||
|
|
|
|||
|
|
| 项 | 结论 |
|
|||
|
|
|----|------|
|
|||
|
|
| 顶部去掉返回按钮 | ✅ 方案正确 |
|
|||
|
|
| 右滑回最后一节 | ✅ 思路正确;SwipeBackEnabler 需真机验证,偶发需加强时机 |
|
|||
|
|
| 最后小节左滑进完成页 | ✅ 保留 LastPageSwipeModifier 即可 |
|
|||
|
|
| 去掉打字机金句 | ✅ 方案正确 |
|
|||
|
|
| 底部回到技能页 Tab | ❌ 方案用「清空当前 path」会错;应用 `navStore.switchToGrowthTab()`,完整代码已改 |
|
|||
|
|
| 调用方改动 | ✅ CompletionView 不需新参数;VerticalScreenPlayerView 保持可选 `navigationPath`,笔记流不受影响 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**完整代码见下(仅作交付,不写入仓库)。**
|