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:
2026-06-26 16:36:18 +08:00
parent 24a7339b68
commit a1c031c9b7
31 changed files with 838 additions and 517 deletions

View File

@ -187,7 +187,7 @@ final class AccountSwitchViewController: UIViewController, UITableViewDelegate {
profileAPI: appServices.profileAPI,
accountContextAPI: appServices.accountContextAPI
)
appServices.appRouter.reset()
appServices.appNavigator.resetAllStacks()
showToast("账号已切换")
navigationController?.popToRootViewController(animated: true)
} catch {

View File

@ -28,6 +28,24 @@ private enum ProfileRowID {
static let logout = "profile:logout"
}
/// value1 Cell Diffable reconfigure
private final class ProfileValueTableViewCell: UITableViewCell {
/// value1 Cell
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .value1, reuseIdentifier: reuseIdentifier)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
private enum ProfileCellReuseID {
static let value = "profile.value"
static let logout = "profile.logout"
}
///
final class ProfileViewController: UIViewController, UITableViewDelegate {
@ -76,43 +94,57 @@ final class ProfileViewController: UIViewController, UITableViewDelegate {
/// Diffable
private func configureTableDataSource() {
tableView.register(ProfileValueTableViewCell.self, forCellReuseIdentifier: ProfileCellReuseID.value)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: ProfileCellReuseID.logout)
tableDataSource = UITableViewDiffableDataSource<ProfileTableSection, ProfileTableRow>(
tableView: tableView
) { [weak self] (_: UITableView, indexPath: IndexPath, row: ProfileTableRow) -> UITableViewCell? in
) { [weak self] (tableView: UITableView, indexPath: IndexPath, row: ProfileTableRow) -> UITableViewCell? in
guard let self else { return UITableViewCell() }
let cell = UITableViewCell(style: .value1, reuseIdentifier: nil)
switch row {
case ProfileRowID.logout:
let cell = tableView.dequeueReusableCell(withIdentifier: ProfileCellReuseID.logout, for: indexPath)
cell.textLabel?.text = "退出登录"
cell.textLabel?.textColor = UIColor(hex: 0xEF4444)
cell.textLabel?.textAlignment = .center
cell.selectionStyle = .default
case ProfileRowID.name:
cell.textLabel?.text = "姓名"
cell.detailTextLabel?.text = self.viewModel.displayRealName
cell.selectionStyle = .none
case ProfileRowID.account:
cell.textLabel?.text = "当前账号"
cell.detailTextLabel?.text = self.appServices.accountContext.profile?.displayName ?? "--"
cell.accessoryType = .disclosureIndicator
cell.selectionStyle = .default
case ProfileRowID.phone:
cell.textLabel?.text = "手机号"
cell.detailTextLabel?.text = self.viewModel.displayPhone
cell.selectionStyle = .none
case ProfileRowID.realName:
cell.textLabel?.text = "实名认证"
cell.detailTextLabel?.text = self.viewModel.realNameStatusText
cell.accessoryType = .disclosureIndicator
cell.selectionStyle = .default
case ProfileRowID.settings:
cell.textLabel?.text = "设置"
cell.accessoryType = .disclosureIndicator
cell.selectionStyle = .default
cell.accessoryType = .none
return cell
default:
break
let cell = tableView.dequeueReusableCell(
withIdentifier: ProfileCellReuseID.value,
for: indexPath
)
cell.textLabel?.textAlignment = .natural
cell.textLabel?.textColor = .label
cell.accessoryType = .none
cell.selectionStyle = .none
switch row {
case ProfileRowID.name:
cell.textLabel?.text = "姓名"
cell.detailTextLabel?.text = self.viewModel.displayRealName
case ProfileRowID.account:
cell.textLabel?.text = "当前账号"
cell.detailTextLabel?.text = self.appServices.accountContext.profile?.displayName ?? "--"
cell.accessoryType = .disclosureIndicator
cell.selectionStyle = .default
case ProfileRowID.phone:
cell.textLabel?.text = "手机号"
cell.detailTextLabel?.text = self.viewModel.displayPhone
case ProfileRowID.realName:
cell.textLabel?.text = "实名认证"
cell.detailTextLabel?.text = self.viewModel.realNameStatusText
cell.accessoryType = .disclosureIndicator
cell.selectionStyle = .default
case ProfileRowID.settings:
cell.textLabel?.text = "设置"
cell.accessoryType = .disclosureIndicator
cell.selectionStyle = .default
default:
break
}
return cell
}
return cell
}
applyTableSnapshot(animated: false)
}
@ -296,7 +328,7 @@ final class ProfileViewController: UIViewController, UITableViewDelegate {
accountContext: self.appServices.accountContext,
permissionContext: self.appServices.permissionContext,
scenicSpotContext: self.appServices.scenicSpotContext,
appRouter: self.appServices.appRouter,
appNavigator: self.appServices.appNavigator,
toastCenter: self.appServices.toastCenter
)
})
@ -311,11 +343,11 @@ final class ProfileViewController: UIViewController, UITableViewDelegate {
case ProfileRowID.logout:
logoutTapped()
case ProfileRowID.account:
HomeMenuRouting.pushProfile(.accountSwitch, from: self)
appServices.appNavigator.push(.profile(.accountSwitch), from: self)
case ProfileRowID.realName:
HomeMenuRouting.pushProfile(.realNameAuth, from: self)
appServices.appNavigator.push(.profile(.realNameAuth), from: self)
case ProfileRowID.settings:
HomeMenuRouting.pushProfile(.settings, from: self)
appServices.appNavigator.push(.profile(.settings), from: self)
default:
break
}

View File

@ -98,7 +98,7 @@ final class SettingsViewController: SimpleTableDiffableViewController {
guard let action = rowActions[row] else { return }
switch action {
case .agreement(let page):
HomeMenuRouting.pushProfile(.agreement(page), from: self)
appServices.appNavigator.push(.profile(.agreement(page)), from: self)
case .download:
copyDownloadLink()
case .version: