Integrate CYLTabBar center scan button and unify UIKit navigation.
Add CYLTabBarController with a global scan entry, promote MainTabBarController to the window root after login, replace RouterPath-based navigation with AppNavigator, and fix Profile diffable cell reconfiguration crashes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -7,12 +7,11 @@ import SnapKit
|
||||
import UIKit
|
||||
|
||||
@MainActor
|
||||
/// 应用根 ViewController,根据登录阶段切换子页面并挂载全局 Overlay。
|
||||
/// 应用根协调器,监听登录阶段并在 window 根层级切换登录页与 `MainTabBarController`。
|
||||
final class RootViewController: UIViewController {
|
||||
|
||||
private let services: AppServices
|
||||
private var currentChild: UIViewController?
|
||||
private let restoringView = UIView()
|
||||
private var currentAuthChild: UIViewController?
|
||||
|
||||
/// 初始化实例。
|
||||
init(services: AppServices = .shared) {
|
||||
@ -29,21 +28,14 @@ final class RootViewController: UIViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.backgroundColor = .systemBackground
|
||||
restoringView.backgroundColor = .systemBackground
|
||||
|
||||
services.appSession.onChange = { [weak self] in
|
||||
self?.handleSessionPhaseChange()
|
||||
}
|
||||
|
||||
mountChild(for: services.appSession.phase)
|
||||
applyRoot(for: services.appSession.phase)
|
||||
bootstrapSession()
|
||||
configurePushNotifications()
|
||||
}
|
||||
|
||||
/// 窗口展示后将 Toast 与全局 Loading 挂载到 window 层级。
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
attachGlobalOverlays()
|
||||
services.configurePushNotifications()
|
||||
}
|
||||
|
||||
/// 冷启动时恢复本地会话,并在已登录时申请推送权限。
|
||||
@ -66,21 +58,9 @@ final class RootViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 配置推送管理器与路由跳转回调。
|
||||
private func configurePushNotifications() {
|
||||
services.configurePushNotifications()
|
||||
}
|
||||
|
||||
/// 将全局 Toast 与 Loading 视图附加到当前 window。
|
||||
private func attachGlobalOverlays() {
|
||||
guard let window = view.window else { return }
|
||||
services.toastCenter.attachToWindow(window)
|
||||
services.globalLoading.attachToWindow(window)
|
||||
}
|
||||
|
||||
/// 登录阶段变化时切换子页面,并在登出时清理各上下文状态。
|
||||
/// 登录阶段变化时切换 window 根控制器,并在登出时清理各上下文状态。
|
||||
private func handleSessionPhaseChange() {
|
||||
mountChild(for: services.appSession.phase)
|
||||
applyRoot(for: services.appSession.phase)
|
||||
|
||||
switch services.appSession.phase {
|
||||
case .loggedIn:
|
||||
@ -93,7 +73,8 @@ final class RootViewController: UIViewController {
|
||||
services.accountContext.reset()
|
||||
services.permissionContext.reset()
|
||||
services.scenicSpotContext.reset()
|
||||
services.appRouter.reset()
|
||||
services.appNavigator.resetAllStacks()
|
||||
services.appNavigator.detach()
|
||||
services.toastCenter.dismiss()
|
||||
services.scenicQueueRuntime.stop()
|
||||
case .restoring:
|
||||
@ -113,31 +94,67 @@ final class RootViewController: UIViewController {
|
||||
)
|
||||
}
|
||||
|
||||
/// 按登录阶段挂载对应子控制器:登录页、恢复占位或主 Tab。
|
||||
private func mountChild(for phase: AuthPhase) {
|
||||
let nextChild: UIViewController
|
||||
/// 按登录阶段切换 window 根控制器。
|
||||
private func applyRoot(for phase: AuthPhase) {
|
||||
switch phase {
|
||||
case .loggedOut:
|
||||
nextChild = LoginViewController(services: services)
|
||||
case .restoring:
|
||||
nextChild = restoringViewController()
|
||||
case .loggedIn:
|
||||
nextChild = MainTabBarController(services: services)
|
||||
showMainTabBarAsWindowRoot()
|
||||
case .loggedOut:
|
||||
showAuthRoot(with: LoginViewController(services: services))
|
||||
case .restoring:
|
||||
showAuthRoot(with: restoringViewController())
|
||||
}
|
||||
}
|
||||
|
||||
/// 登录后将 `MainTabBarController` 设为 window 根控制器,满足 CYLTabBarController 挂载要求。
|
||||
private func showMainTabBarAsWindowRoot() {
|
||||
removeCurrentAuthChild()
|
||||
|
||||
guard let window = activeWindow else { return }
|
||||
|
||||
let mainTabBarController = MainTabBarController(services: services)
|
||||
window.rootViewController = mainTabBarController
|
||||
attachGlobalOverlays(to: window)
|
||||
}
|
||||
|
||||
/// 未登录或恢复阶段展示登录/占位页,由当前协调器承载。
|
||||
private func showAuthRoot(with child: UIViewController) {
|
||||
guard let window = activeWindow else {
|
||||
mountAuthChild(child)
|
||||
return
|
||||
}
|
||||
|
||||
guard currentChild !== nextChild else { return }
|
||||
if window.rootViewController !== self {
|
||||
window.rootViewController = self
|
||||
}
|
||||
|
||||
currentChild?.willMove(toParent: nil)
|
||||
currentChild?.view.removeFromSuperview()
|
||||
currentChild?.removeFromParent()
|
||||
mountAuthChild(child)
|
||||
attachGlobalOverlays(to: window)
|
||||
}
|
||||
|
||||
addChild(nextChild)
|
||||
view.addSubview(nextChild.view)
|
||||
nextChild.view.snp.makeConstraints { make in
|
||||
/// 将登录页或恢复占位页挂载为当前协调器的子控制器。
|
||||
private func mountAuthChild(_ child: UIViewController) {
|
||||
guard currentAuthChild !== child else { return }
|
||||
|
||||
currentAuthChild?.willMove(toParent: nil)
|
||||
currentAuthChild?.view.removeFromSuperview()
|
||||
currentAuthChild?.removeFromParent()
|
||||
|
||||
addChild(child)
|
||||
view.addSubview(child.view)
|
||||
child.view.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
nextChild.didMove(toParent: self)
|
||||
currentChild = nextChild
|
||||
child.didMove(toParent: self)
|
||||
currentAuthChild = child
|
||||
}
|
||||
|
||||
/// 移除当前认证阶段子控制器。
|
||||
private func removeCurrentAuthChild() {
|
||||
currentAuthChild?.willMove(toParent: nil)
|
||||
currentAuthChild?.view.removeFromSuperview()
|
||||
currentAuthChild?.removeFromParent()
|
||||
currentAuthChild = nil
|
||||
}
|
||||
|
||||
/// 会话恢复阶段的空白占位页,避免闪烁登录页。
|
||||
@ -146,4 +163,18 @@ final class RootViewController: UIViewController {
|
||||
controller.view.backgroundColor = .systemBackground
|
||||
return controller
|
||||
}
|
||||
|
||||
/// 将全局 Toast 与 Loading 视图附加到指定 window。
|
||||
private func attachGlobalOverlays(to window: UIWindow) {
|
||||
services.toastCenter.attachToWindow(window)
|
||||
services.globalLoading.attachToWindow(window)
|
||||
}
|
||||
|
||||
/// 获取当前 key window。
|
||||
private var activeWindow: UIWindow? {
|
||||
view.window ?? UIApplication.shared.connectedScenes
|
||||
.compactMap { $0 as? UIWindowScene }
|
||||
.flatMap(\.windows)
|
||||
.first(where: \.isKeyWindow)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user