I have used Cocos2D for a few months. I use:
MyCCScene *sceneToRun = [MyCCScene node];
[[CCDirector sharedDirector] replaceScene:sceneToRun];
This loads the new scene, cleans the previous scene from memory, then displays the new scene. Pretty straightforward.
Question:
Is there something similar to this in UIKit?
-Modal segues keep the old ViewController in memory, their purpose is different.
-Push segues work in UINavigationControllers only.
I think the way to go is to implement my own Container ViewController which handles its child ViewControllers and memory the way I want. http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006926-CH3-SW81
But I am not entirely sure. Isn't there a more straightforward method? This looks like a huge overkill for such a simple and obvious thing.
I might have found a nice solution. One should use a UINavigationController, and manage its viewContollers property (which is the stack of UIViewControllers) manually. After (or before) the new UIViewController loaded, you can delete the old UIViewController from the stack, and thus purge it from the memory.
This post helped:
How can I pop a view from a UINavigationController and replace it with another in one operation?
This way you can make a program flow, where only when UIViewController is in the memory at a time, and you replace them when you need a new UIViewController.
Related
I'm trying to make a custom tab bar controller by creating a view controller, then add buttons at the bottom of the screen. In this VC, I'll have reference to 4 other view controllers and switching them around when user click on the buttons. I'm not sure if this approach is causing any problem compare to using UITabBarController.
I need to do it this way because the middle tab button will have a 3D object in it, and I think I can't do this with default UITabBarController.
What I'm thinking is that this approach would lead to out of memory issue because I keep too many objects (4 view controllers) alive in memory, and each view controller has lots of image & animation in it. Not sure if this is the same as UITabBarController or Apple has some good way to deal with memory management here.
Please help!
Thanks.
In general I think you are over-optimizing too early if this isn't even a visible problem yet. If you implement your container properly using UIViewController Containment (A good guide here), you shouldn't have any memory issues.
Examples from that writeup:
When you are adding a child view controller (switching to a "tab"), you would do something like:
UIViewController *newTabViewController = [UIViewController new];
[self addChildViewController:newTabViewController];
[self.view addSubview:newTabViewController.view];
[newTabViewController didMoveToParentViewController:self];
Then when you are transitioning FROM a tab, along with the above code to go to a new tab, you would do:
[oldTabViewController willMoveToParentViewController:nil];
[oldTabViewController.view removeFromSuperview];
[oldTabViewController removeFromParentViewController];
Using the containment APIs will call all of the appropriate view lifecycle methods on the viewcontrollers being transitioned (viewWillAppear: viewDidAppear: viewWillDisappear: viewDidDisappear:)
In order to make your individual tabs more memory efficient, just make sure to do any appropriate clean-up in viewDidDisappear: which you can then rebuild in viewDidAppear:.
That said, this is unlikely to be an issue unless perhaps your individual viewcontrollers are keeping very large images in memory or something like that. You have to keep in mind that all existing containers also keep all of their viewcontrollers in memory without issue.
I have a stack of UIViewControllers currently, each is a modal ViewController presented over the previous one. My problem is that I do not need a stack of UIViewControllers, I only need the last one. So when a new UIViewControllersis presented, its parent should be purged, deleted completely from memory. My app will never need those viewcontrollers again.
I have read this: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html
But this pattern only cares with memory freeing if the app gets a memory warning. And doesn't purge viewcontrollers, only their content. I would like to do it in a more manual manner... Is this possible, or it is not a common practice in iOS, and I should rely only on memory warning messages.
The easiest way is to avoid creating the stack in the first place. Instead of presenting new modal controllers over existing ones, have your root controller dismiss the existing one first and present each new one.
I might have found a nice solution. One should use a UINavigationController, and manage its viewContollers property (which is the stack of UIViewControllers) manually. After (or before) the new UIViewController loaded, you can delete the old UIViewController from the stack, and thus purge it from the memory.
This post helped:
How can I pop a view from a UINavigationController and replace it with another in one operation?
This way you can make a program flow, where only when UIViewController is in the memory at a time, and you replace them when you need a new UIViewController.
I'm moving my App to Storyboards and, so far, so good.
However, I've found something that I don't really understand and worries me. I would appreciate if someone can provide some insight on this.
My app uses a normal Navigation Controller. For moving "forward" to new View Controllers, I'm using custom segues; no problems there. However, there's a point in the App where I want to move back to the beginning of the Navigation Stack. I have also configured that "navigation" using a custom segue, for that, I created the segue in Interface Builder by dragging the last view controller to the first one (that already looks weird to me), and I've implemented the custom segue perform method in the following way:
-(void)perform
{
UIViewController *src = (UIViewController *)self.sourceViewController;
UIViewController *dest = (UIViewController *)self.destinationViewController;
[src.navigationController popToRootViewControllerAnimated:NO];
// Custom animation code here
}
... It works great. However, I don't understand why it works. In my mind, the custom segue should be instantiating a new instance of my first view controller and assign it as "dest", but it looks like the segue is smart enough to realize I want to navigate to a previous, existent, instance of a View Controller and, instead of creating a new instance, it assigns to "dest" the existing one.
Does anybody know if using segues in this way is ok? Is it possible that it works by chance but might stop working in the future? Am I wasting memory in anyway as the segue is instantiating a View Controller I'm not going to use?
Thanks a lot in advance!
Am I wasting memory in anyway as the segue is instantiating a View
Controller I'm not going to use?
Yes sir! By using a segue, you effectively allocate a new view controller as it's needed to set the DestinationController property for your custom segue. Test by yourself : add a static counter into your root controller, increment it each time this class is initialized and display it in your view : you'll see it getting incremented every time you pop to root using this trick.
Does anybody know if using segues in this way is ok?
As long as you're effectively wasting memory, no!
There's at least one solution to this problem : release the DestinationController of the segue in your (void)perform implentation. This is really quick to implement, but kinda ugly since you allocate and immediately release your view controller every time... even if it's better than just leaking it, it's not what I'd call a good practice!
To my mind, a better way to achieve what you want would be to not use a segue for that transition, just to use a button or whatever and call popToRootViewController:animated when getting a touch on this button.
Is it possible that it works by chance but might stop working in the
future?
For both the first solution I suggested and the way you're currently doing it, I see absolutely no reason : these are not complicated tweaks, just 'bad-implemented' standard navigation. The second solution is perfectly normal so no worries.
Im transitioning from one view controller to another UINavigationController by using a modal segue. Its important for me that this view controller (and its child view controllers) stay in memory so specific references are kept up. Although obviously exactly this not happening. When debugging the viewWillAppear function the rootViewController (viewControllers[0]) reference points to different memory addresses between calls (and contains nil values, my actual problem).
Now there two possibilities which could cause this issue:
The UiNavigationController became destroyed
The rootViewController became destroyed
But to make it really confusing, none of them did happen; neither the UINavigationController nor the rootViewController became destroyed (viewDidUnload not called!).
Edit: Further investigation discovered that the UINavigationController is really recreated for every modal segue. I hope that by maintaining a property i can solve the problem.
I finally ended up by creating my own IBAction functions wich present the controller manually. This works just fine and is coded in less than 5 minutes. One just need to init the controller one time on ViewDidLoad from the storyboard.
Create a strong reference in the main view controller and point your new view controllers to that property. This will keep the view around as long as you need, although this is not recommended for n number of views because it defeats the purpose of a nav controller handling its own creation and removing of views.
I have a CoreData app with the MainView the UITableViewController which houses all of the items in the list. What i'm trying to accomplish is adding a custom back button and using the popToViewController:animated: to access the settings. When I try to use this method the app crashes. After doing extensive reading I realized that push and pop use an NSArray stack for view controllers. For example, the rootView is view 0, when you use pushToViewController:animated: it added in another view, 1 and so forth. That all made sense. What I learned was you can't pop to a view which is not loaded into the stack after the root view. My objective here is to pop to the settings view. When I change the code around in the AppDelegate.m to make the SettingsViewController the rootViewController, the UITableViewController no longer functions, it fails telling me the entity "enityName" can't be initialized. Is there any way to still have the CoreData part of the app function correctly and still pop to the settings? I have thought of using a modal view but it ruins the style of the app.
This was quite hard for me to explain, if you didn't understand any part of it, let me know.
Thanks for your help.
Update: I read in the UINavigationBar documentation that you can use - (void)setItems:(NSArray *)items animated:(BOOL)animated thus allowing you to manually set the array of pushing and popping view controllers. I just can't figure out how to do that. I've gone through apples drillDown sample code, but it didn't have the functionality I was looking for.
Perhaps you are misunderstanding Apple's navigation controller idiom. It is meant for drilling down a hierarchical structure of views and move back and forward easily and intuitively.
A view that is outside this hierarchy (it seems your Settings View belongs to this category) should really be presented modally. On the iPad, you can even use the pretty and convenient UIPopOverControllers.
Of course, if you want to keep your own look and feel (incurring the danger of confusing your users), you could fiddle with the transition animation. You could use Apple's own and thus pre-approved UIModalTransitionStyle property of UIViewControllers.
Or you could try what you did up to now and fiddle with the view hierarchy. Maybe you can eliminate your errors simply by using the view controllers sequentially and not jumping around skipping controllers in between. In this case it should be enough to use
[self.navigationController pushViewController:controller animated:YES];
and
[self.navigationController popViewControllerAnimated:YES];
rather than the more error prone versions pushToViewController and popToViewController.