web-dev-qa-db-fra.com

Comment utiliser les coordinateurs avec un UITabBarController?

Je joue avec l'architecture MVVM-C, mais je ne sais pas comment je peux instancier plusieurs coordinateurs avec différents onglets lorsqu'un onglet est sélectionné.

Voici ma principale classe de coordinateur d'applications ...

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 {}

Alors, comment pourrais-je passer du coordinateur actuel (ProfileCoordinator) au PlansCoordinator lorsque l'onglet est sélectionné?

21
Luke97

Ma structure de coordinateur est différente de la vôtre, mais elle pourrait vous aider. Dans mon cas, le protocole du coordinateur a une propriété rootViewController qui pointe vers le ViewController de ce coordinateur.

Mon AppCoordinator persiste alors un TabCoordinator, qui ressemble un peu à ceci: (Dans le code réel, les coordinateurs persistants sont NavigationCoordinators, qui sont des coordinateurs spéciaux qui contiennent NavigationControllers. Dans ce exemple, je viens d'ajouter les ViewControllers et de supprimer les éléments de gestion de la mémoire pour le rendre plus facile à comprendre.)

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

}
}

Donc, fondamentalement, votre TabBarController est un TabCoordinator dont le rootViewController est un TabBarController. Le TabCoordinator instancie les coordinateurs appropriés et ajoute leurs rootViewControllers respectifs à l'onglet.

Voici une implémentation de base de 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
    }
}

Et une version de base d'un Coordinator:

public protocol Coordinator: class {    
    var rootViewController: UIViewController { get }    
}
26
Bruno Rocha

Je veux partager un exemple pour cette question. Mon approche est un peu différente et TabCoordinator ne contient pas tout dans son corps. Au lieu de cela, chaque coordinateur a une relation avec son contrôleur et dans chaque UIViewController (dans le contrôleur de la barre de tabulation) a son UITabBarController référence comme modèle de délégation.

class Coordinator {

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

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

    func start() {}

}

Alors voici les coordinateurs.

Coordonnateur à domicile

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()

    }

}

À propos du coordinateur

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)
        })
    }    
}

Préparez l'application depuis AppDelegate

dans l'application (_ application: UIApplication, fonction didFinishLaunchingWithOptions

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

Voici le 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)
    }

}

Le schéma de travail est le suivant:

AboutController ---> action déclenchée ---> AboutCoordinator ---> TabBarController reference a une requête sur l'action ----> MainTabCoordinator handle fonctionne.

1
elia