hhru/Nivelir

Переключение таба

Closed this issue · 5 comments

Привет. Как через нивелир можно достать TabBar и открыть на нем второй таб?
topCotainer у меня RootViewController.
Пытаюсь найти TabBar через container, но он его не видит.
image

Мой код:

navigator.navigate { route in
            route
                .first(.tabs)
                .selectTab(of: UINavigationController.self, with: .index(1))
                .makeVisible()
}

Проблему обнаружил в этих строчках когда:

        guard let vc = viewController?.navigationController else { return }
        navigator.navigate(from: vc) {
            $0.embedReplacing(makeTabsScreen())
        }

Если навигировать не из navigationController, тогда все работает ок:

    func setTabsScreen() async {
        navigator.navigate(to: showHomeRoute())
    }

    func showHomeRoute() -> ScreenWindowRoute {
        @IntercityInjected
        var tabsScreenFactory: TabsScreenFactoryProtocol

        return ScreenWindowRoute()
            .setRoot(to: tabsScreenFactory.makeTabsScreen())
            .makeKeyAndVisible()
    }

Но проблема в том, что мне нужен navigationController в этом кейсе.

Привет 👋

Не совсем понятно, как код из второго сообщения взаимодействует с кодом из первого сообщения?
Можете еще прислать логи с ошибками?

Есть роутинг диплинка, который переключает на другой таб:

navigator.navigate { route in
            route
                .first(.tabs)
                .selectTab(of: UINavigationController.self, with: .index(1))
                .makeVisible()
}

Есть создание таб бара:

        guard let vc = viewController?.navigationController else { return }
        navigator.navigate(from: vc) {
            $0.embedReplacing(makeTabsScreen())
        }

Код embedReplacing:

    func embedReplacing<New: Screen>(
        _ screen: New,
        animated: Bool = true,
        route: (_ route: ScreenRootRoute<New.Container>) -> ScreenRouteConvertible = { $0 }
    ) -> Self where New.Container: UIViewController {
        embedReplacing(
            screen,
            animated: animated,
            route: route(.initial).route()
        )
    }
    func embedReplacing<New: Nivelir.Screen, Route: ScreenThenable>(
        _ screen: New,
        animated: Bool = true,
        route: Route
    ) -> Self where New.Container: UIViewController, Route.Root == New.Container {
        fold(
            action: ScreenEmbedReplacingAction<New, Current>(
                screen: screen,
                animated: animated
            ),
            nested: route
        )
    }

Когда приходит диплинк и вызывается первый код для диплинки. Пишет, что контейнер TabBar не найден:

Info: Searching for a first container of UITabBarController type in UIWindow
Error: No container of UITabBarController type found for:
  ScreenFirstAction<UIWindow, UITabBarController>(predicate: container of UITabBarController type)

Важно сказать, что что tabBar я кладу как child на другой viewController. Мб в этом проблема?

  public func perform(
       container: Container,
       navigator: Nivelir.ScreenNavigator,
       completion: @escaping Completion
   ) {
       navigator.logInfo("EmbedReplacing \(screen) on \(type(of: container))")

       let child = screen.build(navigator: navigator)
       // TODO: - Разобраться с ошибкой
       guard let parent = container.parent else {
           completion(.failure(NSError(domain: "", code: 999)))
           return
       }

       container.willMove(toParent: nil)
       container.view.removeFromSuperview()
       container.removeFromParent()
       container.didMove(toParent: nil)

       parent.addChildController(child)

       completion(.success(child))
   }

Скорее всего да, проблема из-за встраивания, нивелир по умолчанию не ищет кастомный контейнер в children.
Чтобы он начал там искал, нужно подписать ViewController, в который встраивается TabBarController, под протокол ScreenIterableContainer:

extension SomeViewController: ScreenIterableContainer {

    public var nestedContainers: [ScreenContainer] {
        children
    }
}

Но хочется понять, какую проблему вы решаете, встраивая UITabBarController в другой контроллер? Возможно через средства Nivelir эту проблему можно решить и без встраивания.

Да, это решение помогло. Спасибо!