I just have a quick question about recommended ways to implement a master-detail view hierarchy in iOS--the kind where selecting a row in a table on one screen pushes a details view for that item onto the navigation stack.
Specifically, should I reuse the same instance of the details view controller and just change its target and reload it each time, or should I instantiate a new instance of the view controller each time?
I'd prefer the first method, as it just seems generally more efficient, but I'm having trouble figuring out how to set the target and do the reload (especially the first time, when the view controller has not yet even been initialized--I'm using storyboards and that pretty much handles all of the initialization itself).
Or perhaps instead of setting the target on the child view controller, I could set it on the parent, such that each time the child view controller is shown, it reloads itself based on the parent selection? That actually sounds like the best bet so far, just looking for tips/warnings from anyone who's run into this before.
First, there's nothing wrong with creating a new view controller each time. If you use segues, that's what you'll get, since segues always instantiate new controllers. The detail controller will be deallocated when you pop or dismiss it anyway, so it won't persist.
If you want to use the same controller, you have to do your push or presentViewController in code. You can still setup the controller in the storyboard. Give it an identifier, but don't connect it up with a segue. In code, you check for the existence of your controller (you'll need a property for it), and if it doesn't exist, create it.
if (! self.detailController) {
DetailController *dvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyIdentifier"];
}
self.dvc.whateverProperty = self.somePropertyIWantToPass; // pass some date to it
[self.navigationController pushViewController:dvc animated:YES completion:nil];
Related
I am not even sure how to title this question. I will change the title as you might suggest later.
Basically, I have a sliding view controller for my side menu. Login view controller is above the sliding one. So I want data in my sliding view controller to be reloaded when login is complete and I open side view. I have tried to use delegation, but could not figure out how to set sliding view as delegate for login view controller.
What would be the best approach here. I am using ECSlidingViewController for side menu. Maybe there is a way to reload view controller using self.storyboard or something else?
Thank you.
Why don't you update the data when its -viewWillAppear is being called? If the view isn't visible, does it really matter to update its contents? I'm assuming that your slidingviewcontroller does the right thing and shows and hides its views so that viewWill/DidAppear and ...Disappear are all getting called.
If you have to update it, then I wouldn't necessarily use delegation. If the slidingviewcontroller is a persistent object and always available, say via the rootviewcontroller, then declare a method, such as -updateData, and call it directly.
For example:
slidingViewController = self.rootViewController.slidingViewController;
[slidingViewController updateData];
Alternatively, you may have to get the rootViewController from your ApplicationDelegate or window. Depends on where you have slidingViewController and rootViewController stashed away.
Create a new single-view project (e.g., 'Test')
Within the main storyboard, create two view controllers with titles One and Two - make One the initial view controller
Place the label One within the content of view controller One and label Two within Two
Include the following within the viewDidLoad of TestViewController.m:
// instantiate the new view controller
UIStoryboard *storyboard = self.storyboard;
TestViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:#"Two"];
// Change the view
viewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:viewController animated:YES completion:nil];
Save, build, and run.
I consistently get an error of the Storyboard does not contain view controller 'Two' variety. Using breakpoints, I've discovered that the problem is at the instantiation step. Yet the above code is taken directly from Apple's View Controller Programming Guide.
I've combed this site and discovered many people having problems with instantiating view controllers programatically. Any definite solution?
There's no bug here -- you're just writing inappropriate code. The code snippet you've given works fine if you put it in an action and trigger it with a button, a timer, etc. But you're attempting to present another view controller modally before the view controller running the code has even gotten around to displaying its own view.
-viewDidLoad is called when the view controller's view has been loaded from the storyboard or .xib file; it's an opportunity to do any initialization that had to be deferred until the view hierarchy comes into existence. However, the view isn't actually displayed at that point. You need to wait until you get a -viewDidAppear message to know that the view is on screen. So, you can imagine that it doesn't make a lot of sense to try to present some other view controller before the current one has even settled in.
I consistently get an error of the Storyboard does not contain view controller 'Two' variety.
In that case, you haven't properly assigned a storyboard identifier to the view controller. Select view controller "Two" in the storyboard editor and then look at the identity inspector. You need to set the identifier like this:
I've combed this site and discovered many people having problems with
instantiating view controllers programatically. Any definite solution?
Did you also look at the answers to their questions? That's how this site works -- we answer questions not just for the people that are asking them, but also to help others in the future who may have similar questions. Identifying a UIStoryboard is a good example of a question similar to yours with an answer that probably would have helped you.
I programmed my app initially for iPhone using a tab bar controller were the view controllers are initialized once and stays persistent - it does not initialize a new instance of the view controller when I tap the tab bar.
on the iPad I am using a different GUI were instead I have one main view that always stays on the screen, and the rest are popovers segueing from the main view.
I want the popovers to stay persistent (only initialize once) what is the best way of archiving this. If I had been using *.xib files I could have initialized the popover´s view controllers in the main view and then sent a copy of them when segueing, and that way only ever have one instance of them. But I am using Storyboards.
You can't use segues if you want your controllers to be persistent, because segues always instantiate new controllers. You can still use the storyboard, but you have to leave the controllers unconnected, and instantiate them in code, and assign them to a strong property. So, something like:
-(void)presentPopover {
if (! self.vc) {
self.vc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyController"];
}
// do what you want here to put vc on screen
}
I found a solution and actually it´s easy, just use a UIPopoverController and initialize it with the view controller you want to present. In this way it will not instantiate a new instance each time a popover is requested.
if (!popoverController)
popoverController = [[UIPopoverController alloc]initWithContentViewController:bellViewController];
[popoverController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
popoverController.delegate=self;
I have about 20 View Controllers, chained together with Modal and Push segues. Now, at the last View Controller I want to switch back again to the first View Controller, as if the user has restarted the app. Unfortunately when I do this with
[UIViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"InitViewController"]];
[self presentViewController:viewController animated:YES completion:nil];
all of the previous view controllers are not unloaded. Not a single viewDidUnload method is called. How can this be done?
The instantiateViewController method creates a new copy of your view controller. Your existing view controllers aren't unloaded because iOS doesn't know that you want to 'go back', so to speak. It can't unload any of your existing view controllers because they're still in the navigation hierarchy. What you really want to do is 'rewind' your storyboard in some way.
Fortunately from iOS 6 there's a much improved way to do this, through unwinding. This lets you 'backtrack' in your storyboard right back to the start, which it sounds like you want to do. The WWDC videos have some examples and walk throughs, and you might also want to look at this existing SO question:
What are Unwind segues for and how do you use them?
I found that it can be done easily by calling dismissViewControllerAnimated:completion: on the first view controller in the hierarchy. Fortunately that's all it is needed to accomplish what I wanted :-)
I've seen this question a couple of times but never really answered. I'm wondering if there is an acceptable/clean way to dismiss all launched view controllers and return to the initial view controller when using storyboards (say from an action within a spawned view controller).
I know how to use delegates, but, I'd prefer to not have my initial view controller implement delegates for every possible spawned view controller. Instead, I'd just like a home button that cleans everything up and returns to the initial view controller from anywhere in the app.
Thoughts?
EDIT: Just for clarity, assume I am NOT using UINavigation Controllers.
EDIT2: Is it possible to just access the methods of the "initial view controller" from anywhere in the app like you might do with the appDelegate?
This should do it at any point. Just stick it in an IBAction and hook it up to a button :)
[self.navigationController popToRootViewController];
I ended up using a singleton. Seems to work quite well.
On the initial load of the initial view controller, I set the view controller as the singleton's property. I can then execute the following code in any action method on any view controller in the app to dismiss all view controllers and return to the initial view controller.
initialViewControllerManager *ivcManager = [initialViewControllerManager sharedInstance];
LPViewController *ivc = ivcManager.initalViewController;
[ivc dismissModalViewControllerAnimated:YES];
May not be the "right" answer, but, seems to work. And, given the complexity of my scenes, relying exclusively on UINavigationControllers would be very complicated.