SwiftUI考勤系统中如何基于ViewModel条件实现页面跳转?
考勤系统页面跳转路由实现方案
针对你当前的需求,我们可以通过路由协议+依赖注入的方式实现MVVM架构下的页面跳转解耦,避免ViewModel直接依赖UI层代码。以下是具体实现步骤:
1. 定义路由协议
先创建一个路由协议,封装页面跳转和弹窗展示的方法,让ViewModel只依赖抽象协议而非具体UI实现:
protocol LoginRouterProtocol { // 跳转到员工验证页面 func navigateToEmployeeValidationScreen() // 展示等待提示Toast func showEmployeeWaitingToast() }
2. 实现具体路由类
在UI层(SwiftUI/UIKit)实现该协议,处理实际的页面跳转和UI操作:
SwiftUI 场景实现
class LoginRouter: LoginRouterProtocol { private let navigationPath: Binding<NavigationPath> init(navigationPath: Binding<NavigationPath>) { self.navigationPath = navigationPath } func navigateToEmployeeValidationScreen() { navigationPath.wrappedValue.append(ValidationScreenTag()) } func showEmployeeWaitingToast() { var style = ToastStyle() style.backgroundColor = UIColor.gray.withAlphaComponent(0.6) style.textColor = UIColor.white ToastManager.showToast("employee_waiting", duration: ToastManager.LENGHT_LONG, position: .bottom, style: style) } } // SwiftUI导航所需的页面标识结构体 struct ValidationScreenTag: Hashable {}
UIKit 场景实现
class LoginRouter: LoginRouterProtocol { private weak var navigationController: UINavigationController? init(navigationController: UINavigationController?) { self.navigationController = navigationController } func navigateToEmployeeValidationScreen() { let validationVC = EmployeeValidationViewController() navigationController?.pushViewController(validationVC, animated: true) } func showEmployeeWaitingToast() { var style = ToastStyle() style.backgroundColor = UIColor.gray.withAlphaComponent(0.6) style.textColor = UIColor.white ToastManager.showToast("employee_waiting", duration: ToastManager.LENGHT_LONG, position: .bottom, style: style) } }
3. 修改ViewModel,注入路由依赖
在ViewModel中添加路由属性,通过初始化注入,业务逻辑中直接调用协议方法即可:
class LoginViewModel { var resultContent: ResultContent? private let router: LoginRouterProtocol // 初始化时注入路由实例 init(router: LoginRouterProtocol) { self.router = router } func fetchUser(cId: String?, eId: Int?) { let apiService = RegisterDataSource() apiService.searchEmployee2(civilId: cId, employeeId: eId) { [weak self] result in guard let self = self else { return } switch result { case .success(let user): DispatchQueue.main.async { self.resultContent = user } guard let regStatusID = self.resultContent?.regStatusID else { return } if Vontime().key == nil || Vontime().key == "" { if regStatusID == 0 || regStatusID == 1 { // 通过路由跳转页面 self.router.navigateToEmployeeValidationScreen() } else if regStatusID == 2 { // 通过路由展示Toast self.router.showEmployeeWaitingToast() } } case .failure(let error): // 可扩展:通过路由展示错误提示 print("API请求失败:\(error.localizedDescription)") } } } }
4. 在View层初始化ViewModel和路由
SwiftUI 场景
struct LoginView: View { @State private var navigationPath = NavigationPath() @StateObject private var loginVM: LoginViewModel init() { let router = LoginRouter(navigationPath: $navigationPath) _loginVM = StateObject(wrappedValue: LoginViewModel(router: router)) } var body: some View { NavigationStack(path: $navigationPath) { // 你的UI布局 Button("Search"){ loginVM.fetchUser(cId: civil_id, eId: emp_id) } // 配置导航目的地 .navigationDestination(for: ValidationScreenTag.self) { EmployeeValidationView() } } } }
UIKit 场景
class LoginViewController: UIViewController { private var loginVM: LoginViewModel! override func viewDidLoad() { super.viewDidLoad() let router = LoginRouter(navigationController: self.navigationController) loginVM = LoginViewModel(router: router) } @IBAction func searchButtonTapped(_ sender: UIButton) { let civilId = civilIdTextField.text let empId = empIdTextField.text.flatMap(Int.init) loginVM.fetchUser(cId: civilId, eId: empId) } }
方案优势
- 职责分离:ViewModel专注业务逻辑判断,路由统一处理UI跳转,符合MVVM设计原则
- 可测试性:单元测试时可以注入Mock路由,无需依赖真实UI环境
- 扩展性:后续新增页面跳转只需扩展路由协议和实现,无需修改原有业务逻辑
内容的提问来源于stack exchange,提问作者Gman256
相关产品推荐
相关产品推荐

