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.
Related
I have been looking for a solution to this for quite some time but I still fail to see what the best solution is:
I am trying to swap view controllers programmatically, without anything in the storyboard, pure swift files.
As far as I see presentViewController() just creates a 'modal' which would cause the previous ViewController to stay in the memory (tested this, deinit never fires for the first controller). The solution I have found is to switch the rootViewController: self.view.window?.rootViewController = ViewController2() -> this fires deinit of the first one.
While this solution would work in theory, I am wondering...
Is there some recommended way or best practice of how to do this programmatically? Or is it really just about changing the viewRootController's value?
How do you structure your app? Do you use one ViewController and you swap views? Or you present other ViewControllers as modals with presentViewController? (I am totally new to this and I can't seem to find any good source; most of the articles deal with storyboard)
Thanks a lot!
EDIT: I will add: my test app is not supposed to have any kind of navigation for different viewcontrollers (no tabs, nor anything like that). It basically works like screen1->screen2->screen3->screen4. If a reset button is pressed, it gets back to screen1. I am purely interested of swapping ViewControllers, nothing else.
1: Changing the main window's root view controller is correct for swapping view controllers.
2: Generally, swapping view controller is not used. You can use a navigation controller that has a root view controller (eg. your main screen) and then push other view controllers as needed. Tab bar view controllers are also very useful.
https://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/CombiningViewControllers.html#//apple_ref/doc/uid/TP40011313-CH6-SW1
Hope this helps!
1.
The basic ways are:
navigationController?.popViewControllerAnimated(true)
or just pop to root view
navigationController?.popToRootViewControllerAnimated(true)
pushing can be done similarly
or setting a new stack
navigationController?.setViewControllers([viewController], animated: true)
These are the recommended ways of navigating in code just pick the one appropriate to your specific situation.
The push and pop mechanism is probably the most recommended way of doing general navigation.
Try to always use navigation controllers and only use modal views when appropriate.
Use navigation controllers when navigating through your main views. Modal views can be used as side views or extension to the view presenting that view modally.
Edit:
There are many ways of efficiency navigating through an app. In this particular case pushing and popping the navigation stack is probably the best way to go. But keep in mind that there are Tab bar controllers, collection views and others. At the end of the day The best is to look at your app structure and based on that decide which navigation method is the best for you.
so I am building a simple chat app. I will have a login screen, register and later on a UITableViewController so show the friend list. I'm not sure if I should use a UINavigationController for this or just stick to UIViewControllers. Below are two images for the potential setups.:
and:
I'm just wondering is it generally better practice to use a navigation controller? I am also a little confused about what happens to a view controller when it is popped. Does [self.navigationController popViewControllerAnimated:YES]; keep the view controller in the memory to be accessed again later or is a new one created each time? The same question for [self dismissViewControllerAnimated:YES completion:nil], is this destroying the controller or storing it for use later? Thanks
I'll give you my opinion about when to use a navigation controller.
If your app need to present its content in a hierarchical fashion (Master/detail) is pretty common to use a UINavigationController.
If you need to present some content that is not strictly related with the presenting content you can present it using a new view controller on top of it.
Regarding the memory, as soon as you don't keep any reference to the controllers either presented or pushed once remove (popped / dismissed) you loose any reference to them so no space used in memory
I think you will stick to the UINavigationViewController. Simply because the login and registration are belonged to your user management. From the UX standpoints, it's better to use Modal if the scene you are going to present is somehow irrelevant to the current scene. Therefore, go with UINavigationViewController for your situation.
The second question is related to memory management. It's kind of like the reference counting. If you have something to reference the UIViewController, its reference counting won't drop to zero which mean the system won't clean this up. So, you still have way to get then. If you just simply pop then up or dismiss it without referencing it. The reference counting will become zero and the system will clean it.
Note: If you want to simply display a view controller which doesn't need hierarchical navigation or a navigation bar then you wouldn't need to use a UINavigationController.
But in this case I would still suggest to use UINavigationController for the simple fact that your use case (Login Flow) can be completely addressed using a navigation flow.
In terms of memory there is now difference. Feel free to use any of them in this concern. Although how these views are presented has difference.
As someone who usually used separate xibs in the past I thought I'd give storyboard a go as it seemed a lot simpler to use and much easier to develop with. I've been writing an application where the essential set up is this:
At the top of all this is a UINavigationController (first level). Then I have Multiple UIViewControllers (second level) with buttons in them which you can tap to switch between the second level UIViewControllers.
However a problem occurs when I start switching between the second level UIViewControllers. I first thought this was an initialisation problem with the NSMutableArrays because in my code I have a NSTimer set to loop periodically and found when I set a breakpoint during it, when I went forward to the next timer tick event there appeared to be different instances of the same NSMutableArrays and it seemed a gamble to try and insert new values into these array with it sometimes working, sometimes not (as it may or may not insert into the correct instance).
Then, looking at the memory usage under Debug Navigator I found the issue. Each time I "switched" between the UIViewControllers a new UIViewController was being initiated, along with all new variables.
The code I am using to switch between them is
-(void) perform {
[[[self sourceViewController] navigationController] pushViewController:[self destinationViewController] animated:NO];
}
Or essentially a push segue transition. This also explains why when I tried to switch back to my view, the data on that view was lost as it is a complete new view.
Does anyone know how to switch between multiple ones of these UIViewControllers (say 5) essentially like a UITabViewController would except without the tab bar being present?
First option you can do this: You can use a tabbarcontroller for switching viewcontroller and hidden the tabbar. Then on buttonclick setthe tabbar index.
Second option you can do this: Create one more view controller and in this viewcontroller subview the all switching viewController and when you want to switch viewcontroller just bring that viewcontroller view to front by delegate.
Do you need the navigation bar and other features provided by your top level navigation controller?
If not, you could use a UIPageViewController instead.
You set up all your second level view controllers and then just have to tell the page view controller which one to display.
If you implement the associated delegate methods, it will automatically provide swipe gestures to switch between them and nice animations to get them on and off screen.
You can also get it to put a UIPageControl at the bottom showing a dot for each VC with the dot for the current VC highlighted.
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.