Will deinit gets called when a view controller gets disappeared? - ios

I created two view controllers like
Navigation controller -> View Controller -> Details View Controller
1 2 3
The (2) View Controller has a button in it which when clicked will shows (3) Details View Controller. I have created a segue from button to (3) VC.
I have added deinit blocks in those two classes.
deinit {
print("vc deinit")
}
However, this does not get logged. When will a view controller get deallocated?
Sample code

In this case, the only deinit will get called is the second one (the one implemented in the Details View Controller) each time you tap the back button on the navigation (pop to the previous view controller).
So, why the first deinit (the one in the View Controller) didn't get called?
That's because it is the first view controller in the navigation controller stack. Pushing to the second view controller does not mean that the previous one(s) one has been deallocated and still exists as the first element in the navigation stack; As an example, that's why viewDidLoad method should not get called when you come back from a popped view controller, which means it did not get deallocated.

Related

How to dismiss all but one view controllers and pop to specified view controller in Swift?

I have a bunch of views and then a logout button, which logs the user out and takes them to the first view controller (a login/register screen). I tried doing this with a modal presentation, but it destroys my navigation, and I can't use a pop to root view controller because it is not the root view controller - I am at least 2 navigation controllers deep. How would I go about somehow displaying only the first one? I basically need it to act as if the app was just relaunched. Would unwind segues help in some way? Thanksthis is what I mean by messing up the navigation. The following view controllers now pop up, instead of (the following pic is from the actual first time launching the app) how it should look
Supposing that you use a UINavigationController, you can use the UINavigationController.setViewControllers(_:animated:) method in combination with UINavigationController.viewControllers. The latter provides you an array of view controllers in the exact order they are stacked by the navigation controller, while the former can be used to alter the navigation stack. For instance, if you want to keep only the first 3 view controllers in the navigation stack:
guard let navigationController = self.navigationController else { return }
let viewControllersToKeep = navigationController.viewControllers.prefix(3)
navigationController.setViewControllers(viewControllersToKeep, animated: true)
Note: The animation performed by the navigation controller in this case is usually (I believe always, but not 100% sure) a push animation instead of pop, which might not be the desired one.
So if you want to make the navigation controller to perform a pop animation, then you should call pop until you reach the desired view controller, but the key is to animated a single pop. For the same example as above:
guard let navigationController = self.navigationController else { return }
let numberOfPops = navigationController.viewControllers.count - 3
for i in 0..<numberOfPops {
let animated = i == 0
navigationController.popViewController(animated: animated)
}
Hope this answers the question about popping to the desired view controller (you can detect your view controller by its type, then you can find out its index and use the logic above).
If you want to dismiss to the first presenting view controller, then you need to call UIViewController.dismiss(animated:completion:) on the first presenting view controller. dismiss method will behaves like this:
If the view controller is presenting a view controller, then it will dismiss the presented view controller;
If the view controller is not presenting any view controller, but it is presented by another view controller (i.e. has a parent view controller), then it will ask the parent view controller to dismiss it;
If none of the above, it will do nothing.
So long story short, you need to call dismiss on the view controller that you want to be left on the screen.
I basically need it to act as if the app was just relaunched.
In this case, assigning the login/register screen controller to UIWindow.rootViewController seems to be the right option. This topic has been already covered here: Swift ios set a new root view controller

load another view controller into UItabbarcontroller

After logging UIViewController, I am redirected to first item of UITabBarController. This works fine, but in my first viewcontrollers I want to push another view controller into the tabor view.This should happen while I logged in first time. In other words I want to replace the selected viewcontroller with another one. This UIViewController is not attached with Tabbar controller, I do not want to attach because it replace the first tabView item only one.
You will run into problems if you try to transition to another view controller in viewDidLoad. You should either switch the child view controllers of the tab bar controller or you can present a subview within one of the child view controllers.
When -viewDidLoad is called, the view controller is generally not presented. As such, if you try to present another controller, it will crash.
Try -viewDidAppear: if you want what you are describing.

How to pass data to child controller when interactive pop gesture is cancelled in iOS7?

Traditionally the data is passed to a child controller by calling prepareForSegue, for example, when a table cell is clicked.
In iOS 7, there seems to be a new navigation idiom, where you drag the left edge of the screen to go back up the navigation stack. It seems like when you just start the gesture, child view is removed and parent is shown, BUT if you cancel it by not dragging far enough, it will be cancelled, child view snaps back into place, but there was no prepareForSegue call. The data item in the child view at this point is nil, which makes me think it's re-created.
How to properly pass data to child view controllers that would both work for segues and this navigation idiom?
You're wrong about the view controller that's being popped by the drag being destroyed, it's not. viewWillDisappear is called as you start to drag, and if it cancels, viewWillAppear and viewDidAppear are called but not viewDidLoad or dealloc, so no new controller is being created. If you want to pass data back to the controller that appears when you do the drag, you should use a delegate protocol. You can set that controller as the delegate of the one that's pushed when you do the original push (or the segue that goes forward to that second controller). Also, it's not really correct to call these controllers parent and child -- they're both controllers in the navigation controller's viewControllers array. The parent of either of those two controllers is the navigation controller.
The actual reason turned out to be completely unrelated to views lifecycle, but the fact that data items passed down to detail views were stored as weak references.
In my code the list view keeps its data in an NSArray* property.
On transition, detail view is initialised with an item from said array and stored in a weak property.
When pop gesture is initiated, list view is shown. In my code, when the list view is shown its item list is reloaded, naturally nulling references in the child view.
When the gesture is cancelled, the detail view is presented without segue, with nil item.
Solution: change references in details views to strong.

How to force init a view controller before it is presented the first time?

I have a tab bar controller with multiple view controllers.
I attempt to do a method call to one of the view controllers (lets call it SomeViewController) and I realised that SomeViewController was not initialised yet.
And SomeViewControllerwill only be initialised when I click it's tab once.
So, is it possible to initialise the view controller even before the view of initialise is presented the first time?

ViewDidAppear from viewcontroller 1 is getting called in viewcontroller 2

I have two view controllers. I display the 1st one and then when I modally display the second one viewDidAppear gets called from the View Controller 1 after viewDidAppear gets executed from View Controller 2.
Why is View Controller 1's viewDidAppear getting called while in View Controller 2? Is this normal behavior?
More importantly the content view of a scrollview is getting resized when the viewDidAppear gets from the View Controller 1 gets called inside View Controller 2.

Resources