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.
Надеюсь, Вы дальше разберетесь, но если будут еще вопросы, или переоткройте эту задачу, или, если это не связано, создайте новую.