Как использовать координаторы с UIITabBarController?

Я играю с архитектурой MVVM-C, но я не знаю, как я могу создать несколько координаторов с разными вкладками при выборе вкладки.

Вот мой основной класс координаторов приложений...

protocol UINavigationControllerType: class {
func pushViewController(_ viewController: UIViewController, animated: Bool)
func popViewController(animated: Bool) -> UIViewController?
}

protocol Coordinator: class {
func start()
}

final class AppCoordinator: Coordinator {
// MARK: - Properties
var managedObjectContext: NSManagedObjectContext!
var coordinators = [String : Coordinator]()

var tabController: UITabBarController?

// MARK: - Object Lifecycle
init(moc: NSManagedObjectContext, tabController: UITabBarController) {
    self.managedObjectContext = moc
    self.tabController = tabController
}

// MARK: - Coordinator
func start() {
    guard let tabController = tabController else {return}

    let profileNavigationController = NavigationController()
    profileNavigationController.tabBarItem = UITabBarItem(title: "Profile", image: UIImage(named: "profileUnselected"), selectedImage: UIImage(named: "profileSelected"))

    let plansNavigationController = NavigationController()
    plansNavigationController.tabBarItem = UITabBarItem(title: "Plans", image: UIImage(named: "plansUnselected"), selectedImage: UIImage(named: "plansSelected"))

    tabController.viewControllers = [profileNavigationController, plansNavigationController]
    tabController.selectedViewController = profileNavigationController

    let profileCoordinator = ProfileCoordinator(navigationController: profileNavigationController)
    profileCoordinator.managedObjectContext = managedObjectContext
    coordinators["profileCoordinator"] = profileCoordinator
    profileCoordinator.delegate = self
    profileCoordinator.start()
}
}

// MARK: - ProfileCoordinatorDelegate
extension AppCoordinator: ProfileCoordinatorDelegate {}

Итак, как бы я мог перейти от текущего координатора (ProfileCoordinator) к PlansCoordinator, когда выбрана вкладка?

Ответ 1

Моя структура координатора отличается от вашей, но она может вам помочь. В моем случае протокол Coordinator имеет свойство rootViewController, которое указывает на этот координатор ViewController.

My AppCoordinator затем сохраняется TabCoordinator, который выглядит примерно так: (В реальном коде постоянные координаторы NavigationCoordinators, которые являются специальными координаторами, которые содержат NavigationControllers. В этом примере я просто добавил ViewControllers и удаленные элементы управления памятью, чтобы было легче понять.)

final class TabCoordinator: NSObject, Coordinator {

var rootViewController: UIViewController {
    return tabController
}

let tabController: UITabBarController

let homeCoordinator: HomeCoordinator
let historyCoordinator: HistoryCoordinator
let profileCoordinator: ProfileCoordinator

var coordinators: [Coordinator] {
    return [homeCoordinator, historyCoordinator, profileCoordinator]
}

init(client: HTTPClient, persistence: Persistence) {

    tabController = UITabBarController()

    homeCoordinator = HomeCoordinator(client: client, persistence: persistence)

    historyCoordinator = HistoryCoordinator(client: client, persistence: persistence)

    profileCoordinator = ProfileCoordinator(client: client, persistence: persistence)

    var controllers: [UIViewController] = []

    let homeViewController = homeCoordinator.rootViewController
    homeViewController.tabBarItem = UITabBarItem(title: Localization.homeTab.string, image: Asset.iconMenuRecharge.image, selectedImage: Asset.iconMenuRechargeActivated.image)

    let historyViewController = historyCoordinator.rootViewController
    historyViewController.tabBarItem = UITabBarItem(title: Localization.walletTab.string, image: Asset.iconMenuWallet.image, selectedImage: Asset.iconMenuWalletActivated.image)

    let profileViewController = profileCoordinator.rootViewController
    profileViewController.tabBarItem = UITabBarItem(title: Localization.profileTab.string, image: Asset.iconMenuProfile.image, selectedImage: Asset.iconMenuProfileActivated.image)

    super.init()

    controllers.append(homeViewController)
    controllers.append(historyViewController)
    controllers.append(profileViewController)

    tabController.viewControllers = controllers
    tabController.tabBar.isTranslucent = false
    tabController.delegate = self

}
}

Таким образом, ваш TabBarController является TabCoordinator, чей rootViewController является TabBarController. TabCoordinator создает экземпляр соответствующих координаторов и добавляет соответствующие вкладки rootViewControllers на вкладку.

Вот базовая реализация NavigationCoordinator:

class NavigationCoordinator: NSObject, Coordinator {    

    public var navigationController: UINavigationController     

    public override init() {
        self.navigationController = UINavigationController()
        self.navigationController.view.backgroundColor = .white
        super.init()
    }    

    public var rootViewController: UIViewController {
        return navigationController
    }
}

И базовая версия Coordinator:

public protocol Coordinator: class {    
    var rootViewController: UIViewController { get }    
}

Ответ 2

Я хочу поделиться примером для этого вопроса. Мой подход немного отличается и TabCoordinator не содержит всего в его теле. Вместо этого каждый координатор имеет отношение к своему контроллеру, и в каждом UIViewController (в контроллере вкладок) есть ссылка UITabBarController, такая как шаблон делегирования.

class Coordinator {

    var navigationController: UINavigationController?
    var childCoordinators: [Coordinator] = [Coordinator]()

    init(with navigation: UINavigationController) {
        self.navigationController = navigation
    }

    func start() {}

}

Тогда вот координаторы.

Домашний координатор

final class HomeCoordinator: Coordinator {

    var currentController: HomeController?
    weak var tabController: TabController?


    override init(with navigation: UINavigationController) {
        super.init(with: navigation)

        currentController = HomeController()
        currentController?.coordinator = self
        childCoordinators.append(self)
    }

    override func start() {

        navigationController?.pushViewController(currentController ?? UIViewController(),
                                                 animated: true)

    }

    public func getHomeData() {

        // GETTING HOME DATA

        tabController?.requestFromHomeController()

    }

}

О координаторе

final class AboutCoordinator: Coordinator {

    var currentController: AboutController?
    weak var tabController: TabController?

    override init(with navigation: UINavigationController) {
        super.init(with: navigation)

        currentController = AboutController()
        currentController?.coordinator = self
        childCoordinators.append(self)
    }


    override func start() {

        navigationController?.pushViewController(currentController ?? UIViewController(),
                                                 animated: true)

    }

    public func getAboutData() {

        // GETTING ABOUT DATA

        tabController?.requestFromAboutController()

    }
}

UITabBarController

final class TabController: UITabBarController {

    weak var coordinator: MainTabCoordinator?


    override func viewDidLoad() {
        super.viewDidLoad()


        let navigationController1 = UINavigationController()
        let coordinator1 = HomeCoordinator(with: navigationController1)
        coordinator1.tabController = self
        coordinator1.currentController?.tabBarItem = UITabBarItem(title: "HOME",
                                                                  image: nil,
                                                                  tag: 11)

        let navigationController2 = UINavigationController()
        let coordinator2 = AboutCoordinator(with: navigationController2)
        coordinator2.tabController = self
        coordinator2.currentController?.tabBarItem = UITabBarItem(title: "ABOUT",
                                                                  image: nil,
                                                                  tag: 22)


        viewControllers = [
            coordinator1.currentController!,
            coordinator2.currentController!
        ]

        tabBar.barTintColor = UIColor.white
        tabBar.isTranslucent = false

    }

    public func requestFromHomeController() {
        print("Home Triggered the function")

        coordinator?.fetchHome(with: "Simple Data",
                               completion: { (dictionary, error) in
                                print("dict from home -> ", dictionary)
        })



    }

    public func requestFromAboutController() {
        print("About Triggered the function")

        coordinator?.handleAbout(with: "Simple Data",
                                 completion: { (dictionary, error) in
                                    print("dict from about -> ", dictionary)
        })
    }    
}

Подготовьте приложение из AppDelegate

in application(_ application: UIApplication, didFinishLaunchingWithOptions function

let appNavigationController = UINavigationController()
let tabCoordinator = MainTabCoordinator(with: appNavigationController ?? UINavigationController())
tabCoordinator.start()
window?.rootViewController = appNavigationController

Вот AboutController

final class AboutController: UIViewController{

    weak var coordinator: AboutCoordinator?

    // define a button and add its target to handleButton function

    @objc private func handleButton(_ sender: UIButton) {

        coordinator?.getAboutData()
    }


    override func viewDidLoad() {
        super.viewDidLoad()

        // ui settings
    }
}

MainTabCoordinator

final class MainTabCoordinator: Coordinator {

    var currentController: TabController?


    override init(with navigation: UINavigationController) {
        super.init(with: navigation)

        self.currentController = TabController()
        self.childCoordinators.append(self)
        currentController?.coordinator = self
    }



    override func start() {

        navigationController?.pushViewController(currentController ?? UIViewController(),
                                                 animated: true)

    }

    func fetchHome(with title: String, completion:  @escaping (_ result: Dictionary<String,Any>, _ error: NSError?) -> ()) {

        completion(["ss":"ss"], nil)

    }


    func handleAbout(with title: String, completion: @escaping (_ result: Dictionary<String,Any>, _ error: NSError?) -> ()) {

        completion(["ss":"ss"], nil)
    }

}

Рабочая схема выглядит следующим образом:

AboutController ---> триггерное действие ---> AboutCoordinator ---> TabBarController reference запросил о действии → Ручка MainTabCoordinator работает.