UINavigationController deinit never called - ios

From some ViewController of my UINavigationController stack I present another ViewController and will never come back, but the problem is that deinit{} is not called. How should I remove each ViewController from the stack before navigation? Or should I use some other method? Now my code looks like
let destinationVC = storyboard?.instantiateViewControllerWithIdentifier("revealViewController") as! SWRevealViewController
self.presentViewController(destinationVC, animated: true, completion: nil)

First of all, when you call presentViewController:animated:completion: you will present the new viewController modally, outside of the navigationController's hierarchy.
If you wish to present it within the navigationController hierarchy use:
self.navigationController!.pushViewController(destinationVC, animated: true)
And if you want to change the view hierarchy, the navigationController has a property viewControllers which can be set with or without animation.
self.navigationController!.setViewControllers([destinationVC],
animated: true)
See the iOS Developer Library for more information.

Related

How I can instantly replace presented UIViewController?

I have implemented two different UIViewControllers, that should replace each other based on server respond and user actions. For simplicity let's think that we have two UIViewControllers with button replace with another, that should trigger this replacement.
Now I need to do that replace part, but the problem is there is some delay between dismissing one screen and showing the other. I want to somehow get rid of it. I show UIViewController modally with present method.
I thought about making these two screen as views in xib-files, and one UIViewController that will be loading these xibs, adding them as subViews and replacing one subView with another when it needs it, but maybe there is a way to do that with two UIViewControllers?
Code that I use to present controller:
let vc = UIViewController1()
vc.modalPresentationStyle = .overFullScreen
self.present(vc, animated: false, completion: nil)
And to dismiss:
self.dismiss(animated: false, completion: nil)
you can add a container view and in that view you can simply add your desired controller as the child view controller
official doc reference.
another reference
Set the present method's "animate" parameter to be false
To Present
let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "YourViewController") as! YourViewController
self.present(vc, animated: false, completion: nil)
To Dismiss
self.dismiss(animated: false, completion: nil)
First you send notification to parent class for present another 'viewController' and then dismiss current class
Note: Both present class method set animation false

back to initial view controller

As you can see i have an initial view controller with label in it "Initial ViewController". In this view controller i use below code to go to selected tab of Tab Bar Controller
let vc1 = sb.instantiateViewController(withIdentifier: "tabbar") as! UITabBarController
vc1.selectedIndex = selected
let controllers = [vc1]
navigationController!.setViewControllers(controllers, animated: true)
In my First View, there's a button "Present VC" which presents "Presented View".
let vc1 = sb.instantiateViewController(withIdentifier: "PresentedVC") as! PresentedVC
present(vc1, animated: true, completion: nil)
Now i want to go back from presented vc to root vc(Initial View Controller). I can do this with:
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "FirstNavigationController") as! FirstNavigationController
UIApplication.shared.keyWindow?.rootViewController = viewController
but this instantiate Initial View Controller every time and views remain in memory. How can i go back to Initial View Controller without instantiating it?
PS: FirstNavigationController is initial Navigation Controller.
At the very beginning you remove the initial view controller by doing this:
navigationController!.setViewControllers(controllers, animated: true)
All the previous viewControllers are gone now and replaced with the tabbar controller and its children.
If you want to go back to the initial controller, you will have to push the tabbar onto the stack instead of replacing everything with it. E.g.
navigationController?.pushViewController(tabbarVc, animated: true)
First when you did this
navigationController!.setViewControllers(controllers, animated: true)
the initial VC is deallocated (if it has not a strong references)
to keep it you can use push , pop instead of setViewControllers but note it may cause memory issues if you have heavy processing in your app as it will remain in navigationController stack
You are missing the concept between "Presenting" or "Setting" View Controller & "Navigating" the View Controller. You will get the answer, once you understood the concept. Here, it is..
When you are setting the ViewController, you are completely replacing the stack container to the new view controller. the way you did it here:
navigationController!.setViewControllers(controllers, animated: true)
In this case, STACK holds the addresses of the latest set/presented ViewControllers that means previous whole ViewController vanished.
On the other hand, if you are navigating to other view controller by pushing it. you can go back to previous controller by simple popping the ViewController address from stack. or to next ViewController by pushing
e.g:
self.navigationController?.pushViewController(tabbarVc, animated: true)
self.navigationController?.popViewController(animated: true)
Summary:
If you push TabBarController then, only you will get InitialVC.
Now, in your case, you are setting the ViewController and hence, you are not getting InivtialVC. Try Pushing tabbarVC This will work.
navigationController?.pushViewController(tabbarVc, animated: true)

create tabbarcontroller on the fly and assign it to the view other than rootview of window

I want to use a third-party library which implements a nice tabbar controller. But it does all the work programmatically, basically all it does is create two uiviewcontrollers and add them to a tabbarcontroller, and then instantiate an uinavigationcontroller with the tabbarcontroller. In the last step, it assigns the uinavigationcontroller to the rootviewcontroller of the window like the following:
self.window?.rootViewController = getNavigationController()
But I want to use this navigationcontroller in a place other than the rootviewcontroller of the window, say like I want to push from another view and goes to this navigationcontroller. How can I achieve that?
You can present it modally over your current navigation controller from your current viewcontroller
let vc = myNavigationControllerWithTabBarControllerInside() //change this to your navigation controller
self.navigationController.presentViewController(vc, animated: true, completion: nil)
Please verify, for your self.navigationController to not be nil. otherwise, use
self.presentViewController(vc, animated: true, completion: nil)

Present modal view controller over modal view controller

I have a view controller VC1 that is presented modally over full screen from some other VC0. In my storyboard I have a modal segue from VC1 to VC2 also presenting over full screen. When in my app I can plainly see VC2 over VC1 over VC0, because some parts of their views are transparent. Perfect.
However, I'm going to reuse VC2 many times so I don't want to have a segue to it for each controller in my storyboard, so I want to accomplish this same thing programmatically. However, when I call presentViewController:animated:completion in VC1 to present VC2, the view of VC1 disappears when the modal transition is complete. When VC2 is dismissed, the view of VC1 reappears when the transition animation is complete.
How can I get the same effect programmatically as when I'm using the storyboard segue?
let newView = self.storyboard!.instantiateViewControllerWithIdentifier("NewViewController") as! NewViewController
newView.modalPresentationStyle = UIModalPresentationStyle.OverFullScreen
self.presentViewController(newView, animated: true, completion: nil)
You can only present on a visible controller, which is usually the rootViewController. But when there is a modally-presented controller, it covers the root controller, so you can't use that. But you can present on the modal, which is accessed though rootViewController.prsentedViewController. Here's some code:
let rootVC = window?.rootViewController
let presentingVC = (rootVC?.presentedViewController ?? rootVC)
presentingVC?.present(myController, animated: true, completion: nil)
You don't need to change the modalPresentationStyle.
You need to set the modalPresentationStyle property of the presented controller to UIModalPresentationOverFullScreen. Set that property just before you call presentViewController:animated:completion.

pass data via. navigationController

i'm trying to pass Data to a viewController. The problem is that its embedded in a navigationController and is presented modally. How can i pass to a viewController which is presented modally and is embedded in a navigation Controller
func offerNew(sender: UIBarButtonItem) {
let offerVC = self.storyboard?.instantiateViewControllerWithIdentifier("OfferViewController") as UINavigationController
self.presentViewController(offerVC, animated: true, completion: nil);
}
i've tried this
let offerVC = self.storyboard?.instantiateViewControllerWithIdentifier("offerNavigation") as UINavigationController
let targetVC = offerVC.topViewController as OfferViewController
self.presentViewController(targetVC, animated: true, completion: nil);
I'm assuming that offerVC is the navigationController in which your "target" view controller is embedded. If so, your target view controller can be accessed through the topViewController property of offerVC. So
let targetVC = offerVC.topViewController as TargetViewController
will give you a reference. You can then access the properties of your target view controller.
EDIT
But you should present OfferVC - it will display targetVC automatically.
self.presentViewController(offerVC, animated: true, completion: nil);
You could add a property to your OfferViewController, then assign it. Your view initialization code would be done in loadView, which won't be called until after presentViewController, so not having it during init shouldn't be a problem in most cases.

Resources