ekazaev/route-composer

Custom navigation using setViewControllers(_:animated:)

Ostroverkhov opened this issue · 5 comments

Есть UINavigationController, который содержит 5 контроллеров: 1->2->3->4->5.
Я хочу добавить еще один после 2, т.е. должно получится так: 1->2->6.

В текущей реализации сначала происходит сброс до 2 контроллера, а потом переход на 6, что выглядит не очень красиво.

Подскажите, пожалуйста, как можно это решить?

@Ostroverkhov Добрый день

Обычно, это как раз поведение которого все хотят, так как оно более наглядно показывает пользователю что же происходит, вопрос красивости довольно субъективен :). Ну да бог с ним. Если, по какой то причине, вы хотите сделать свое поведение, то нужно посмотреть в сторону Action. Эта сущность отвечает за модификацию текущего графа вью контроллеров.

Возьмите за образец PushReplacingLastAction и напишите свою реализацию. Должно получиться что то вроде

struct PushAfterProductListAction<ViewController: UINavigationController>: ContainerAction {

    init() {}

   func perform(embedding viewController: UIViewController,
                        in childViewControllers: inout [UIViewController]) {
        if let index = childViewControllers.firstIndex(where: { $0 is ProductListViewController}) {
            childViewControllers = Array(childViewControllers.prefix(index))
        }
        childViewControllers.append(viewController)
    }

   func perform(with viewController: UIViewController,
                        on navigationController: ViewController,
                        animated: Bool,
                        completion: @escaping (_: RoutingResult) -> Void) {
        var viewControllers = navigationController.viewControllers
        perform(embedding: viewController, in: &viewControllers)
        navigationController.setViewControllers(viewControllers, animated: animated)
        if let transitionCoordinator = navigationController.transitionCoordinator, animated {
            transitionCoordinator.animate(alongsideTransition: nil) { _ in
                completion(.success)
            }
        } else {
            completion(.success)
        }
    }

}

let configuration =   StepAssembly(
            finder: ClassFinder<ProductDetailsViewController, Any?>(),
            factory: ClassFactory())
            .using(PushAfterProductListAction<UINavigationController>())
            .from(homeScreen.expectingContainer())
            .assemble()

(ЗЫ: Я не проверял компилируется ли код, так как не за компьютером, но, думаю, идея понятна)

Можете расширить Action что бы сделать его переиспользуемым и передавать способ найти нужный контроллер в конструкторе.

Пожалуйста, отпишитесь правильно ли я Вас понял и нужно ли что то еще.

@ekazaev Спасибо за ответ)
Да, Вы верно меня поняли)

Адаптировал код выше под себя, но что-то не удалось добиться нужного эффекта(
В этой строчке я уже получаю нужный массив:
var viewControllers = navigationController.viewControllers

И получается этот код не выполняет никакой работы:

if let index = childViewControllers.firstIndex(where: { $0 is ProductListViewController}) {
    childViewControllers = Array(childViewControllers.prefix(index))
}

И в итоге поведение не отличается от UINavigationController.push().
Пока не могу понять где у меня ошибка(

@Ostroverkhov Если вы уже получаете нужный массив значит он уже сформирован вашей конфигурацией. Обратите внимание на предыдущий шаг. Он уже выполнился к этому моменту и сфрмировал вам нужный массив. Вам нужно его изменить. Вам нужно что бы предыдуший шаг просто отдавал вам UINavigationController

Вместо .from(homeScreen.expectingContainer()) должно быть что то вроде допустим .from(GeneralStep.custom(using: ClassFinder<UINavigationController, Any?>())) или .from(GeneralStep.root().expectingContainer()) смотря что у вас там за структура приложения.

Если вы говорите .from(homeScreen.expectingContainer()) это значит что сперва нужно его найти (а вдруг его нет), показать, а потом на его UINavigationController применять Action. И к тому времени когда ваш Action начинает работать - там уже и так все хорошо, но Вам так не надо.

@ekazaev Спасибо! Теперь все понятно

Hope you are satisfied with the result. Feel free to reopen the issue or create a new one if you’ll have another question.

Надеюсь, Вы дальше разберетесь, но если будут еще вопросы, или переоткройте эту задачу, или, если это не связано, создайте новую.