Just a question I've been pondering, 'traditionally' with modal viewcontrollers the presenting 'parent' view controller should dismiss the presented child.
Apple have stated that the presented child should not self dismiss, thus I tend to set up a delegate protocol just to dismiss a modal view controller.
This seems somewhat overkill,
I was wondering since objective C passes by reference anyway, and there wouldn't be a performance cost, couldn't I just pass a reference of the presenting parent viewcontroller to the modally presented child viewcontroller during instantiation and then make a method call back to the parent to dismiss the child?
Sorry if this is a stupid question...
The reason why you typically setup the delegate/protocol for something like this is that it makes your code much less coupled. Say you wanted to present your view as a popover or subview later? You may not think this is needed but it may as the project grows.
Since the parent controller handles its own modals, subviews, and popovers it knows what to do when a button to exit is pressed (or some other action). The modal (in this case) does not necessarily (and shouldn't!) know how it is being presented, hence it should tell its delegate that and let that controller handle it (popViewController, dismissViewController, removeSubview, etc...). This is a large part of understanding OOP and will help keep your code cleaner.
And no, this is not a stupid question in my opinion.
Related
I have this 2 "Loops" in my App between my ViewController.
First Loop is the Game-loop. At first VC my Level is displayed. The second VC is my Game Screen and if the game is finished, my third VC will appear with bonus point, stars, and so on.
The second "Loop" are just three VC with swiping.
Ok, where is now the problem? i have problems with the deallocations. for example, overtime i swipe, my locations are going up in sínstruments, the curve is getting higher and higher...
also my game loop. i can't deallocate the vc before.
i think i didn't understand correctly how [self dismissViewControllerAnimated:NO completion:nil]; works.
is it right, that this method is always sent to the parent vc, and the parent vc deallocate the vc where i execute this method?
is the parent vc my initial vc?
how can i dismiss and deallocate my view controllers correctly in my "loops"?
now, my allocing curve in instruments is getting higher and higher at each level, and about level 18-21 my app is crashing, i think because of too much allocations.
can anyone tell me hoe i can solve my problems?
To begin with your questions:
Is it right that this method is always sent to the parent vc, and the parent vc deallocate the vc where I execute this method?
From the Documentation about dismissViewControllerAnimated:completion:
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, UIKit asks the presenting view controller to handle
the dismissal.
Is the parent vc my initial vc?
Parenting is a bit complex with your structure. Normally, when a VC (let's call it X) presents another VC (this one is Y), X would be the parent of Y. But you must share additional code such as how you're allocating these VC's, how/when you're presenting them, etc. to know which one is the "most" parent.
When you call [self presentViewController:Y animated:** completion:** on X;
X would be the presentingViewController.
Y would be the presentedViewController.
Hence, X is the responsible one for Y. Parent :) So, your question is a bit invalid for your scheme. However, you can easily say that your initial VC would probably be the parent of all VC's that's presented by him. (For example, if you embed navigation controller for VC's, it would be the root VC and it will be responsible for "winding/unwinding", which makes it "parent".)
Normally, view controllers should not go in a loop between themselves. They can be dependable on each other, but it shouldn't create loops.
Solution proposal:
It seems to me that you're duplicating these 3 kind of VC's every time a level begins. That's the first loop's problem. The second loop is a bit complex, I assume you want to save the state of the VC's while swiping to the other VC's.
1. Manipulating current VCs.
These VCs must have an option to clear his state, everything should be reset like it was loaded for the first time. You will only create these VCs once, and reset them if needed.
2. Create a Singleton Class which will hold these VCs.
(Caution: Since this solution involves holding VCs up in a class, they will always hold up memory. They won't be drawn to window, though.)
When you start the app, a singleton will be created, and you will also create the VCs needed at the same time. This class should have methods like:
addViewControllerToStack:
showViewController:animated:completion:
resetViewController:
And of course properties to hold these VCs.
3. Control through Singleton
When the user presses up a button like "go to X level", you must interact with the Singleton class you've created. If you're going to dismiss a VC, the Singleton should do that. Also for presentation.
Summary:
With this method, you cannot create multiple VCs of the same type. You can always use the one which is created by your Singleton class, and you can always reuse them. But be cautious, since you're not deallocating any VC, you may end up with residues (like forgetting to reset/remove timers, to reset score, etc).
I've used it in one of my project, which required a view controller with OpenGL. I couldn't dismiss it since it's always doing calculations. And it could've been created only once. The bad part is, it was in the middle of the VC segues. So, whenever I needed to present that VC, I call my Singleton class to show it for me. And I create it only once.
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.
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.
What is the difference beetween calling presentModalViewController and pushViewController, when :
animation is set to NO (even if yes, that's just an animation style that can be changed).
a navigation controller is defined when presenting the modal view, so it can be navigable too, with a call stack, ....
Is this just to be able to go back from the first pushed view ? Woooaaaaaa.....
I guess the difference is elsewhere and deeper. No ?
Ignoring transitions/animations and how things are structured behind the scenes (which aleph_null's alswer provides a good discussion of), the only user-facing difference is the ability to return to the preceding view automatically using the navigation bar.
If you use pushViewController you will automatically get a "Back" button in the navigation bar. If you use presentModalViewController you do not, and generally will have to implement your own controls and/or callbacks to handle dismissing the controller.
Conceptually the modal presentation style is generally used for atomic tasks that you cannot navigate away from (i.e. you either complete the task, or you cancel, and you cannot do anything else within the app until you do one or the other).
If you're wondering why have the difference in the first place, I can't say. Personally I think frameworks that provide a unified API for moving from one controller to another (like cocos2d, or Android) make a lot more sense.
The most important difference is about semantics. Modal view controllers typically indicate that the user has to provide some information or do something. This link explains it more in depth: http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html
Here's another, less abstract difference they talk about:
"When you present a modal view controller, the system creates a parent-child relationship between the view controller that did the presenting and the view controller that was presented. Specifically, the view controller that did the presenting updates its modalViewController property to point to its presented (child) view controller. Similarly, the presented view controller updates its parentViewController property to point back to the view controller that presented it."
Also see this thread: why "present modal view controller"?
Take a look into the viewControllers in the image
The top 2 viewControllers(login & submit) at the top left are disconnected from the tabBarController & NavigationController
The rest of the viewControllers are embedded in a NavigationController. They somehow belong to the natural flow of the app.
Now you have to ask yourself
Do I need to always show login + submit page every time? It would be pain in the neck for the user to each time go to login even if they logged in last time. These 2 screen really don't fit the natural flow of the screens. So what do we do? We just add them modally using presentViewController
However for the rest of the viewControllers we want to keep them inside 2 navigation so we can easily go back and forth so we use pushViewController
For more information I recommend you to see this video
The image was also picked from this great answer. It's worthy of a look.
This is what my experience says,if you want to manage a hierarchy of views,better go for pushViewController in the navigation controller. It works like a stack of view-controllers in the navigation controller. If however the requirement is just to show a view on executing some actions on the parent view controller then the best way is presenting it modally.
If you need a complex push pop logic always prefer a pushViewController.
UINavigationController are used when you want to have some sort of hierarchal representation of your data (ie drill down). They work using a stack of UIViewController subclasses. Every time you “drill down”, you simply add another view controller to the stack. Then, the “back” logic is simply a matter of popping view controllers off of a stack.
You can check out this link:
http://www.icodeblog.com/2011/10/11/back-to-basics-an-introduction-to-view-controllers/