Moving back to parent view controller or dismissing child view controller - ios

On click of a button, I am navigating from one controller to another. I was initially doing self.present(nextViewController, animated:true, completion:nil) and it was covering the whole window on the device. Since I wanted it to be of the size of my main (presenting) controller, below is the code that I use now to present new controller.
Navigating to NewViewController from Parent Controller:
#IBAction func doneButtonAction(_ sender: Any) {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "MyViewID") as! NewViewController
nextViewController.view.frame = self.view.bounds
nextViewController.willMove(toParent: self)
self.view.addSubview(nextViewController.view)
self.addChild(nextViewController)
nextViewController.didMove(toParent: self)
//self.present(nextViewController, animated:true, completion:nil)
}
This works fine and navigates me to NewViewController controller. But now from NewViewController controller, I wish to come back to this parent controller for which my dismiss logic is not working.
Going back from NewViewController to Parent Controller
#IBAction func backButtonAction(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
What am I missing? How do I go back to the parent controller?

What you actually had to do to present over the entire window was to modify modalPresentationStyle property of nextViewController.
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "MyViewID") as! NewViewController
nextViewController.modalPresentationStyle = .fullScreen
present(nextViewController, animated: true)
Add this code to doneButtonAction and present it the old way.

Related

Can I preserve a modally presented view controller when setting the root view controller?

Let's say I have three view controllers. On the first one, I tap a button and present the second view controller modally. On the second one, I tap a button and store the presenting view controller (first), then set the third view controller as the root view controller. Finally, I have a button on the third view controller that grabs the stored controller and sets that as the root view controller.
When I restore the first view controller, I find that its presentedViewController property is nil. I wonder if I can preserve the navigation stack and have the modal presented when I restore the first view controller?
In this example I'm storing the view controller inside AppDelegate for simplicity.
Code:
FirstViewController.swift
#IBAction func presentViewController(_ sender: Any) {
let storyboard = UIStoryboard(name: "SecondVC", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "Second")
present(controller, animated: true, completion: nil)
}
SecondViewController.swift
#IBAction func secondButtonCLick(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.savedVC = presentingViewController
let storyboard = UIStoryboard(name: "Third", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "ThirdVC")
UIApplication.shared.keyWindow?.rootViewController = controller
}
ThirdViewController.swift
#IBAction func thirdBtnTouch(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let rootVC = appDelegate.savedVC
UIApplication.shared.keyWindow?.rootViewController = rootVC
}

How can I present VC and set as new Root viewController in Swift?

I have this warning:
Presenting view controllers on detached view controllers is discouraged
I need to know how can set my rootViewController in another VC and avoid this warning
I have this code in my VC:
#IBAction func dissmissInfo(_ sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "firstVC")
present(vc, animated: true, completion: nil)
})
And in the firstVC I have this:
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.shared.keyWindow?.rootViewController = self
}
but when I try to move to another VC I have the same warning:
Presenting view controllers on detached view controllers is discouraged
You mean you want to set firstVC
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "firstVC")
as the new RootViewController?
If YES:
#IBAction func dissmissInfo(_ sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "firstVC")
UIApplication.shared.keyWindow?.rootViewController = vc
})
Then in firstVC, remove
UIApplication.shared.keyWindow?.rootViewController = self

Attempt to present UIImagePickerController on ViewController whose view is not in the window hierarchy

I'm in trouble, after that I'm moving from View Controller to a second View Controller and then switching back to the View Controller and clicking on the UI Image Picker it's not appearing and bringing me back to the second View Controller. On the debugger it's appearing:
Attempt to present on whose view is not in the window hierarchy!
I'm really becoming crazy. My code that is bringing me back from second View Controller to the first View Controller is inside a button and it's below:
#IBAction func profileButton(_ sender: Any) {
let webVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! ViewController
webVC.profileLink = linkProfilo
self.present(webVC, animated: false, completion: nil)
}
in second view controller add this variable:
var firstVC: ViewController?
and this setter function to set it
setup(firstVC: ViewController) {
self.firstVC = firstVC
}
in the first view controller, when presenting second view controller pass in a reference to self (first view controller)by calling the setup function
let secondVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as! ViewController
secondVC.setup(firstVC: self)
self.present(secondVC, animated: false, completion: nil)
now when button is clicked in secondVC,
guard let first = self.firstVC else {return}
first.profileLink = linkProfilo
self.dismiss(animated: false, completion: nil)
I finally found the way for fix all of this:
#IBAction func profileButton(_ sender: Any) {
if let webVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as? ViewController {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
webVC.profileLink = linkProfilo
dismiss(animated: false, completion: nil)
appDelegate.window?.rootViewController!.present(webVC, animated: false, completion: nil)
}
}
it's enough to add the appDelegate sharing method and dismiss before to present the webVC for share the data in the right way and don't cause problems on the first view when presented.

How to go back to the initial ViewController the right way?

I have a few view controllers with navigation bar and tab bar. Then I have an initial view controller with no navigation bar and tab bar. I want to go back to the initial view controller with no other view controllers in stack. Here's what I do:
// Head back to Initial View Controller.
let initialViewController = self.storyboard!.instantiateViewController(withIdentifier: "Initial")
UIApplication.shared.keyWindow?.rootViewController = initialViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = initialViewController
This works perfectly. But then it doesn't move to another view controller from the initial view controller again. For example:
#IBAction func loginButton(_ sender: Any) {
print("Login Button Tapped.")
let storyboard = UIStoryboard(name: "SignInViewController", bundle: nil)
let signInViewController = storyboard.instantiateViewController(withIdentifier: "SignInViewController") as! SignInViewController
self.navigationController?.show(signInViewController, sender: nil)
}
Here, the print statement is run, but self.navigationController.show won't. How do all this work? And what's the right approach to accomplish this?
EDIT:
To avoid any confusion, here's my storyboard. The left most viewcontroller (pink screen) is the initial view controller I want to go back to. The right most on the same level is the view where I want to go back from.
It doesn't move to another view controller, because you are setting a simple view controller in your rootViewController.
What you need is:
let initialViewController = self.storyboard!.instantiateViewController(withIdentifier: "Initial")
let navController = UINavigationController.init(rootViewController: initialViewController)
UIApplication.shared.keyWindow?.rootViewController?.present(navController, animated: true, completion: nil)
This will create a navigationviewcontroller, so when on "Initial" you should be able to perform push, present actions of another view controllers.
From your initial view controller, you need to instantiate your navigation stack and present it modally. Then, when you want to get back to your pre-navigation initial VC, just call dismiss(animated:,completion:), which will dismiss your entire navigation stack and return you to the initial view controller.
self.navigationController.popToRootViewController(animated:) is what you're looking for.
https://developer.apple.com/documentation/uikit/uinavigationcontroller/1621855-poptorootviewcontroller
EDIT
Try dismissing the navigationController after calling popToRoot.
self.navigationController.dismiss(animated: true, completion: nil)
EDIT
Try the following:
#IBAction func loginButton(_ sender: Any) {
print("Login Button Tapped.")
let storyboard = UIStoryboard(name: "SignInViewController", bundle: nil)
let signInViewController = storyboard.instantiateViewController(withIdentifier: "SignInViewController") as! SignInViewController
present(signInViewController, animated: true, completion: nil)
//self.navigationController?.show(signInViewController, sender: nil)
}
Then to dismiss the signInViewController call self?.navigationController.dismiss(animated:true, completion:nil) from inside the signInViewController.

How to add Navigation bar programmatically by clicking the button

I have Login, Signup and Reset viewControllers I have connected the UINavigation controller to login controller but when I go to other view controllers by clicking the button like signup or reset password the destination controller appears without navigation bar. help to include navigation bar programmatically when I click the below button.
#IBAction func signupButton(_ sender: Any) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "SignUpViewController") as! SignUpViewController
self.present(newViewController, animated: true, completion: nil)
}
For display navigation bar you need to add your SignUpViewController in UINavigationController you can add directly in storyboard By
Selecting SignUpViewController -> Editor Menu -> Embed in -> Navigation Controller
OR
you can add programmatically like this
IBAction func signupButton(_ sender: Any) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "SignUpViewController") as! SignUpViewController
let naviCon = UINavigationController(rootViewController:newViewController)
self.present(naviCon, animated: true, completion: nil)
}

Resources