I have a tab bar controller with 3 view controllers and one navigation view controller in it.
That navigation view controller has a table view controller and detail view.
In my first view controller (first tab) there are three buttons. I want the first button to load the detail view in the navigation controller. (as seen on image below)
Storyboard
How can i achieve this ?
I have tried calling the method that performs the segue in LocationsTableViewController, but that gives me the error "Receiver () has no segue with identifier 'addLocation'". Although a segue "addLocation" certainly exists.
Method connected to button in StartViewController:
- (IBAction)addLocationView:(id)sender {
LocationsTableViewController *LTVC = [[LocationsTableViewController alloc] init];
[LTVC addLocation];
}
LocationsTableViewController:
-(void)addLocation
{
[self performSegueWithIdentifier: #"addLocation" sender: self];
}
Your "addLocation" segue is from the Locations Table View Controller to the Add Location View Controller, so the segue exists on the LocationsTableViewController. That is correct. However, if your current view controller isn't LocationsTableViewController, it doesn't make sense to call that segue.
You have a few options:
Storyboard Option #1: Make a new segue from StartViewController to AddLocationViewController, and perform that segue in addLocationView:.
Storyboard Option #2: Make a new segue from the button to the AddLocationViewController, and remove your IBAction. The storyboard will automatically call that segue when the button is clicked.
Programatic Option: Instantiate a new AddLocationViewController and push it onto the navigation stack: [self.navigationController pushViewController:[[AddLocationViewController alloc] init] animated:YES].
Related
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.
I have a tab view controller with a navigation controller. In the first tab item I click on a button in a view that pops up a view with animated: YES.
Then when that view is done I hit another button that dismisses it. Like:
[self dismissViewControllerAnimated:NO completion:^{
ProfilesViewController *profile = [[ProfilesViewController alloc] init];
[self.navigationController pushViewController:profile animated:YES];
//SHOW YOUR NEW VIEW CONTROLLER HERE!
}];
But everytime this code runs, it dismisses the view, DOES NOT push the profiles controller, and shows the view from the first tab bar item.
How do I push the ProfilesViewController to the screen with a Back arrow?
If you are using dismissViewControllerAnimated to dismiss that means that the VC is presented modally. As such, it doesn't have a navigation controller (so self.navigationController is nil) and thus it can't push anything into the navigation controller.
You should really add a property to the controller which is a delegate or a completion block which can be used to push the controller from another controller (the one that presents it) to dismiss and push the controller.
A second option is to pass the navigation controller, it's a similar amount of code to using a block but not so good.
A crappy option is to use the parentViewController to find the appropriate navigation controller, but that sucks for many reasons.
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!
I am having a silly problem with opening another viewcontroller on a buttonclick.
On my viewcontroller on my storyboard, I have a button. When a users clicks on this button, a new viewcontroller should be opened. As far as I know, there are 2 ways to do this.
in code
on the storyboard itself
Both methods are not working for me.
First the problem with storyboards:
(source: livefilestore.com)
I have connected my mainViewController with a navigationController, and I have connected my button with my next view (for adding an item). However, when I run the program, I get the following error:
'NSGenericException', reason: 'Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
I really don't understand this error, because the mainviewcontroller IS managed by a UINavigationController.
So, as this didn't work, I tried to handle it in code. I connected the buttonclick action to the mainViewController.h file and implemented it in the .m file.
This is the action method when a user clicks on the button:
- (IBAction)actionBtnAdd:(id)sender {
AddInventoryViewController *addinvController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddInventoryViewController"];
[self presentViewController:addinvController animated:NO completion:nil];
}
The other viewcontroller is in the same storyboard, and the ID of the other viewcontroller is "AddInventoryViewController". This gives me the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Anybody who know the problem? What would be the best approach to solve this?
Your navigation controller is not the initial view controller. Select your nav controller on storyboard, go into Attributes Inspector and select "Is Initial View Controller".
Make your navigation controller Initial View Controller.
Then create a push-segue from button to target controller.
Then you can use following method to make some adjustments before pushing:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"TargetSegue"]) {
TargetViewController *vc = segue.destinationViewController;
//vc.items = nil;
}
}
Update:
There are more options:
If your initial view controller is tabbar: Add segue from one of the tabs to navigation controller which contains your view controller.
If your initial view controller is another navigation controller:
Remove navigation controller which contains your view controller. Then add the segue between the thirdViewController and the source from which it is presented. The you can use push segue between the button and the target view controller.
If you want to do it programmatically:
Remove navigation controller which contains your view controller. Push the thirdViewController from your currently presented navigation controller. Push the targetViewController from buttonAction.
Hope it will help :)
In your Xcode .Click the storyboard on the left side and on top select Editor->EmbedIn->Navigation controller .It will automatically add your navigation controller to the first view controller .Then it would work
You have to set the Navigation controller as the first controller
See before the Navigation controller there is the storyboard starting arrow
Under your IBAction method instead of using
[self presentViewController:addinvController animated:NO completion:nil];
use this code and check
[self.navigationController pushViewController:addinvController animated:YES];
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.