简体   繁体   中英

Back button not being called in TabbarCoordinator in horizontal flow iOS 12

Coordinator pattern is an old topic with many libraries trying to solve it and I am learning it in simple example app. My current set up is 3 rootViewControlers: LoadingStateCoordinator , WelcomeCoordinator , TabBarCoordinator but missing connection between UIKit and coordinators. I am trying to implement it with a UINavigationController but the button is not being called. I need a way to connect to back button and a reusable coordinator that I could push to and dellocate accordingly (that is without RxSwift).*Set up Welcome screen as the parent/main navigation and always be able to come back to it.** So after user selects a form from modal view (vertical flow) presented I show on a push a TabBarCoordinator (horizontal). All viewControllers have empty .storyboard , UIViewController and Coordinator exept the TabBar.Here I only have a coordinator due to the set up of child tab coordinators and the magic needs to happen on a back button tap. Currenly this only being called when user comes from LoadingStateCoordinator. There I need to send the user back to the Welcome screen so they can change the onboarding set up. Here is the first code for LoadingStateCoordinator:

final class LoadingStateCoordinator: NSObject, Coordinator {
    *// MARK: - Inputs required*

    var childCoordinators: [Coordinator]
    var presenter: UINavigationController
    private let window: UIWindow

    *// MARK: - Initialization*
    init(window: UIWindow) {
        self.window = window
        childCoordinators = []
        presenter = UINavigationController()
    }
    *// MARK: - Coordinator*
     func start() {
        let controller: LoadingStateViewController = LoadingStateViewController.instantiate()
        window.rootViewController = controller
        controller.delegate = self
        }

}
    *// MARK: - LoadingViewControllerDelegate*
extension LoadingStateCoordinator : LoadingViewControllerDelegate {
    func performScreenSwitch() {
        if UserDefaults.standard.userWasHere == false {
            let tabCoordinator: TabBarCoordinator = TabBarCoordinator(window: window, tabBarController: UITabBarController())
            window.rootViewController = presenter
            addChildCoordinator(tabCoordinator)
            tabCoordinator.start()
            presenter.pushViewController(tabCoordinator.tabBarController!, animated: true)

        } else {
            let welcomeCoordinator = WelcomeCoordinator(window: window, presenter: presenter)
            window.rootViewController = welcomeCoordinator.presenter
            addChildCoordinator(welcomeCoordinator)
            welcomeCoordinator.start()
        }
    }
}

And here is the TabBarCoordinator that need to perform back to Welcome screen action. When I present popToRoot function it pushes the Welcome screen but all the button there are disbled. I guess to be retain cycle issue. Do I need funadametally another set up? Is there a way to popToRoot(vc) in this set up? What I tryed ended with runtime error "poping to non existing controller". TabBarCoordinator code that need to perform this:

final class TabBarCoordinator: NSObject, Coordinator {
    internal var presenter: UINavigationController
    internal var tabBarController: UITabBarController?
    internal var childCoordinators: [Coordinator]
    var parentCoordinator: LoadingStateCoordinator?
    lazy var leftBtn: UIBarButtonItem = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(systemName: "arrow.turn.up.left"), for: .normal)
        button.sizeToFit()
        button.addTarget(self,
                         action: #selector(self.popToRoot(_:)),
                         for: .touchUpInside)
      return UIBarButtonItem(customView: button)
    }()

    init(window: UIWindow, tabBarController: UITabBarController) {
        self.tabBarController = tabBarController
        childCoordinators = []
        self.presenter = UINavigationController()

    }
     func start() {
        performGetTabBar()
        self.presenter.delegate = self
    }
    private func performGetTabBar() {
        let coordinators: [Coordinator] = generateTabCoordinators()

        coordinators.forEach({ coordinator in
            coordinator.start()
            addChildCoordinator(coordinator)
        })

        let presenters: [UIViewController] = coordinators.map({ coordinator -> UIViewController in
            return coordinator.presenter
        })
        leftBtn.style = .plain
        tabBarController?.navigationItem.leftBarButtonItem = leftBtn
        tabBarController?.setViewControllers(presenters, animated: false)
        selectTab(type: SurfTripCoordinator.self)
    }

    private func generateTabCoordinators() -> [Coordinator] {
        let calculatorCoordinator: CalculatorCoordinator = CalculatorCoordinator(presenter: UINavigationController())
        let tripCoordinator: SurfTripCoordinator = SurfTripCoordinator(presenter: UINavigationController())
        let sellCoordinator: SavedTripsCoordinator = SavedTripsCoordinator(presenter: UINavigationController())
        return [calculatorCoordinator, tripCoordinator, sellCoordinator]
    }
    *//this is not being called when coming from vertical flow*
    @objc func popToRoot(_ sender: UIBarButtonItem) {
        let storyboard: UIStoryboard = UIStoryboard(name: Constants.Storyboards.welcomeViewCoordinator, bundle: nil)
        let controller: WelcomeViewController = WelcomeViewController.instantiate(from: storyboard)
        tabBarController?.navigationController?.pushViewController(controller, animated: true)

    }
}

extension TabBarCoordinator: UINavigationControllerDelegate {

    func selectTab<T: Coordinator>(type _: T.Type) {
        guard let index = childCoordinators.firstIndex(where: { coordinator in
            coordinator is T
        }) else {
            return
        }
        tabBarController?.selectedIndex = index
  }
}

and here is the current WelcomeCoordinator set up

class WelcomeCoordinator: NSObject, Coordinator {
    internal var presenter: UINavigationController
    var childCoordinators: [Coordinator]

    init(window: UIWindow, presenter: UINavigationController) {
        self.presenter = presenter
        childCoordinators = []

    }
    func start() {
        let storyboard: UIStoryboard = UIStoryboard(name: Constants.Storyboards.welcomeViewCoordinator, bundle: nil)
        let controller: WelcomeViewController = WelcomeViewController.instantiate(from: storyboard)
        controller.delegate = self
        presenter.pushViewController(controller, animated: true)
    }
}

extension WelcomeCoordinator : WelcomeViewControllerDelegate {

    func performAddLevel() {
        let addLevelCoordinator: AddLevelViewCoordinator = AddLevelViewCoordinator(presenter: UINavigationController())
        addLevelCoordinator.start()
        addChildCoordinator(addLevelCoordinator)
        addLevelCoordinator.presenter.modalPresentationStyle = .fullScreen
        presenter.present(addLevelCoordinator.presenter, animated: true, completion: nil)
    }
}

sorry for the long post I wish there was more reaktive native way to do this...

Ok so I found partlly a solution the back button solution for my case: not using pushViewController or show because it comes with back button. presenter.setViewControllers([tabCoordinator.tabBarController!], animated: true) and there setting the navBar to hidden. I made my own navItem button to navigate to rootVC. Next step to allocate and remove all child tabBar coordinators on back tap recognized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM