Navigate to Root ViewController From Modal - ios

I have a root view controller (RVC) that opens up a Modal ViewController (MVC). I then navigate within the MVC to few more VC's via a push. What is the best practice to get from one of those VC's back to the RVC?
Normally I have a delegate from the Modal VC that calls up to the RVC which then dismisses the modal, but if you navigate away from it, but I'm not sure how I would do that if you navigate away from it.

Without seeing any code it is a bit hard to help but let me shoot in the dark here.
I will assume that the first controller presented inside the modal view provides the protocol/delegate to call the dismiss action.
If you use UINavigationController inside your modal view to push other view controllers on the stack you can always obtain the first controller like this
UIViewController * yourFirstController = [[[self navigationController] viewControllers] objectAtIndex:0];
// and then use your delegate to call your dismiss method
// you will need to typecast your controller based on your subclass otherwise will get warning here
if ([[yourFirstController delegate] respondsToSelector:#selector(yourCloseProtocolMethod)]) {
[[yourFirstController delegate] yourCloseProtocolMethod];
}

Don't forget that a delegate doesn't have to be a property of a UIViewController inside your model navigation stack. Consider creating a singleton class that holds a reference to the rootviewcontroller as a delegate. That way any class in your application has access to it and you aren't forced to continually pass it through to every UIViewController that requires it.

Related

Setting the rootViewController after a modal view has been presented

I have an app where I re-init the app if it's been in the background for too long. When the user opens up the app after the allotted time, the re-init happens, and I display the splash screen while I get the data I need. When I get the data from the server I set the window.rootViewController property to a new value. If the old root view controller has had a view controller presented modally, and that view controller was being displayed when the re-init happens, dealloc doesn't get called on the view controller (I've tested this by putting NSLog's in the dealloc method). In the case where a modal view controller was not presented, the dealloc gets called as expected.
Does anyone know of a solution to this? I'm not sure if it's an Apple bug, or if it's something that I need to handle on my own.
The solution that I came up with was before I set the RootViewController, I call
- (void)_dismissRootViewControllersModalViewsIfAny {
UIViewController *rootViewController = self.window.rootViewController;
if (rootViewController.presentedViewController || rootViewController.presentingViewController)
{
[rootViewController dismissViewControllerAnimated:NO completion:nil];
}
}
This is a method that I created, which makes sure that if there is a modal view controller to dismiss, it will be dismissed.
Your modal view is presented by the rootViewController (it's presentingViewController property is set to the rootViewController), that might be the source of your problem.
You can set your rootViewController to an instance of UINavigationController and then just use its setViewControllers:animated: method to display a freshly instantiated view controller instead of switching the window's rootViewController.

displaying a ViewController from a non UI class

I perform some data loading tasks from an Ojective§C class and once everything is loaded, I simply wants to display a Viewcontroller subclass prepared in a storyboard.
So when everything is ok, the following method is called:
- (void)loadingNextView
{
CABBndGSite *mySite = [CABBndGSite alloc];
CABBndGSelectLanguageViewController *vc = [[mySite myRootViewController].storyboard instantiateViewControllerWithIdentifier:#"SelectLanguageViewController"];
[[mySite myRootViewController] presentViewController:vc animated:YES completion:nil];
}
So I verified that myRootViewController is not nil. It's a UINavigationController class.
vc is not nil so it found my view in the storyboard.
Anyway, the presentViewcontroller message seems to doing what expected.
Certainly a stupid mistake but my poor iOS programming knowledge lets me in the fog!
I use this code from ViewController subclasses with success and as here I get a valid ViewController pointer, I don't understand why it doesn't work.
I also tried to implement the AppDelegate method explained here How to launch a ViewController from a Non ViewController class? but I get a nil navigation pointer. Maybe something not well connected in my application
May I have some explanation?
Kind regards,
UINavigationController maintains a stack of view controllers. You can access this stack through the viewControllers property. To present your view controller, you can:
(a) have the navigation controller push the new view controller on to
the stack (pushViewController:animated:);
(b) have the top view controller in the view controller stack present
the new view controller modally (presentViewController:animated:completion:), or;
(c) add the new view controller to the view controller stack array
manually by assigning a new viewControllers array to the navigation
controller's viewControllers property (setViewControllers:).

Get a pointer to controller on stack

I have a left view controller (sliding menu controller) in use for example called LeftMenuTableViewController.
When a user logs out a modal view controller is displayed but the tableview controller stays in the background. When they login the controller is dismissed and the others shown again.
How do I do the following:
1. Check that the table view controller does exist on the stack.
2. Create a pointer to this controller on the stack without alloc init (creating another one)
I need a pointer to it so that I can load the tableView reloadData method once logged in, if it exists on stack.
There are several different ways to do this. It just depends on the look you want to achieve.
I think the neatest way to do this would be to use a UIViewController with two container views, then embed your login view controller in one container view, and your tableview controller in the other container view.
After you create references to these containers in their view controller, you can animate each container view however you want to, such as sliding out the login view then hiding it. This way, your "master" view controller will always have a reference to both your tableView controller and your login view controller.
If you don't want to move away from the method of using modal transitions between view controllers, here is what I tell you:
There doesn't exist a "stack" of view controllers anywhere as you describe it. You will have to create one yourself, probably in the app delegate.
In order to do this, you will need to create a reference to the view controller you need a pointer for in your app delegate, make a property for it, and synthesize it. It would look something like this:
MyAppDelegate.h
#interface MyAppDelegate : UIResponder
{
MyViewControllerClass *myViewController;
}
#property (nonatomic) MyViewControllerClass *myViewController;
MyAppDelegate.m
#synthesize myViewController;
Then, in the viewDidLoad method of the view controller....
[[[UIApplication sharedApplication] delegate] setMyViewController:self];
After you set this up, you can check to see if a pointer to that view controller exists by saying
if([[UIApplication sharedApplication] delegate].myViewController)
{
//does exist
}
else
{
//does not exist
}
To access a method on that view controller, just say something like
[[[UIApplication sharedApplication] delegate].myviewController performMyMethod]
Hope that helps you.

Reference 2nd subview in Modal

I have a master/detail app and I'm displaying a modal from my my master view. My modal contains a navigation controller and two sub views. The first subview (login) pushes to the second subview (pick and initialize data). How do I get a reference to the second subview so I can catch it's delegate call in my master?
The goal is to update my master table view after the user logs in and initializes the core data database from a JSON webservice.
Thanks for any help!
When you say 'subview' I assume you mean the second viewController of your modally-presented navigationController's stack. If so you can't refer forward to it from your master view before you present as it is not yet created (the modal NavController is responsible for that).
You could refer to the first viewController - as
[[self presentedViewController] topViewController]
pass yourself as delegate to that controller, which would then be responsible for passing your delegate reference onwards to the second controller when it is created.
More simply though, you can refer back to the presenting setup from your second viewController thus:
[[self navigationController] presentingViewController]
This will get you to a reference to the container controller from where the presenting took place from where you can easily get a reference to the detail viewController.
You could use that route to get your data back to your master table view.
see also my answer here:
How can I pass value between NavigationController and ViewController with StoryBoard?

How to send message to a parentViewController that is a subclass of UIViewController?

I have a UIViewController (MyViewController) and another view controller i'm presenting modally though MyViewController (call it SecondViewController). I want to be able to send a message to MyViewController from SecondViewController by using
[self.parentViewController hideSecondViewController];
But since parentViewController is defined as a UIViewController, and hideSecondViewController isn't a method of UIViewController, I get a warning saying "UIViewController may not respond to 'hideSecondViewController'". It works fine, because it CAN send the message successfully during the program, but since I #import SecondViewController in MyViewController, I can't #import MyViewController in SecondViewController. Any way around this?
When it comes time to dismiss a modal
view controller, the preferred
approach is to let the parent view
controller do the dismissing. In other
words, the same view controller that
presented the modal view controller
should also take responsibility for
dismissing it whenever possible.
Although there are several techniques
for notifying a parent view controller
that it should dismiss its modally
presented child, the preferred
technique is delegation.
In a delegate-based model, the view
controller being presented modally
must define a protocol for its
delegate to implement. The protocol
defines methods that are called by the
modal view controller in response to
specific actions, such as taps in a
Done button. The delegate is then
responsible for implementing these
methods and providing an appropriate
response. In the case of a parent view
controller acting as a delegate for
its modal child, the response would
include dismissing the child view
controller when appropriate.
Read more at the View Controller Programming Guide for iOS.
P.S:
since I #import SecondViewController
in MyViewController, I can't #import
MyViewController in
SecondViewController.
To solve a circular dependency problem you can use a forward declaration.
It would be better to redesign your architecture as albertamg proposed but this should work:
[self
dismissModalViewControllerAnimated:YES];
you can call dismiss on both the presenting and presented view controller and it will do the same thing.

Resources