I have a main container controller that initializes child view controllers.
I'm trying to learn to use UIStoryboards tho and I'm stumped as to how to get out of a storyboard.
Here's the flow:
Once I get to the end of a storyboard's scenes, how should I get out of storyboard and back to my container controller?
Should I keep a pointer to the storyboard? What would I do with it?
Should I keep a pointer to the initial view controller (which is the one I explicitly add as child)? It's .view won't be on screen at the end so I don't know what I would do with that either.
Try looping through the childViewControllers
for (UIViewController *vc in self.childViewControllers) {
// do something
}
What do you mean "out of a storyboard". You don't need to save a pointer to your storyboard as self.storyboard should work in most cases. If you use a UINavigationController as your entry point in your storyboard with your initial view controller as it's root view, all you have to do to get back to your initial controller is
[self.navigationController popToRootViewControllerAnimated:YES];
Also, you can get a reference to your storyboard like this as well:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
Related
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];
Upon pressing a button in ViewController A, I want that view controller to completely be forgotten as I move to ViewController B. Here is my code to get to code B:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
MapViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"MapView"];
[self presentViewController:vc animated:NO completion:nil];
Is there any way to stop the view controller that calls presentViewController from staying in memory? How can I dismiss it upon exit?
One Possible way to do it, is you have the following Setup.
Navigation Controller -> VC_A -> VC_B.
Here when you click on the button in VC_A, you ask the Navigation controller can remove VC_A from the navigation Stack & create and VC_B as the root view controller on the navigation stack and present it.
I still think this is not the right approach. If you could explain why you want to get rid of VCA, we might be able to think of a better solution.
dont remove from memory you can update its contents in viewWillAppear or viewDidAppear
I added a Navigation Controller to my storyboard and it appears like so:
Now in the table view controller, I gave the TableViewController a storyboard id and class to a TableViewController Controller
When I run my app, I don't see the Navigation Bar at the top. This has been extremely frustrating and can't find a solution anywhere. PLEASE HELP
To get to the scene, someone clicks a button and this code runs and it goes to my Table View Controller:
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
LHFileBrowser *LHFileBrowser = [storyBoard instantiateViewControllerWithIdentifier:#"FileBrowser"];
[self.navigationController pushViewController:LHFileBrowser animated:YES];
[self presentViewController:LHFileBrowser animated:YES completion:nil];
The error is in your code.
If you want to (modally) present a view controller when the user presses a button, you need to present the navigation controller (which will contain the table view controller), not the table view controller itself.
Right now, you're presenting the view controller, which won't show it being embedded in a navigation controller.
Also, you're mixing up two different approaches, by trying to push a view controller onto a navigation controller stack, and also presenting the view controller.
Code Sample:
Here's what you apparently mean to do:
UIStoryboard *storyboard = self.storyboard;
UINavigationController *navigationController = [storyboard instantiateViewControllerWithIdentifier:#"MyNavigationControllerID"];
LHFileBrowser *rootViewController = [navigationController topViewController];
// Configure your LHFileBrowser view controller here.
rootViewController.someProperty = ...;
// Modally present the embedded view controller
[self presentViewController:navigationController animated:YES completion:nil];
If you want to change the presentation or transition style, you can set those details in your storyboard.
You didn't explain why you had to programmatically add buttons, but Storyboard segues would have instantiated and presented an embedded view controller for you, without you having to have done it in code.
The more you can do in Storyboard, the less code you have to maintain, support, and update, and the more likely your app will still work properly when a new SDK is released.
Update:
The better way to do this is to let Storyboard do it for you, by adding a segue from the button to the navigation controller that you want to present.
I tried many answers here but none of them had the same exact scenario.
I'm trying to navigate to a UIViewController that's within a separated storyboard in a different bundle, so far I was able to navigate to it but am unable to return to the previous UIViewController. The method that invokes the external view controller (TabBarController) is implemented as follows:
+(void) launchExternalUI: (UIViewController *) previousViewController {
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle: [self frameworkBundle]];
TabBarController *vc = (TabBarController*) [sb instantiateInitialViewController];
[previousViewController.navigationController pushViewController:vc animated:YES];
[vc release];
}
Now the method within TabBarController that should return to the previous view controller:
- (void) navigateToPreviousViewController: (UIGestureRecognizer *)gesture {
[self.navigationController popViewControllerAnimated:YES];
}
In TabBarController, if I print all the viewcontrollers within self.navigationController, all I see is the TabBarController, shouldn't I see the previous view controller that pushed this on launchExternalUI ? The [self.navigationController popViewControllerAnimated:YES]; has no effect at all. I'm a bit lost on this.
It's also important to notice that previousViewController is defined in a local storyboard and TabBarController is implemented in a different .framework, would that cause the issue?
Thanks in advance for all the help!
**Edit: The navigation flow I need is storyboard1:VC1->storyboard2:VC2->storyboard1:VC1, I can get storyboard1:VC1->storyboard2:VC2 part to work but not storyboard2:VC2->storyboard1:VC1
I often split projects up into various Storyboards, and have created a dynamic view controller that handles the task of loading the appropriate controller from a secondary storyboard, whilst maintaining the navigation tree.
I've created a sample project and uploaded to github as it's easier than explaining all the steps here. The key part to note is the User Defined Runtime Attributes for each of the DynamicStoryboardViewControllers in the Main.stoyboard. Note also that each of the secondary storyboards need the "is initial View Controller" checked for one of your viewControllers. Not included in the example is loading a specific scene from a storyboard. This is no more than adding the "sceneName" dynamic runtime attribute much in the same way as the storyboardName attribute is added.
it's a quick sample so a little rough, but you'll get the idea of how it all works. Feel free to ask questions if you get stuck.
Cheers!
EDIT:
It dawned on me that perhaps you don't have a navigationController in the view hierarchy (as i do in my sample). And in any event, seemingly, you won't have much control over where your tab bar is introduces. So without a navigationController the [self.navigationController popViewControllerAnimated:YES] won't work;
You should test for this and either call the popViewControllerAnimated as you do, or call dismissViewControllerAnimated ;
if(self.navigationController){
[self.navigationController popViewControllerAnimated:YES];
}else{
[self.presentingViewController dismissViewControllerAnimated:Yes completion:nil];
}
Hope this helps, if not, perhaps you can supply some sample code.
Create an unwind segue within your original view controller, and use segues instead of pushing/popping view controller views onto the view array.
To create an unwind segue you should create an unwind method in the ORIGINAL viewcontroller:
- (IBAction)unwindToOriginalViewControllerSegue:(UIStoryboardSegue*)sender {
; //TODO: anything special you need, reference via sender
}
Then, in your NEW viewcontroller, in the storyboard, drag from the controller icon to the Exit icon. Link that to the Unwind segue you named above.
UIViewController * YourViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Storyboard Identifier"];
[self.navigationController pushViewController:YourViewController animated:TRUE];
There are two views in my application.
After launching the app I switch the view with button like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:[NSBundle mainBundle]];
UIViewController *view = [storyboard instantiateViewControllerWithIdentifier:#"view_a"];
[self presentViewController:view_a animated:NO completion:nil];
But whenever i switch the view the code above initialize the view.
I want to maintain previous status of the view.
How can I solve this problem?
instantiateViewControllerWithIdentifier: always returns a new instance of an UIViewController.
You need to keep a reference to a previous one if you don't want to create it over and over.
On an iPad this will present the second view modally, as dictated by the views modalTransitionStyle. So there you could get back to the original by calling dismissViewControllerAnimated:completion: on the new ViewController.
On the iPhone you can use a UINavigationController in your storyboard to push and then pop the secondViewController.
As long as you are using the storyboard, you can set up the transition there and the perform it using - performSegueWithIdentifier:sender: from your button. Or for that matter you can connect the segue directly to your button in which case the transition will be performed without additional code.