Page 384 note that It’s important that you always call the scene coordinator’s transition(to:type:) and pop() functions to transition between scenes.
So, when I push a view controller, the navigation controller will create a back bar button item for users to pop back, and its action can’t be changed. In this case, I create a left bar button item for the new top-level view controller along with a action calls the scene cooldinator’s pop function.
What’s weird is, when I clicked the left bar button item as soon as the new top-level controller appear which leads to the fatalError("Can't navigate back from \(currentViewController)")
.
In the end, how and when to use scene cooldinator’s pop() function to pop back to the previous view controller?
You have to set currentViewController
correctly after push/pop using scene coordinator.
However, I always met crashes when push/pop quickly while using UIButton
as the custom view of UIBarButtonItem
.
It seems like that currentViewController
can not be set correctly in the situation above.
What I wound up doing was adding a back scene coordinator method and call that from the view controllers viewWillDisappear method:
@discardableResult
func back() -> Observable<Void> {
let subject = PublishSubject<Void>()
guard let navigationController = currentViewController.navigationController else {
fatalError("No navigation controller: can't navigate back from \(currentViewController)")
}
// one-off subscription to be notified when back complete
if currentNavDidShowObserver != nil {
currentNavDidShowObserver!.dispose()
}
currentNavDidShowObserver = navigationController.rx.delegate
.sentMessage(#selector(UINavigationControllerDelegate.navigationController(_:didShow:animated:)))
.map { _ in }
.bind(to: subject)
currentViewController = SceneCoordinatorImpl.actualViewController(for: navigationController.viewControllers.last!)
return subject.asObservable()
.take(1)
.ignoreElements()
}
Also note I added a currentNavDidShowObserver var to the scene coordinator otherwise all those supposedly “one-off” subscriptions really aren’t (just add a debug() after the sentMessage to see.) I changed all the other call sites as well.
@noahblues quick button presses can be alleviated by using an Action
. Since the action stays “locked” while executing, all you have to do is delay the end of execution to prevent double taps. Hence some of the examples in the book where I do:
backButton.rx.action = viewModel.backAction()
and the back action is something like that:
func backAction() -> CocoaAction {
return CocoaAction { [unowned self] in
self.coordinator.pop()
return Observable.empty()
.delaySubscription(0.5, scheduler: MainScheduler.instance)
}
}
1 Like
Unfortunately, it’s not work…Still push, pop,push,pop,push,crash…
Ok so there was a discussion about this on the RxSwift slack. It turns out that enabling a custom back button before the view controller is completely on-screen (therefore before the transition has completed) would lead to a crash when trying to popViewController
early, even through the VC stack shows the new VC as present.
This looks more like a general iOS-related issue than a RxSwift one. What you may want to do is add your custom left bar button item but not immediately add the action to it. Rather, set the action in your viewDidAppear
callback.
Awesome . Set the action in viewDidAppear
works. Thank you very much.
2 Likes