Get a pointer to controller on stack - ios

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.

Related

Start second view controller of navigation stack first

There is a default calendar app.
It starts with the next view controller and back button is already there like there some other view controller was started before this one:
When you press back button you get the next view controller:
How did they do it?
In my app I need the same logic (to start a view controller with the latest or default category but users can press back button to select a different category)
If I were to do this, I would start by simply using pushViewController(animated:) to push the month view onto the navigation stack, with animated: false in the root view controller's viewWillAppear(animated:) method. The calendar would appear to the user already one level deep in the navigation stack.
So, the first controller is the year view, and then the month view is the second one pushed onto the stack, but it all happens before the user has seen any of the views. Simple, right?
Here are the docs for UINavigationController in case that helps.
I think what you want is to push the view controllers once at start. An easy way to do it is to sub-class UINavigationController and assign it to the root navigation controller in your storyboard. Then simply do the work in your sub-class' viewWillAppear method, as this will be called exactly once at startup.
Of course, you can also accomplish the same result by using a flag to only load the next view controller once if you put the push code in the first view controller's viewWillAppear.
#interface MyNavigationController : UINavigationController
#end
#implementation MyNavigationController
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIViewController *secondVC = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
[self pushViewController:secondVC animated:NO];
}
#end

How can I know when a `UIViewController` has been just pushed from a `UINavigationViewController`?

How can I know when a UIViewController has been just pushed from a UINavigationViewController ?
I obviously don't want to use viewDidAppear because that's method is called everytime that view appears, not just when it's pushed.
viewDidLoad is called before the view controller is pushed and I don't have the reference to the navigationController available
You can check this from of navigation viewcontrollers array
//Eg:
//Maintain your navigation object:
#property (strong, nonatomic) UINavigationController *navController;
//use:
if([[self.navController.viewControllers lastObject] class] == [your view controoler class]) {
// your view controller is present in navigation stack
}
There are multiple ways to do this:
The simplest way. When pushing the controller, set a flag on it. You know when you are pushing, you can run custom code at that moment.
UINavigationControllerDelegate and its navigationController:didShowViewController:animated:. Again, you can make the controller the delegate or just call a method on it externally.
Logic inside the controller - You can probably use a combination of viewDidAppear and willMoveToParentViewController:. Initially, when the controller appears, it has been pushed. After that, you can reset the "pushed" state when the controller is removed from the navigation controller when listening to changes of parent controller. Your use case is not very clear but in some cases you could just handle the first viewDidAppear call and it would work.
However, note the first two options are far easier to implement. That's because you are trying to listen to an event from a class that shouldn't know about that event.
The most sensible solution in that case is listen to that event somewhere else and setup the controller externally to handle that event.
You can do that from viewDidAppear. set a flag or bool status to true or YES when pushed from previous VC. and then put condition in viewDidAppear that if status or flag is true then only do some stuff that you want to do on push. when you pop from another view to current view set this flag or status to NO so, your condition from viewDidAppear will not execute.

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:).

How can I keep a view controller in memory after its popped from the stack?

This is an embarrassingly simple question, but I need to preserve a view controller and my current solution is not a solution.
I have a slide out table view menu that lets me select a new view controller to push to the forefront. When I select a view, it deallocs the old main view controller to push the new one. Since 90% of my functionality revolves around one view controller, I want it to stay in memory so I don't have to constantly spend resources to allocate it and either pull the last data source from core data or make a network request.
I naively tried to set a placeholder temporaryMainViewController and assign it to my center view controller before it is assigned another VC, but assigning the current main view controller to the temporaryMainViewController simply assigns the address of the main view controller- so when it's changed, so is my tempVC.
Trying to copy the view controller causes a crash.
So how can I effectively do self.temporaryMainViewController = self.currentCenterViewController; where the temp controller is assigned by value of object and not value of address?
-- EDIT --
More info:
ECSlidingViewController keeps (in my case) 3 controllers in memory- the top/center/main view controller, a left hidden controller, and a right hidden controller. My left hidden controller is a tableview, LeftMenuTableViewController , where each row, when selected, observes the view controller class I've associated with that indexPath then instantiates an instance of that class and sets it to the topViewController with a simple assignment statement. I want to keep only my initial top view controller (of class PlacesNavigationViewController (which holds PlacesTableViewController)) in memory when a new view controller is assigned to the top view.
My first approach was to declare a placeholder property in LeftMenuTableViewController, since it never leaves memory itself.
#property (nonatomic, strong) UIViewController* temporaryViewController;
then in didSelectRowAtIndexPath:
// make local variables for storyboard and the identifier of the view controller that will be pushed, then..
self.temporaryViewController = self.topViewController;
self.topViewController = [storyboard instantiateViewControllerWithIdentifier:newViewControllerIdentifier];
but this fails because the temporary controller is assigned the memory address of the topViewController- which in the next line is given a new view controller to hold.
So what I need is a way to hold the contents of topViewController so that when topViewController changes, I still have the old VC in memory.
I'm likely forgetting some obvious tenet of Objective-C, but this is giving a good bit of trouble. Let me know if I did not make something clear.
You want to use viewControllers property of UINavigationController class. The documentation is here. Basically, you can just swap 2 view controllers in viewControllers array and then use setViewControllers:animated: to make the transition.
Hope this helps!
Cheers!

Navigate to Root ViewController From Modal

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.

Resources