I have the next function
func switchRootViewController(rootViewController: UIViewController) {
let window = UIApplication.shared.windows.first!
window.rootViewController = rootViewController
window.makeKeyAndVisible()
}
And I use it in a presented view controller, ex in PresentedViewController.
let navigationViewController = UINavigationController(rootViewController: PresentedViewController())
present(navigationViewController, animated: true, completion: nil)
But when I switch to the needed view controller, my presented view controller doesn't deinitialize. I have to use this way, first dismiss:
self.dismiss(animated: true, completion: {
switchRootViewController(rootViewController: HomeViewController.instantiate())
})
instead of simple
switchRootViewController(rootViewController: HomeViewController.instantiate())
UIViewController objects are retained when presented by another UIViewController. To release your view controllers, they need to be removed from the view hierarchy, which you can accomplish by dismissing them. As far as where to dismiss your view controller, I'm not sure because you haven't given enough information. But you need to dismiss any view controller that you want released from memory.
Related
My app has a TabBarController. Each tabBarItem relates to a ViewController embedded in a NavigationController.
When in first tabBarItem and selecting another tabBarItem I want to do some stuff before moving to the selected ViewController. Therefore I created a class for my tabBarController and made it UITabBarControllerDelegate.
What I want to do is present an alert with two buttons; button A cancels the move to the selected viewController and button B lets the move happen.
My problem is that when button B is pressed, I want to popToRootViewController. I gave the navigationController a storyboardID and tried to instantiate it like shown below.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let alert = self.storyboard?.instantiateViewController(withIdentifier: "ActiveSessionWarningAlert") as? ActiveSessionWarningAlert {
alert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
alert.modalTransitionStyle = UIModalTransitionStyle.flipHorizontal
let alertWasDismissed: (Bool) -> Void = { userWantsToMoveToSelectedViewController in
if userWantsToMoveToSelectedViewController {
if let navContr = self.storyboard?.instantiateViewController(withIdentifier: "firstNavContr") as? UINavigationController {
navContr.popToRootViewController(animated: true)
}
tabBarController.selectedViewController = viewController
}
}
alert.alertWasDismissed = alertWasDismissed
self.present(alert, animated: true, completion: nil)
}
return false
}
Everything works as expected, but the popToRootViewController doesn't seem to occur; when selecting the first tabBarItem again the same viewController that was 'active' when we left the item is still showing.
I checked so that the viewController I want to pop is actually in the navigation stack and that the navContr != nil.
What am I missing?
You don't say so, but I'm assuming that the alertWasDismissed closure you pass to your alert view controller gets invoked when the user dismisses the alert.
The problem with your closure is this bit:
if let navContr = self.storyboard?.instantiateViewController(withIdentifier: "firstNavContr") as? UINavigationController
Any time you call instantiateViewController(withIdentifier:), you are creating a brand new, never before seen instance of a view controller (a navigation controller in this case.) That navigation controller has nothing to do with the one that belongs to the current tab that you are trying to dismiss. It doesn't have anything in it's navigation stack other than the root view controller that is defined in the storyboard.
What you need to do is find the navigation controller of the current tab in your tab bar controller's tabBarController(_:shouldSelect:) method, and pass that navigation controller to your alertWasDismissed closure.
At the time the tabBarController(_:shouldSelect:) method is called, the tab bar controller's selectedViewController should contain the current view controller. Replace the line above with:
if let navContr = tabBarController.selectedViewController? as? UINavigationController {}
That should work.
I am trying to dismiss a modal and push a new view controller from the navigation controller, but the code bellow dismisses the navigation controller as well, so there is nothing to push to and the window collapses. This code is inside my routing class:
func navigateToVC() {
self.navigationController.presentingViewController.dismiss(animated: false, completion: nil)
self.navigationController.pushViewController(newVC, animated: false)
}
So is there a way to dismiss the presenting view controllers while keeping the navigation controller?
Use this to dismiss presented ViewController,
self.dismiss(animated: true)
Once you dismiss it , that's where you should push ViewController. You can't push after you dismiss your viewController because it's deallocated already.
I recommend creating a delegate and call it from your HomeViewController
Having some trouble permanently moving one from view controller to another. The normal segues seem to all have a 'go back' option.
I know I can imbed the VC in a navigation controller and create a custom segue which rewrites the hierarchy/changes root VC
class ReplaceSegue: UIStoryboardSegue {
override func perform() {
source.navigationController?.setViewControllers([self.destination], animated: false)
}
}
But I want to avoid a navigation controller as it will confuse things when I add a SWRevealViewController later.
Can/Should I change the storyboard VC (outside of AppDelegate that is)?
Thanks - apologies if this is a 'beginner' question
You can either change rootViewController of your main window:
self.window.rootViewController = vc
or you can just simply set vc's modalPresentationStyle to fullscreen (this doesn't have a "go back" option by itself, only you can dismiss it calling dismiss)
let vc = UIViewController()
vc.modalPresentationStyle = .fullScreen
or from storyboard change it's presentation style to fullscreen.
I am having an issue with using the .overCurrentContext modalPresentationStyle on a tvOS view controller:
let vc = UIStoryboard(name: "", bundle: Bundle.main).instantiateInitialViewController() //representative of actually presented VC
vc.modalPresentationStyle = .overCurrentContext
present(vc, animated: true, completion: nil)
On the presented view controller, pressing the menu button ceases to return to the presenting view controller. This also occurs when setting it to .overFullScreen and .blurOverFullScreen. However, I am having no such problem when setting it to .currentContext or .fullScreen. Is there anything particular that needs to be used when using certain UIModalPresentationStyle's?
let vc = UIStoryboard(name: "", bundle: Bundle.main).instantiateInitialViewController() //representative of actually presented VC
vc.modalPresentationStyle = .overCurrentContext
self.definesPresentationContext = true //*** adding this line should solve your issue ***
self.present(vc, animated: true, completion: nil)
So what's going on here? The definesPresentationContext property was added in iOS 8, and the documentation states the following:
When a view controller is presented, iOS starts with the presenting view controller and asks it if it wants to provide the presentation context. If the presenting view controller does not provide a context, then iOS asks the presenting view controller's parent view controller. iOS searches up through the view controller hierarchy until a view controller provides a presentation context. If no view controller offers to provide a context, the window's root view controller provides the presentation context.
If a view controller returns YES, then it provides a presentation context. The portion of the window covered by the view controller's view determines the size of the presented view controller's view. The default value for this property is NO.
By setting definesPresentationContext to YES you ensure that the controller to be presented is presented within the bounds of the original view controller.
I have VC1 which is a basic view controller, I then want to present VC2 which is inside a navigation controller. Whenever I present it, it doesn't display the navigation controller. I want this all done pragmatically. The code I have been using to present VC2 is:
func matchesPressed(sender: UIButton!) {
let matchesTVC: Matches = self.storyboard?.instantiateViewControllerWithIdentifier("Matches") as! Matches
self.presentViewController(matchesTVC, animated: true, completion: nil)
}
How do I present the navigation controller it is inside as well?
Is the navigation controller in the storyboard? If so, give it a storyboard ID and replace your matchesTVC stuff with the navigation controller.
If matches is a standalone view controller in the storyboard, you can do it in code like this:
let matchesTVC: Matches = self.storyboard?.instantiateViewControllerWithIdentifier("Matches") as! Matches
let navContr = UINavigationController(rootViewController:matchesTVC)
self.presentViewController(navContr, animated: true, completion: nil)
Instantiate the navigation controller that contains your view controller and present the navigation controller.