ios - nav bar on a presented modal controller with storyboard - ios

I simply want to add a navigation bar (with some nav bar button) on a presented modal controller with storyboard.
Programmatically with XIBs, it looks like that :
SDMapController *mapController = [[SDMapController alloc] initWithNibName:#"SDMapController" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:mapController];
[self presentModalViewController:navigationController animated:YES];
But I have no idea how to handle it with Storyboard. I guess i have to implement some code on the -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender method but since the destinationController property of segue object is readonly, i don't really know how to do this.
Any idea ?

You have to implement the prepare for segue, only if you want to pass on some data to your presented view controller. Otherwise you can leave it empty. The presenting of the View Controller is from the Interface Builder. You add a navigation controller with it's root view controller and make a segue (ctrl + drag) to the navigation controller. Set the segue type to modal, and give it an ID. You can trigger this segue from code by calling [self perforSegueWithIdentiefier:#"MySegueID"];. If you dragged the segue from a button or a table view cell, it will be triggered automatically when you tap on it, without calling this method. As I said, in the prepareForSegue method, the segue.destinationViewController will bee the presented navigation controller. You can access it's topViewController if you need and pass some data to it.

Related

pushViewController that's not a root viewController

I have two View Controllers: LevelSelectViewController and GameViewController.
Neither are the root view controller for the app I am making (the root view controller is called MainViewController).
How can I use the navigation method pushViewController:animated: for this transition for LevelSelectViewController to GameViewController?
In my LevelSelectViewController, you click a button and the following action method performs:
- (IBAction)buttonPressed:(id)sender {
// the pushViewController:animated: method hopefully can be used
// other code
}
How can I use the navigation method pushViewController:animated: for this transition for LevelSelectViewController to GameViewController?
The term "root view controller" can be a little confusing because UIWindow has a rootViewController property, and UINavigationController has a initWithRootViewController parameter. If you're calling -pushViewController:animated:, you must have a navigation controller in your view controller graph since that method belongs to UINavigationController. If your LevelSelectViewController instance is part of a navigation stack, you can do this:
- (IBAction)buttonPressed:(id)sender {
// create a game controller
GameViewController *gameController = [[GameViewController alloc] initWithNibName:nil bundle:nil];
// push it
[self.navigationController pushViewController:gameController animated:YES];
// other code
}
A more typical thing to do these days, though, is to put all the view controllers in a storyboard and simply connect the button to a push transition.
If you're using storyboards just create a segue. If you're not passing any information you can push to it by control dragging from one view controller to another. It doesn't matter if it's the root view controller or not.
If you want to pass data create a segue on storyboards from the view controller, not the index path or button etc. and invoke the prepareForSegueMethod in the view controller with the exact same identifier as the segue on storyboards.

Add navigation to existing view controllers / segue

I have several ViewController chained together with segue in a simple hierarchy.
In each of them :
[self performSegueWithIdentifier:#"myController" sender:myVar];
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"myController"]){
MyController *controller = (MyController *)segue.destinationViewController;
controller.myVar = sender;
}
}
I'm looking for the simplest way to integrate a top navigation bar with a back button. I know I can use something like this in viewWillAppear :
UINavigationBar *myNav = [[UINavigationBar alloc]initWithFrame:CGRectMake(0, 0, x, y)];
[self.view addSubview:myNav];
But in this case the navigation bar hides a part of the view (like a "position:absolute" would in css).
I can add a Navigation Controller which solves the problem above but from what I understand this will make my segue useless, which I don't want.
This is exactly what UINavigationController is for. Your first view controller (called the root) is embedded in a navigation controller. Additional view controllers are then pushed onto the navigation controller.
UINavigationController automatically manages the navigation bar on the top and creates the back button for you.
Once you have embedded your view controller in a navigation controller, you can use push segues.

Go to another Viewcontroller

In my app, I need to go to another UIViewController with a button click, but when I did it in the new UIViewController it displays only what I set programmatically.
I used:
NewSubject *NewS = [[NewSubject alloc] initWithNibName:nil bundle:nil];
[self presentViewController:NewS animated:YES completion:nil];
"NewSubject" is the UIViewController I need to go too, however I want the computer to display also the stuff I set by the Storyboard.
Have you set in the Storyboard, in the NewSubject View Controller, in the third tab (Show Identity Inspector) the StoryBoard ID?
You should set it to some name, such as "NewSubject" and use it as follow:
NewSubject *NewS = [self.storyboard instantiateViewControllerWithIdentifier:#"NewSubject"];
[self.navigationController pushViewController:NewS animated:YES];
I want the computer to display also the stuff I set by the Storyboard.
If you're using a storyboard, -initWithNibName:bundle: is the wrong method to use. You can use UIStoryboard's -instantiateViewControllerWithIdentifier: method to create a new view controller that's defined in a storyboard, but the more typical approach is to have your button trigger a segue between the two view controllers.
Try this:
While editing your storyboard, control-drag from your button to the new view controller. A popup menu should appear that lets you choose how you want to transition between the view controllers -- push (push the new controller onto the top of the navigation stack), modal (present the view controller modally), etc. Pick the appropriate one.
In simple cases, you're done -- there's no need to write any code just to get the transition to happen. The segue takes care of creating the new view controller and performing the transition for you. However, you often want to pass some data from the existing view controller to the new one. If that's the case, implement -prepareForSegue:sender: in the existing view controller -- this method gives you a chance to pass whatever data you need. It'll look something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// It doesn't hurt to check that it's the segue that you expect
if ([segue.identifier isEqualToString:#"MySegueIdentifier"]) {
NewViewController *newVC = segue.destinationViewController;
// This is your chance to set properties or call methods to pass data to the new view controller
newVC.foo = self.foo;
newVC.bar = self.bar;
}
}

Programmatically dismiss popover from view embedded in navigation controller

I have a view controller. When I press a button in it, a popover controller with a uitableview shows up. I select a row, which shows another view with some controls in it. When I press a button that says "Save Item", I want it to dismiss the popover. How do I do this?
Here's what I've tried:
Using the delegate and protocol pattern. This hasn't worked since in order to push another view inside my tableview, the whole thing must be embedded in a navigation view controller, so when I segue, it segues to a nav controller, not the tableview which I could set the popover delegate for.
Adding my main view as a member of the view I want to dismiss from. I don't know why this doesn't work.
The Hard Clean Way
There are four view controllers in the story, plus a popover controller. I will call the three view controllers "main view controller", "nav", "vcA", and "vcB". As I understand it, "nav" is the initial content view controller of the popover and has "vcA" as its root view controller.
main view controller -> popover controller -> nav -> vcA -> (later) vcB
When you present the popover from your main view controller, you keep a reference to the popover controller. This is what makes dismissing possible, as you know.
When you create the Save button, you make its target the main view controller and its action a method in the main view controller. You will have to set this up in code; it cannot be configured from a storyboard because you cannot form an action from one scene to another. (You are able to do this because you started out with a reference to nav and vcA when you configured the popover controller initially. Thus you can hand vcA a reference to self, the main view controller. If necessary, you can then pass this reference down the chain from vcA to vcB as vcB is summoned and pushed onto the navigation stack.)
Now the user taps Save, your main view controller's method runs, and it uses its reference to the popover controller to tell it to dismiss.
The Easy Dirty Way
The heck with all that. The main view controller registers for an NSNotification. The Save button posts that NSNotification. Done. :)
The Middle Way
You could set the whole chain up in your storyboard using a popover segue, and do the dismissal through an Unwind segue matched by an unwind method back in the main view controller. I never think of this initially, because I don't like popover segues very much. But it does work.
This is how I solved my problem (sorry for the bad english):
First, Create a property of UIStoryboardPopoverSegue in the VcA and set it from the main view controller.
Nav -> VcA_ViewController
#property (strong, nonatomic) UIStoryboardPopoverSegue *popupSegue;
Then, in the Main View Controller prepareForSegue set the property:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"your segue from the mainview to the navigation"]) {
UINavigationController *navigationController = (UINavigationController *)c;
VcA_ViewController *vcA = (VRPointOfInterestsFiltersViewController *) navigationController.topViewController;
vcA.popupSegue = (UIStoryboardPopoverSegue*)segue;
} }
Now, from the VcA controller you can have the dismiss button
- (IBAction)dismissPopoup:(id)sender {
[self.popupSegue.popoverController dismissPopoverAnimated:YES]; }
Don't forget to link the popOverSegue from the MainViewController to the NavController.
Hope it helps!

Can I modally present a view controller that is part of a navigation controller?

I have a table view and a map view that need to display the same detail controller.
The detail controller is currently embedded in a navigation controller with the table view (I believe it is still using the auto-generated segue from the master-detail template).
I plan to have my map annotations segue to the proper detail controller, but just for testing I put a UIButton on the MKMapView and made a modal segue to the detailViewController through the storyboard.
When I run the app, the button on the map view does nothing and no errors are thrown/loggeed.
I know you don't have to be in a navigation controller to modally present a view controller, so am I not allowed to modally present a view controller that is part of a navigation controller?
Do I need to take the detail controller out of the storyboard nav controller and push it on to the navigation stack manually?
You can replace storyboard segues with coded segues:
create in each source controller a property destinationVC and initialise it:
#property (nonatomic, strong) UIViewController* dest;
self.dest = [self.storyboard instantiateViewControllerWithIdentifier:#"destinationID"];
Don't forget to set the identifier in storyboard for the destinationID.
To segue modal:
[self presentModalViewController:self.dest animated:YES];
To push:
[self.navigationController pushViewController:self.dest animated:YES];
You should be careful at the dest VC when you segue back.
if ([self.parentViewController.modalViewController isEqual:self]) {
//Modal
[self dismissModalViewControllerAnimated:YES];
} else {
//Push
[self.navigationController popViewControllerAnimated:YES];
}

Resources