There is HomeViewController as main viewController and LeftMenuViewController presented modally over current context. Now in LeftMenuViewController there is option to open ProfileViewController but I don't want to show ProfileViewController over LeftMenuViewController, So used this code but it is not working for some unknown reason, LeftViewController is dismissed but ProfileViewController is not pushed to front.
#IBAction func editAction(_ sender: Any) {
let homeVC:HomeViewController = self.presentingViewController!.childViewControllers[0] as! HomeViewController
self.dismiss(animated: false) {
DispatchQueue.global().async {
let infoVC:ProfileViewController = self.storyboard?.instantiateViewController(withIdentifier: "ProfileViewController") as! ProfileViewController
homeVC.navigationController?.pushViewController(infoVC, animated: true)
}
}
}
Any suggestion will be very helpfull
Change
DispatchQueue.global()
To
DispatchQueue.main
UI operations should be put in main queue
Related
I have two viewControllers:
1/ LoginViewController
2/ BibliothequeViewController
I am presenting BibliothequeViewController by clicking on a button inside LoginViewController and in the same time trying to communicate to BibliothequeViewController that it was LoginViewController that presented it.
So inside LoginViewController, I have this:
#IBAction func onLibrariesButtonClicked(_ sender: Any) {
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: BibliothequesViewController = storyboard.instantiateViewController(withIdentifier: "BibliothequesViewController") as! BibliothequesViewController
BibliothequesViewController.sharedInstance.presentedBy=self
print("INSIDE LoginViewController.onLibrariesButtonClicked, BibliothequesViewController.sharedInstance.presentedBy",BibliothequesViewController.sharedInstance.presentedBy!)
// Logs LoginViewController
self.present(vc, animated: true, completion: nil)
}
Inside BibliothequesViewController, I have this:
class BibliothequesViewController: UIViewController {
// presentedBy represents which view has presented the BibliothequesViewController
// So that when a library is selected, I go back to the ViewController that presented BibliothequesViewController
var presentedBy: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("INSIDE viewDidLoad, presentedBy: ",presentedBy)
// Logs nil
}
}
So as you see, in the LoginViewController code, BibliothequesViewController.sharedInstance.presentedBy has been set to LoginViewController.
But inside BibliothequeViewController, it is nil, when it should be LoginViewController.
I tried this:
print("INSIDE BibliothequesViewController, BibliothequesViewController.sharedInstance.presentedBy",BibliothequesViewController.sharedInstance.presentedBy!)
And I got LoginViewController logged. So I got what I needed.
But I expected the code above to work too.
Here i'm dismissing one VC and trying to navigate new VC , but navigation not working. Dismiss VC working fine.
#IBAction func onClickEmailBtn(_ sender: UIButton) {
//dismiss VC
self.dismiss(animated: false, completion: nil)
//Navigate to VC
DispatchQueue.main.async {
let cevc = self.storyboard?.instantiateViewController(withIdentifier: "CEVC") as! EmailViewController
self.navigationController?.pushViewController(cevc, animated: true)
}
}
Here I have one NavigationViewController called VC1, on that I'm presenting one VC called VC2. In this VC2 when I click button I want to navigate new VC called EmailVC.
try this code
#IBAction func onClickEmailBtn(_ sender: UIButton) {
if let controller = self.storyboard?.instantiateViewController(withIdentifier: "CEVC") as! EmailViewController {
self.dismiss(animated: false, completion: nil)
self.presentingViewController?.present(controller, animated: true, completion: nil)
}
}
You were trying to push a ViewVontroller from dismissing view controller,
and the answer which you have accepted is presenting a viewController from dismissing viewController. Although it may serve your purpose, but I am going to answer you original question
// get the object of presenting nanvigation controller(navigation controller with has presented this view controller)
let presentingNavigationController = presentingViewController?.navigationController
dismiss(animated: true) {
let cevc = self.storyboard?.instantiateViewController(withIdentifier: "CEVC") as! EmailViewController
// here you push your view controller with presentingNavigationController
presentingNavigationController?.pushViewController(cevc, animated: true)
}
Try to find out Navigation controller using below code and replace Viewcontroller with your Controller name.
let productVC = SeconViewViewController()
let parentVC = (parent!.presentingViewController as! ViewController).childViewControllers.first as! UINavigationController
self.navigationController?.dismiss(animated: true, completion: {
parentVC.pushViewController(productVC, animated: true)
})
I have a parent UIViewController(MainVC). From there I have 2 segue to 2 UIViewControllers: FirstVC (identifier: goFirstVC) and SecondVC (identifier: goSecondVC)
In FirstVC I have a button Save and when I click it I want to dismiss the FirstVC and to go on SecondVC.
Here is my code:
#IBAction func saveBtnTapped(_ sender: UIButton) {
//navigationController?.popViewController(animated: true)
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: "goSecondVC") as! SecondVC
let presentingVC = self.presentingViewController
self.dismiss(animated: false, completion: { () -> Void in
presentingVC!.present(destinationController, animated: true, completion: nil)
})
}
Here is the design for my issue:
You can use setViewControllers to keep the parent only and the SecondVC
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: "goSecondVC") as! SecondVC
self.navigationController?.setViewControllers([self.navigationController!.viewControllers.first!,destinationController], animated: true)
There are many methods but one of the generic one is to use delegates and protocols. Use the following code in your classes.
Add Following code in the first VC
protocol SecondVCDelegate : AnyObject {
func goToSecondVC()
}
class FirstVC: UIViewController {
var Delegate : SecondVCDelegate!
#objc func save() {
Delegate.goToSecondVC()
}
}
//In second view
Add following code in MinVC
class MainVC: UIViewController {
override func viewDidAppear() {
self.performSegue(withIdentifier: <yourSegueIdentifierToFirstVC>, sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == <yourSegueIdentifierToFirstVC> {
let cont = segue.destination as! FirstVC
cont.Delegate = self
}
}
}
extension MainVC : SecondVCDelegate {
func goToSecondVC() {
self.performSegue(withIdentifier: <yourSegueIdentifierToSecondVC>, sender: nil)
}
}
//This is MainVC
I think you should present second from first rather than dismissing first then presenting second.
when you will present second from first then if you wanto go back to main you can simply dismiss 2 veiwcontrollers at once without any hack.
try it:
We can control our presented controllers with navigation controller calling pushViewController and popViewController methods.
FirstVC.navigationController?.popViewController(animated: animated) // pops the top view controller
self.navigationController?.pushViewController(SecondVC, animated: true) // Pushes a view controller onto navigation's stack of controllers
In your case:
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: "goSecondVC") as? SecondVC
self.navigationController?.popViewController(animated: animated)
self.navigationController?.pushViewController(destinationController, animated: true)
First of all I am using storyboard. My ViewController hierarchy like this NavigationController -> SplashScreen -> LoginScreen -> MainTabBarController -> MainNavigationController -> MainViewController -> DetailViewController.
When I click a button on the DetailViewController page does not back to MainViewController. It is going to LoginScreen.
I tried this codes within addToBasket action in DetailViewController.
#IBAction func addBasket(_ sender: Any) {
SingletonCart.sharedFood.food.append(food!)
let mainView = self.storyboard?.instantiateViewController(withIdentifier: "FoodOrder") as! MainViewController
let appDelegate = UIApplication.shared.delegate as! AppDelegate
self.navigationController?.popViewController(animated: true)
dismiss(animated: true)
}
Here is my loginButton codes for make MyTabBarController as rootViewController.
#IBAction func loginButton(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "MainTabBarController")
self.window?.rootViewController = viewController
}
In your approach the RootViewController is Navigation controller of Login screen therefore this issue occurs.
Your approach should be like as follows:
A) NavigationController -> SplashScreen -> LoginScreen
B) MainTabBarController -> MainNavigationController -> MainViewController -> DetailViewController
When user logged in, you should replace your window.rootViewController with B) MainTabBarController
#IBAction func addBasket(_ sender: Any) {
SingletonCart.sharedFood.food.append(food!)
self.navigationController?.popViewController(animated: true)
}
My goal is whenever i click a button on a first view controller, then it will navigate to another controller which is a navigation controller.
firstViewController and secondViewController has no connection or anything.
Picture
I used this code
#IBAction func buttonTapped(sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("secondViewCtrl") as! SecondViewController
self.presentViewController(vc, animated: true, completion: nil)
}
The reason why i instatiate so that I could pass data like
vc.name = "Myname"
The problem with this code is that it doesn't present navigation bar and as well as the tab bar. What should I do to show both?
Updated question
#IBAction func buttonTapped(sender: UIButton) {
guard let tabBarController = tabBarController else { return }
tabBarController.selectedIndex = 1
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("trackYourGenie") as! TrackYourGenieViewController
let navController = tabBarController.viewControllers![1]
let secondViewController = navController.topViewController
vc.name = "Myname"
}
You are instantiating the view controller hence you wouldn't get the navigation bar. To get the navigation bar please instantiate navigation controller and since the second view is only child you would get the second view by default.
#IBAction func buttonTapped(sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("Navigation Controller Id") as! UINavigationController
self.presentViewController(vc, animated: true, completion: nil)
}
The above code should give you the navigation bar.
A safe approach:
guard let tabBarController = tabBarController else { return }
tabBarController.selectedIndex = 1
If you need to access to your tabBarController to pass datas you can do simply:
let navController = tabBarController.viewControllers[1]! as! UINavigationController
let secondViewController = navController.topViewController
Your method could be:
#IBAction func buttonTapped(sender: UIButton) {
guard let tabBarController = tabBarController else { return }
let navController = tabBarController.viewControllers[1]! as! UINavigationController
let secondViewController = navController.topViewController as! SecondViewController
secondViewController.name = "my name"
tabBarController.selectedIndex = 1
}
In your case, the following code should be sufficient :
self.tabBarController.selectedIndex = 1
As UITabBarController is your rootViewController, you can access it with self.tabBarController. You don't have to instantiate UINavigationController as it is in the storyboard.
I can see from your StoryBoard that you have a TabBarController. If your configuration is that and FirstViewController is on first tab and SecondViewController on second tab, you can just change TabBarController selectedIndex property:
#IBAction func buttonTapped(sender: UIButton) {
tabBarController?.selectedIndex = 1
}
If you want to pass data to SecondViewController you can try one of these solutions:
let controller = tabBarController.viewControllers[1] as SecondViewController!
controller.data = "some data"
This soultion could not work since SecondViewController is not ready yet.
Create a singleton class where to save data, then retrieve data in SecondViewController viewDidLoad method
Save data in UserDefaults, then retrieve data in SecondViewController viewDidLoad method (bad solution if information doesn't have to be persistent)
Extend UITabBarController and use it, create a custom var in tabBarController, put data in that variable and then retrieve data in SecondViewController viewDidLoad method
Then clear data if needed.
Navigation will not work from firstViewController as to navigate something we need UINavigationController.
Try Like This