I have a UIViewController, MainViewController, that presents a SheetViewController using modalPresentationStyle = .formSheet. SheetViewController then presents another ViewController using modalPresentationStyle = .fullScreen. Doing this causes viewWillDisappear() to be called on SheetViewController, but never on MainViewController.
Since I have some frequent calls to a backend in MainViewController, I want to be informed when it is not visible, such that I can stop these calls. This could be done using delegates, but can it really be that there is no way that MainViewController can be informed directly by iOS when it is no longer visible on the screen?
The issue is related to how you are manipulating the view hierarchy.
If you present a VC using modalPresentationStyle = .fullScreen, UIKit removes the presenting controller.
If you present a VC using modalPresentationStyle = .formSheet, UIKit DOES NOT remove the presenting controller.
So...
From MainVC, present SheetVC as .formSheet ... MainVC is still in the hierarchy.
From SheetVC, present FullScreenVC as .fullScreen ... SheetVC is the presenting controller, so it is removed from the hierarchy -- but MainVC is still where it was.
When you dismiss FullScreenVC, SheetVC is again added to the view hierarchy, on top of MainVC.
Related
i have a label with a TapGesture in a table view cell that should dismiss the view controller, which is embedded in a Navigation controller, if its pressed.
Normally I would do it like that:
_ = navigationController?.popViewController(animated: true)
OR
self.dismiss(animated: true)
But this is not working inside the table view cell class.
Would be great if someone could help me!
For it to work you should pass that obligation to the viewController which is managing your tableView. So above your tableViewCell class declare a protocol with a function which the delegate (In this case the ViewController which is holding your tableView) should implement when called. So when a delegate gets called inside a ViewController dismiss the ViewController if it was modally presented and pop it if it was pushed, so one of the methods you mentioned above should work. If you Don't know how custom protocols work, try reading this article https://medium.com/#aapierce0/swift-using-protocols-to-add-custom-behavior-to-a-uitableviewcell-2c1f09610aa1
Is there any way to make sure resetScene() gets called only after the view controller vc has been dismissed? Any help would be appreciated.
vc.present(activityVC, animated:true, completion: nil)
self.resetScene()
to make sure resetScene() gets called only after the view controller vc has been dismissed
Put the call to resetScene() inside the view controller vc's viewDidDisappear.
Or put it inside self's viewDidAppear and check first to make sure that the reason why we are reappearing is that vc was dismissed.
So I have a normal UINavigationController with its rootViewController set in the storyboard. Whenever I replace the navigation controller (don't ask why, but certain transitions in my app require the entire stack to be replaced with the new controller) using
var newController: UIViewController!
// newController is initiated at some point
...
if let navigationController = self.navigationController {
navigationController.setViewControllers([newController])
}
And I can visually see the transition occur, but neither the viewWillDisappear or viewDidDisappear methods are called. When I subsequently replace the navigation controller stack they're called as expected. Is there something special about the root the prevents them from being called?
Basically Navigation controller works as a stack of controllers where child controllers go on top of the stack.
If you want to remove a deck of view controllers from a navigation controller, you should use the pop method on their root:
navigationController?.popViewController(animated: true)
This will pop everything out of the stack and you will be able to replace the root with other controllers.
This other answer: How to detect if view controller is being popped of from the navigation controller? expands on the different options you have to detect when your controller gets popped e.g. use isMovingFromParentViewController.
I have two UIViewControllers, vc1 and vc2.
I want to switch between them. But before loading the view of the new view controller, I want to destroy/release/remove (I'm not sure abt the correct word to use here) the previous viewcontroller.
For example, when I am switching to vc2 from vc1 ,I want to destroy vc1 completely, so that when I return to vc1 from vc2, vc1 will be loaded from scratch (i.e. viewDidLoad will be executed).
Which type of view switching should I opt for?
presentModal...
addSubview.
I am not using a navigation controller.
Currently I am using the presentModal... method, but when I use dismissModalViewcontroller on the newly presented view controller, it doesn't show up a new instance of the previous view controller. Instead, it shows the already running instance of it.
I want the viewDidLoad method of the previous view controller to run when I dismiss the newly presented view controller.
What exactly needs to happen in viewDidLoad?
You also have viewWillAppear available to you, so it could be that you could move the required functionality there and still use the modal presentation.
See this answer. You can do this with or without animation.
Animate change of view controllers without using navigation controller stack, subviews or modal controllers?
I have an app that has a UINavigationController that pushes a UITabBarController into view. This UITabBarController has four tabs, one of which shows a custom UIViewController, an instance of EventInformationViewController. A button in this custom view controller in turn pushes another custom view controller EventRatingAddViewController into view. An action in this view controller should invoke a method in the EventInformationViewController instance. The following code makes the app crash instead:
// get the index of the visible VC on the stack
int myIindex = [self.navigationController.viewControllers indexOfObject:self.navigationController.visibleViewController];
// get a reference to the previous VC
EventInformationViewController *prevVC = (EventInformationViewController *)[self.navigationController.viewControllers objectAtIndex:myIindex - 1];
[prevVC performSelector:#selector(rateCurrentEvent:)];
I thought that the viewControllers property kept an array of all VCs on the navigation stack, so the index the currently visible one minus one should point to the VC that pushed the currently visible VC into view. Rather, it seems to point to my UITabBarController:
-[UITabBarController rateCurrentEvent:]: unrecognized selector sent to instance
What is up with that and more importantly, how do I get a pointer to the VC that pushed the currently visisble VC into view?
EDIT: I ended up creating a delegate protocol for the EventRatingAddViewController and assigning the EventInformationViewController as delegate. This works well - still I am thinking there should be a way to access the pushing VC through the navigation stack.
I'm pretty sure that that UITabBarController did indeed push your current view controller, but that what you are looking for is the view controller of one of this UITabBarController's tabs, the view controller that was visible in the UITabBarController at the time this UITabBarController pushed your view controller on the navigation stack. Possibly this UITabBarController pushed your view controller on the stack, because it was told to do so by the visible tab's view controller, so it would be something like this: [self.tabBarController.navigationController pushViewController:someViewController];.
The way to find out what view controller was shown in the UITabBarController at the time your view controller was pushed on the stack, is to use the .selectedViewController property, so that would result in something like this:
// get the index of the visible VC on the stack
int currentVCIndex = [self.navigationController.viewControllers indexOfObject:self.navigationController.topViewController];
// get a reference to the previous VC
UITabBarController *prevVC = (UITabBarController *)[self.navigationController.viewControllers objectAtIndex:currentVCIndex - 1];
// get the VC shown by the previous VC
EventInformationViewController *prevShownVC = (EventInformationViewController *)prevVC.selectedViewController;
[prevShownVC performSelector:#selector(rateCurrentEvent:)];