基于MVVM-C的TabBarController:跨标签页跳转同ViewController的问题
问题描述
我有一个包含两个标签页的TabBarCoordinator:
- 第一个标签页由
FirstTabCoordinator管理,对应电影列表页(MoviesListViewController) - 第二个标签页由
SecondTabCoordinator管理,对应收藏电影页(FavoritesViewController)
两个页面都带有tableView,且各自拥有独立的UINavigationController。
从电影列表页或收藏页点击电影时,均可正常推送MovieDetailsViewController。但MovieDetailsViewController中设有相似电影集合,当前问题:在MovieDetailsViewController的didSelectItemAt indexPath方法中点击相似电影时,如何确定应使用哪个协调器来推送下一个MovieDetailsViewController?因为该详情页可从两个不同协调器跳转进入。
我认为需要在两个协调器之间添加某种机制,但不确定具体方案。
相关代码
MainCoordinator
protocol Coordinator { func startCoordinator() } class MainCoordinator: Coordinator { var tabBarController = UITabBarController() static func getCoordinator() -> MainCoordinator? { if let scene = UIApplication.shared.connectedScenes.first, let delegate = scene.delegate as? SceneDelegate { return delegate.coordinator } return nil } func startCoordinator() { FirstTabCoordinator.shared.startCoordinator() let moviesListVC = FirstTabCoordinator.shared.moviesListViewController self.setupTabBarItem(vc: moviesListVC, title: "All Movies", imageName: "film", selectedImageName: "film.fill") SecondTabCoordinator.shared.startCoordinator() let favoritesVC = SecondTabCoordinator.shared.favoritesViewController self.setupTabBarItem(vc: favoritesVC, title: "Favorites", imageName: "heart", selectedImageName: "heart.fill") self.tabBarController.tabBar.barStyle = .black self.tabBarController.tabBar.isTranslucent = false self.tabBarController.tabBar.tintColor = .white self.tabBarController.viewControllers = [FirstTabCoordinator.shared.navigationController, SecondTabCoordinator.shared.navigationController] } func setupTabBarItem(vc: UIViewController, title: String, imageName: String, selectedImageName: String) { let defaultImage = UIImage(systemName: imageName) let selectedImage = UIImage(systemName: selectedImageName) let tabBarItem = UITabBarItem(title: title, image: defaultImage, selectedImage: selectedImage) vc.tabBarItem = tabBarItem } }
FirstTabCoordinator
class FirstTabCoordinator: Coordinator { var navigationController: UINavigationController! { didSet { navigationController?.navigationBar.tintColor = .white navigationController?.navigationBar.barStyle = .black } } let storyboard = UIStoryboard(name: "Main", bundle: .main) private lazy var navigationManager = NavigationManager(self.navigationController) static let shared = FirstTabCoordinator() private init() {} lazy var moviesListViewController: MoviesTableViewController = { let viewController = self.storyboard.instantiateViewController(identifier: "MoviesTableViewController") as! MoviesTableViewController viewController.title = "All Movies" return viewController }() func startCoordinator() { self.navigationController = UINavigationController() self.navigationController.viewControllers = [self.moviesListViewController] } func goToMovieDetails(movie: Movie) { self.navigationManager.goToMovieDetails(movie: movie) } }
SecondTabCoordinator
class SecondTabCoordinator: Coordinator { var navigationController: UINavigationController! { didSet { navigationController?.navigationBar.tintColor = .white navigationController?.navigationBar.barStyle = .black } } let storyboard = UIStoryboard(name: "Main", bundle: .main) private lazy var navigationManager = NavigationManager(self.navigationController) static let shared = SecondTabCoordinator() private init() {} lazy var favoritesViewController: FavoritesViewController = { let viewController = self.storyboard.instantiateViewController(identifier: "FavoritesViewController") as FavoritesViewController viewController.title = "Favorites" return viewController }() func goToMovieDetails(movie: Movie) { self.navigationManager.goToMovieDetails(movie: movie) } func startCoordinator() { self.navigationController = UINavigationController() self.navigationController.viewControllers = [self.favoritesViewController] } }
解决方案
方案一:给MovieDetailsViewController持有来源协调器引用
直接在创建详情页时传递来源协调器,点击相似电影时复用该协调器推送新页面。
- 修改
MovieDetailsViewController添加协调器属性:
class MovieDetailsViewController: UIViewController { weak var coordinator: Coordinator? var movie: Movie! func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let similarMovie = similarMovies[indexPath.item] switch coordinator { case let tabCoordinator as FirstTabCoordinator: tabCoordinator.goToMovieDetails(movie: similarMovie) case let tabCoordinator as SecondTabCoordinator: tabCoordinator.goToMovieDetails(movie: similarMovie) default: break } } }
- 更新
NavigationManager的初始化和跳转方法,传递协调器:
class NavigationManager { private weak var navigationController: UINavigationController? weak var coordinator: Coordinator? init(navigationController: UINavigationController, coordinator: Coordinator) { self.navigationController = navigationController self.coordinator = coordinator } func goToMovieDetails(movie: Movie) { let storyboard = UIStoryboard(name: "Main", bundle: .main) let detailsVC = storyboard.instantiateViewController(identifier: "MovieDetailsViewController") as! MovieDetailsViewController detailsVC.movie = movie detailsVC.coordinator = coordinator navigationController?.pushViewController(detailsVC, animated: true) } }
- 修改两个TabCoordinator中
navigationManager的初始化:
// FirstTabCoordinator private lazy var navigationManager = NavigationManager(self.navigationController, coordinator: self) // SecondTabCoordinator private lazy var navigationManager = NavigationManager(self.navigationController, coordinator: self)
方案二:通过NavigationManager反向匹配协调器
利用每个Tab协调器持有独立navigationController的特点,通过当前详情页所在的导航控制器匹配对应的协调器。
修改MovieDetailsViewController的点击方法:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let similarMovie = similarMovies[indexPath.item] guard let currentNav = navigationController else { return } if FirstTabCoordinator.shared.navigationController === currentNav { FirstTabCoordinator.shared.goToMovieDetails(movie: similarMovie) } else if SecondTabCoordinator.shared.navigationController === currentNav { SecondTabCoordinator.shared.goToMovieDetails(movie: similarMovie) } }
该方案无需大量修改现有代码,但依赖协调器单例的正确性。
方案三:使用闭包回调解耦
通过闭包将点击事件回调给来源协调器,避免详情页直接持有协调器引用。
- 修改
MovieDetailsViewController添加回调闭包:
class MovieDetailsViewController: UIViewController { var movie: Movie! var onSimilarMovieSelected: ((Movie) -> Void)? func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let similarMovie = similarMovies[indexPath.item] onSimilarMovieSelected?(similarMovie) } }
- 更新
NavigationManager的跳转方法,接收闭包参数:
func goToMovieDetails(movie: Movie, onSimilarSelected: @escaping (Movie) -> Void) { let storyboard = UIStoryboard(name: "Main", bundle: .main) let detailsVC = storyboard.instantiateViewController(identifier: "MovieDetailsViewController") as! MovieDetailsViewController detailsVC.movie = movie detailsVC.onSimilarMovieSelected = onSimilarSelected navigationController?.pushViewController(detailsVC, animated: true) }
- 修改两个TabCoordinator的
goToMovieDetails方法,设置闭包逻辑:
// FirstTabCoordinator func goToMovieDetails(movie: Movie) { navigationManager.goToMovieDetails(movie: movie) { [weak self] similarMovie in self?.goToMovieDetails(movie: similarMovie) } } // SecondTabCoordinator func goToMovieDetails(movie: Movie) { navigationManager.goToMovieDetails(movie: movie) { [weak self] similarMovie in self?.goToMovieDetails(movie: similarMovie) } }
该方案解耦了详情页与协调器,符合依赖倒置原则。
内容的提问来源于stack exchange,提问作者alexionut01
相关产品推荐
相关产品推荐

