Modal view controllers life cycle? - ios

I have read Apple's design patterns docs, and a few other guides and there are things I can not understand .
I encounter the problem of passing variables between viewControllers, and I saw the delegate option.
Than i have realize that if you go from viewControlA to viewControlB , and you need to update some mutableArray from B to A , you can post a delegate from B and A will get it .
BUT, if A can hear the delegate, that means that A is still alive after I went to B .
I was thinking that only when you push between views, the previous is still alive, but when the transition is modal, the previous scene is actually dead .
What is the life cycle of each view controller class ? They are always alive ?

If you have two UIViewControllers called A and B, and you want to show B modally, A stays in memory. No one says to A to remove (this is true until some other part of the code will remove it).
So, A can respond to B until the latter (B) remains the presentedViewController of A (presentingViewController).
About the delegate, you could just avoid it. Suppose for example that A and B as a property like
#property (nonatomic, strong) NSMutableArray* myArray;
Before presenting B as the modal controller, you can say
B* b = // alloc init
b.myArray = [self myArray];
// present modally B
Now they will touch the same array. When B is dismissed (it will released from memory if you have no references to it), in myArray (within A) you will find the modifications done in B.
Obviously this is just an example. And this is not an advice to not using delegates.
For further references I would just take a look to Presenting View Controllers from Other View Controllers in Apple Doc.

Related

How can a PresentingViewController get notified that its PresentedViewController dismissed itself?

Given:
ViewController A that presents ViewController B
ViewController B has no reference to ViewController A (except implicitly the presentingViewController property)
ViewController B calls dismiss on itself and does nothing else
What I want to achieve:
ViewController A wants to know when ViewController B got dismissed in order to clean up some state
Restrictions:
I do not want to use KVO
I do not want to modify ViewController B or its behavior in any way
What I have found out so far:
dismiss(animated:completion:) according to the documentation forwards the call to its presentingViewController. But as it seems dismiss(animated:completion:) is not called, but rather a private method _performCoordinatedPresentOrDismiss:animated:.
iOS documentation on presentingViewController is misleading. It says "the view controller that was presented has this property set to the view controller that presented it", but that's not true. In iOS 11, this will always point to the root parent VC of the VC that present was called on. Similarly the documentation on presentedViewController is misleading. It says "the view controller that called the method has this property set to the view controller that it presented", that's not the whole story. Every VC in the hierarchy of the VC (all its parent VCs and child VCs) that called present will point to the same presentedViewController.
In your Controller A , kame it as UINavigationControllerDelegate and with navigationController:didShowViewController mark the presentation of Controller B (isControllerBisPresented = true). When viewDidAppear of B , check if isControllerBisPresented is true.
An ugly workaround would be to use a man-in-the-middle which does something in deinit. So A presents M which embeds B as a childVC. When B dismisses itself, M will be dismissed implicitly as well, so it's deinit method should be called. There it can notify A that it was dismissed.
This is fragile, as some reference cycle could prevent M from being deallocated which would result in A not getting notified. So I'd rather want to find a better solution.

ViewController allocation and deallocation problems

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.

Is the UIViewController calling the method presentViewController removed from the memory?

Supposedly I have a UIViewController A and a UIViewController B. From A, I call the method presentViewController:B. When B shows up, what happens to A? Is it removed from the memory? If not, what method should I call to delete it?
If my UI flow is like this, A->B->A->B->A->B->... and so on, how to prevent the memory from increasing accordingly?
When you use the presentViewController:animated:completion: method from controller A to present controller B modally, what happens is that the presentedViewController property of A is set to controller B, and the presentingViewController property of B is set to A. Thus, both controllers are kept in memory while the presentation is taking place.
When you go from B to A, you call dismissViewControllerAnimated:completion: on A via the presentingViewController property of B, like this:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
(You can also call [self dismissViewControllerAnimated:YES completion:nil] and the system will automatically forward the request to the presenting view controller.)
After that, the presentedViewController property of A will be set to nil and, consequently, it will be subject to memory deallocation by the system, provided that there isn't any other strong pointer pointing to it.
No, A won't be removed from memory.
And if you want to alternate between A and B you can either:
use a UINavigationController and push B, then pop it off again and you're back at A
or make B the new root controller of your window and then A again and add a proper transition
or use a as the root and present B. Then dismiss B and you're back at A.
The UIViewController will stay in memory unless you popped it.
Use push & pop instead of just pushing new UIViewControllers.
Instead of this A->B->A->B->A->B->
You'll get this A<->B
Have a root view controller when ever A completes it's task let your root view is responsible of removing A and and presenting B. Like wise when B finishes its task alert your root view to remove B and show A again.
It won't be released automatically. You can release it manually by using dismissModalViewControllerAnimated.
Here is a very good tutorial to see this : Link

ViewDidLoad call only one time in time of navigation

I have viewcontroller. I have created its object in appdelegate.m file as I want to use that viewcontroller in poptoviewcontroller method. Now once I have created its object in appdelegate file and when I am pushing that viewcontroller it is calling viewdidload only 1 times. But from second time it is not calling viewdidload. I have some component which i want to load each and every time that controller load. What to do?? Is there any other way to use viewcontroller in popToViewController method without creating its object in appdelegate.m file
I have 4 viewcontroller A,B,C,D.
I am navigating from A to B. And I have some component in B which I am loading in viewdidLoad. Now From A to B flow I am getting value from server of component. And displaying in B. Now if user want to change value then he will redirect to C from B to change B component value. And also some times he will redirect to D and from D I am poping to B with B component value. Now If i will write my B code in viewwillappear then When I will return from C to B it will load value which I got at the time of navigation from A to B.
You can use viewWillAppear or viewDidAppear (this last one is called after the first). ViewDidLoad is only called when the ViewController is constructed. Then, since you let it live in the navigation stack it isn't called anymore.
For more information follow this thread on stackoverflow.
EDIT
With your edit the whole question becomes a different one. If you want a delegate to be called every time you will show your ViewController viewWillAppear or viewDidAppear is the answer.
It seems to me that you are over complicating things. From what I understand you are changing B in your AppDelegate? If so I can't see any problem with B having a previous state when you came from C and update your state in viewWillAppear. If you have some kind of pointer in the AppDelegate to B and you change it, then the viewWillAppear shouldn't have any issue.
You can have several solutions for your problem, and it all depends on your specific case but I would suggest that you separate the model better. You could create a singleton that holds B data and in the viewWillAppear you can get that data and display it. Then C and D only perform changes on that singleton.
viewDidLoad Method only called on initialisation of class, If you have some task to do again and again whenever view appears on screen than use
viewWillAppear
or
viewDidAppear
Method.

Go forward in UINavigationController

I have 2 views, A and B. A is a tableview, and B is a detail view. When a cell is tapped in view A, the appropriate data is loaded in view B and I use [self.navigationcontroller pushviewcontroller] to present it. This all works fine, but if the user presses row 10, lets view B load, goes back, and presses row 10 again, I would like for it to just go forward to the view B that is already loaded. The navigationcontroller always lists as only having one view in the stack. Do I have to manually save a view to the navigationcontroller to re-present? The project uses ARC and Storyboard if that makes a differance. Thanks
For this actually you dont even have to concern yourself with navigationcontroller. Have a UIViewController variable (lets say lastViewController) in your ViewController1 where viewA and viewB reside.
Before pushing ViewController2 the detailed view store that reference in lastViewController. When you pop back and press another row, check if its that same viewController and show the same viewController.
But I would advise against this approach. Keep it simple, let ViewControllerB load again. There could be a scenario where loading again is preferential as it might show the user more latest information row10.
The behavior you are seeing is correct! When ViewController B is needed it is created and pushed onto the stack of the navigation controller and we see view B. When the user goes back to ViewController A and we see view A, ViewController B is no longer needed, and it and its view go out of existence.
This is efficient and lightweight and makes perfect sense. Once you are back in ViewController A and view A, who knows what row the user will tap now? If the user taps row 10 and goes back to A, taps row 10 and goes back to A, over and over, a new ViewController B will be created and go out of existence each time. So what? From the user's point of view it is perfectly consistent; it looks like the same view B! That is all that matters. You don't want to go back to the same view B; you want to create view B all over again. And that is what you do.
Indeed, this is the genius of iOS. With a single tiny screen, it is able to make views come and go without burdening memory and other resources.
For more about UINavigationController architecture, which is what you're using here (master-detail), see my book: http://www.apeth.com/iOSBook/ch19.html#_navigation_controllers

Resources