UINavigation pushing a new root controller - ios

I am trying to push a new root controller to a navigation stack, but using a side reveal menu.
My app delegate has the following:
welcomeViewController = [[MyWelcomeViewController alloc] initWithNibName:#"MyWelcomeViewController" bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:welcomeViewController];
navController.navigationBarHidden = YES;
// Then we setup the reveal side view controller with the root view controller as the navigation controller
self.revealSideViewController = [[PPRevealSideViewController alloc] initWithRootViewController:navController];
[self.revealSideViewController setDirectionsToShowBounce:PPRevealSideDirectionNone];
[self.revealSideViewController setPanInteractionsWhenClosed:PPRevealSideInteractionContentView | PPRevealSideInteractionNavigationBar];
// Then we make the window root view controller the reveal side view controller
self.window.rootViewController = self.revealSideViewController;
Once the welcome view controller is displayed, the user logs in. Once logged in the following process runs again from the App Delegate.
self.navController.navigationBarHidden = NO;
[self.navController setTitle:#"Home"];
[self.navController pushViewController:homeViewController animated:NO];
I then have a side view controller setup which is a table view with custom cells setup.
When a row is selected I need to push a new root controller onto the navigation controller. I try this by using the following in the table view for the cell selected.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[self.navigationController setViewControllers:[NSArray arrayWithObject:accountViewController] animated:NO];
Unfortunately this does not do anything. If I add the code to the App Delegate and then call the method from the table view controller then it works, however not from the .m file for the table view itself. Adding a log I can see the above is run, just does not do anything.
I am unsure if I need to do anything different on the above. For example, completely pop the views currently shown, then create the navigation controller and PPRevealSideViewController all over again. If I am supposed to, I am unsure how to pop all the current views to then push the new to the window, not from the AppDelegate.
The reason I do not want this in the App Delegate is because it is the incorrect way to approach this, and I would then need a separate method for each new root controller I would like to push from the menu, so the App Delegate would become very large.

Check UINavigationController.h:
#interface UIViewController (UINavigationControllerItem)
#property(nonatomic,readonly,retain) UINavigationController *navigationController; // If this view controller has been pushed onto a navigation controller, return it.
It means when you do myViewController.navigationController you will either get nil if myViewController is not pushed to any navController or the navController reference myViewController is pushed into.
As I understand your tableViewController is not pushed into the navController stack, that means you can't get the navController with tableViewController.navigationController. Instead you'll need to use anyViewControllerInTheStack.navigationController or if the navController is the rootViewController of your keyWindow, by
((UINavigationController*)[[UIApplication sharedApplication] keyWindow].rootViewController)

Add something like this to your AppDelegate.h:
#define XAppDelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate])
Now you can access any iVar of AppDelegate from any .m file in your project.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[XAppDelegate.navController pushViewController:accountViewController animated:NO];
Make sure you add the correct imports.
One more thing: It's good to pop the login window from your navcontroller once you are done Logging in.
Hope this helps.

Related

Navigation View Controller from Subview

I have been working on this problem for a while and thought I would ask for some help. I have 3 view-controllers: 1 Navigation Controller, 1 Main controller and 1 detail view controller.
Within the main view controller, I have a series of subviews with buttons. Due to the class structure, however, I am unable to directly call self.storyboard to get the current storyboard object.
I have tried 2 different methods, a variety of ways, and am still unsuccessful. I posted my methods below and described what is and what is not happening in each segment. The overall goal is to present a child view controller (the detail view) by tapping a button in a subview, of which does not have access to the parent storyboard directly.
Method 1
//Instantiate the new view controller
ProfileViewViewController *tempViewToShow = [del.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"profile"];
// Pass data into the new view controller
tempViewToShow.thisUser = self.postUser;
// Output a simple log to ensure both were created
NSLog(#"Temp User Name: %#, Profile Desc: %#", [tempViewToShow.thisUser getFullName], tempViewToShow.description);
// Using the AppDelegate for the RootViewController, present the detail view
[UIApplication.sharedApplication.delegate.window.rootViewController presentViewController:tempViewToShow animated:YES completion:NULL];
Issues
The issue with this series is that the detail view does not carry the navigation controller (since it is not mentioned), however, this way still shows a full View Controller
Method 2
...
// Use the Delegate and the navigation controller to present the new view controller
[UIApplication.sharedApplication.delegate.window.rootViewController.navigationController presentViewController:tempViewToShow animated:YES completion:NULL];
Issues
Does not display anything
Method 3
// Use the recommended 'pushViewController' for the navigation controller to carry over
[UIApplication.sharedApplication.delegate.window.rootViewController.navigationController pushViewController:tempViewToShow animated:NO];
Issues
Does not display anything
En toto, how would I make this work? What lines would I modify and how? Thanks!
You can solve this issue like this:
ProfileViewViewController *tempViewToShow = [del.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"profile"];
UINavigationController *naviController = [[UINavigationController alloc] tempViewToShow];
And then do this :
[UIApplication.sharedApplication.delegate.window.rootViewController presentViewController:naviController animated:YES completion:NULL];
You can create instance of storyboard from storyboard name.once you have correct storyboard instance, get NavigationController from Its identifier, and detailviewController from its identifier. Push detailviewcontroller on Navigationviewcontroller.
get storyboard-- replace name of your storyboard in "MainSToryboard"
UIStoryboard *storyboard =
[UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:[NSBundle mainBundle]];
get instance of Navigationcontroller - replace identifier:
UINavigationController *navController =(UINavigationController *)
[storyboard instantiateViewControllerWithIdentifier:#"navcontroller"];
get detailviewconrtoller :
UIViewController *detailvc=
[storyboard instantiateViewControllerWithIdentifier:#"profile"];
Push detail on current navigationcontroller:
[navController pushViewController:detailvc animated:YES];
I found an alternate solution. The cause was because the incorrect view controller was being called by
UIApplication.sharedApplication.delegate.window.rootViewController.*
The workaround is:
In the primary view controller class, I passed the displayed viewcontroller into the delegate class. Then, from the child class I wanted to call, I referenced that view controller, and navigation controller, and it worked just fine. My final code is below:
// Create the detail View Controller
ProfileViewViewController *tempViewToShow = [del.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"profile"];
// Set the user variable in the detail view controller
tempViewToShow.thisUser = self.postUser;
// Push the view controller into the navigation controller
// Note that del.currentNav comes from this code:
/*
* In this class, create the delegate reference
* AppDelegate *del = (AppDelegate *)[[UIApplication sharedApplication] delegate]
*
* In the Delegate class, get the set the current navigation controller {let currentVC : UIViewController = passedInVC}
* self.currentNav = currentVC.navigationController;
*/
[del.currentNav pushViewController:tempViewToShow animated:YES];

Pop SWRevealViewController

I'm using SWRevealViewController to build a sliding menu. Before accessing the menu, a login view is displayed to allow the user login. Now i want to let the user disconnect and display the login view again. My question is how to pop the SWRevealViewController.
code that provides access to application after login is:
MainViewController *vMainMenu = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
vMainMenu.strURL = URL;
LeftMenu *vLeftMenu = [[LeftMenu alloc] initWithNibName:#"LeftMenu" bundle:nil];
UINavigationController *principalNavController = [[UINavigationController alloc] initWithRootViewController:vMainMenu];
UINavigationController *rearNavController = [[UINavigationController alloc] initWithRootViewController:vLeftMenu];
SWRevealViewController *mainRevealController = [[SWRevealViewController alloc] initWithRearViewController:rearNavController frontViewController:principalNavController];
mainRevealController.delegate = self;
self.viewController = mainRevealController;
principalNavController.navigationBar.hidden = YES;
rearNavController.navigationBar.hidden = YES;
self.window.rootViewController = self.viewController;
This solution is not working:
[self.revealViewController.navigationController popViewControllerAnimated:YES];
Splash *vSplash = [[Splash alloc] initWithNibName:#"Splash" bundle:nil];
[self.navigationController pushViewController:vSplash animated:YES];
Can you help me please
Thank you.
You can't go back to your LoginViewController by pop. You can add PresentViewController without animation to do this.
Splash *vSplash = [[Splash alloc] initWithNibName:#"Splash" bundle:nil];
[self presentViewController: vSplash animated:NO completion:nil];
I had the same issue. I have implemented several storyboards.
let storyboard = UIStoryboard(name: "Login", bundle: nil)
let startcontroller = storyboard.instantiateViewControllerWithIdentifier("LoginVC") as UIViewController
window?.rootViewController = startcontroller
With this snippet you can access the right View Controller.
You can use an "Unwinding Segue". The unwinding segue is like a regular segue which goes back instead of forward.
First, go to the Login View Controller and add the following method:
-(IBAction)unwindToLoginScreen:(UIStoryboardSegue *)segue {
}
The method may be empty and it can have any name; it is good to use descriptive names because you might have many unwind segues on your code and it will be useful to be able to distinguish between them.
This unwind method will act as a global constant that will be visible to all View Controllers on the storyboard.
Now, on each view controller that should be able to return to the Login Screen you create a Unwind Segue. You can do this on the Storyboard:
Select the View Controller you want; on the top bar you should see three buttons, one representing the View Controller, another representing the First Responder and another representing an Exit.
Ctrl+Drag from the View Controller button to the Exit button. A context menu will appear and will list all visible unwind methods you created on your storyboard.
On the Document Outline, an "Unwind segue" will appear for the view controller. You can add an identifier to that segue (such as "Login" in your case). Then you call performSegueWithIdentifier. Select the one you created on your Login view controller.
Doing this, the complete stack between the view controller and the login view controller will be unwinded. That is, all view controllers stacked above the Login view controller will be popped.
You can also use prepareForSegue for the unwind segues the same way you would use them for regular segues. And you can add code to the -(IBAction) method if you want to do something as a result to the segue.

Add a second UINavigationController

I have my first NavigationController set up with root viewcontroller - PhotoViewController, It's a collectionView with grid of images. When image is selected I wan't to present a second navigationController with root controller - DetailViewControler.
My first navigationController is set up in appDelegate. I really don't understand how and where should I create the second. I'm not using IB. At the moment I have this in my DetailViewcontroller viewDidLoad:
DetailViewController *detailView = [[DetailViewController alloc] init];
self.detailController = [[UINavigationController alloc] initWithRootViewController:detailView];
But when i'm trying to push a new controller, nothing happens. I guess that my code is in wrong place.
When I try to present second controller modally from my PhotoViewController, I have an error(trying to present nil viewController).
The common idea is to have a single navigation VC that contains -- and lets you navigate between -- other VCs. In the situation you describe, you wouldn't create another navigation VC. Instead, create just the detail VC and push that onto the existing navigation VC...
// assuming self is a view controller contained by a navigation controller
self.detailController = [[DetailViewController alloc] init];
[self.navigationController pushViewController:self.detailController animated:YES];
Use a parent view controller as your initial view controller and add the navigation controllers as children, then transition between them. See this blogpost that I wrote: http://sketchytech.blogspot.co.uk/2012/09/container-view-controllers-parent-view.html

Setting up a UINavigation controller and various view loads

I have setup a UINavigation controller that uses the AppDelegate as the main point of contact.
I have different methods which run such as presentHomeViewController, presentLoginViewController, which push the different view controllers to the Navigation Controller.
App Delegate - didFinishLaunching
welcomeViewController = [[MyWelcomeViewController alloc] initWithNibName:#"MyWelcomeViewController" bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:welcomeViewController];
navController.navigationBarHidden = YES;
self.revealSideViewController = [[PPRevealSideViewController alloc] initWithRootViewController:navController];
[self.revealSideViewController setDirectionsToShowBounce:PPRevealSideDirectionNone];
[self.revealSideViewController setPanInteractionsWhenClosed:PPRevealSideInteractionContentView | PPRevealSideInteractionNavigationBar];
self.window.rootViewController = self.revealSideViewController;
Is this the correct process for this?
- (void)presentHomeViewController {
// We start by dismissing the ModalViewConrtoller which is LoginViewController from the welcomeview
[self.welcomeViewController dismissModalViewControllerAnimated:YES];
// Check if the home view controller already exists if not create one
if (!self.homeViewController) {
NSLog(#"presentHomeViewController- Creating the Home View controller");
homeViewController = [[MyHomeViewController alloc] initWithNibName:#"MyHomeViewController" bundle:nil];
}
// Push the homeViewController onto the navController
NSLog(#"presentHomeViewController");
self.navController.navigationBarHidden = NO;
[self.navController setTitle:#"Home"];
[self.navController pushViewController:homeViewController animated:NO];
If I then add the following to a different class :
[self.navigationController pushViewController:accountViewController animated:NO];
No view is pushed to the stack, should I control all the movement within the AppDelegate as I have been doing, or is there betters way to approach this?
EDIT
Thanks for posting your code. So, to address your final question first, I don't recommend controlling your navigation stack from the app delegate. You should be controlling the stack from the view controllers that are the children of the navigation controller.
To that point, remember the hierarchy of view controllers: UINavigationController inherits from UIViewController, and UIViewController has properties defined for all the things you'd see in a navigation layout such navigation items and title. More importantly, it also has properties for its parent view controllers, the view controller that presented it, and its navigation controller. So, considering the hierarchy, your app delegate should only instantiate the navigation controller's root VC and the nav controller itself, and then subsequently set the nav controller's root VC.
From there, you should be pushing and popping other VCs from the VCs themselves. Remember, every VC has a property that's automatically set to point at the navigation controller it's a part of. That's why [self.navigationController pushViewController:] works. For instance, if I have a nav controller whose root VC is a UITableViewController, and tapping on one of the items in the table view pushed a new VC onto the stack, I would push that VC from the table VC and not from the nav controller class or the app delegate.
Sorry if that's confusing. Please let me know if that needs clarification and I'll give it my best. Otherwise, hopefully that gets you on the right track.

Initializing root view controller with myViewController / Using split view(sliding navigation) controller

I'm working on adopting slide (split) view controller to my project.
JT, DD, ZUUI, JW, ECS.....
All these sources suggesting initialize my root view controller in appDelegate.
Something like this....
MyMainViewController *controller = [MyMainController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:Controller];
.........
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
Problem is I can't make my view visible, my app show blank page only with a empty navigation bar.
I customized my main view using AQGrid, is this causing a problem?
My view looks different to storyboard look. (because I customized it.)
So when I do initialize I'm using "self.storyboard initialize......method".
But in appdelegate, I can't use that method.
Simply, I can't make this view hierarchy because when I initialize my view it is not visible.
ZUUIRevealController is parent of:
UINavigationController is parent of:
FrontViewController
If you are using a storyboard, don't do any of that. Instead, choose your starting view controller from the storyboard and check the "Is Initial View Controller" in the Attributes inspector.

Resources