hello I want to pass value to my second Controller.The second Controller is connected to NavigationController and the storyboard Id has been set in NavigationController, not in SecondViewController
I show ViewControllers like this
var _profile : UINavigationController{
get{
return self.storyboard?.instantiateViewControllerWithIdentifier("ShowUserProfileTableViewController") as! UINavigationController
}
}
case 0:
if (self.revealViewController().frontViewController.isKindOfClass(UINavigationController)){
let nav : UINavigationController = self.revealViewController().frontViewController as! UINavigationController
if (!nav.viewControllers[0].isKindOfClass(ShowUserProfileTableViewController)){
self.revealViewController().setFrontViewController(self._profile, animated: true)
}
}
else{
self.revealViewController().setFrontViewController(self._profile, animated: true)
}
break
The simplest approach is to use segues and implement prepareForSegue, like so:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ShowSecondViewController" {
let navigationController = segue.destinationViewController as! UINavigationController
let targetViewController = navigationController.viewControllers.first as! SecondViewController
targetViewController.someString = "foo"
}
}
Now, this assumes that
the segue was given a storyboard identifier of ShowSecondViewController;
the destination view controller was a navigation controller whose "root view controller" had a base class of SecondViewController; and
the SecondViewController had some property called someString.
Clearly, change these identifier, class, and property names accordingly, but this is the basic approach. And you'd either programmatically call performSegueWithIdentifier or just have a segue between some UI control and the next scene (the navigation controller).
Related
I have a view controller as my initial view controller.
there's a button in it(GO button) which when the user taps, it should go to another view controller(let's call it Destination view controller with label 'This is where i wanna go'). Meanwhile i want to pass it through a Tabbar controller. The reason is i want to have tabbar in my navigation stack and when users presses back on Destination view controller, it must go to tabbar controller. Picture shows what i want. what can I do to skip tabbar while having it in navigation stack?
You can do that easily inside the IBAction of GO button:
#IBAction func goTapped(_ sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc1 = storyboard.instantiateViewController(withIdentifier: "myTabBarViewController")
let vc2 = storyboard.instantiateViewController(withIdentifier: "myGoalViewController")
let controllers = [vc1, vc2]
self.navigationController!.setViewControllers(self.navigationController!.viewControllers + controllers, animated: true)
}
Good luck!
Going to DestinationViewController could be manually:
if let destinationViewController = self.storyboard?.instantiateViewController(withIdentifier: "Storyboard ID of DestinationViewController") {
self.navigationController?.pushViewController(destinationViewController, animated: true)
}
(Alternatively, you could make a segue from FirstViewController to the DestinationViewController directly in Storyboard)
And in your DestinationViewController, insert the TabbarController to the Navigation sequence manually after view did appear, then you are able to go back to the TabbarController:
class DestinationViewController: UIViewController {
//......
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.isBeingPresented || self.isMovingToParentViewController {
var viewControllers = self.navigationController?.viewControllers
if let index = viewControllers?.endIndex.advanced(by: -1),
let tabBarController = self.storyboard?.instantiateViewController(withIdentifier: "Storyboard ID of TabBarController") {
viewControllers?.insert(tabBarController, at: index)
self.navigationController?.viewControllers = viewControllers!
}
}
}
//......
}
I have been following a firebase tutorial to create my own chat application, I am trying to access a chatviewcontroller from the uiviewcontroller containing a table view. The Viewcontroller is embedded in a navigation controller. How can I bring up the chatviewcontroller when a cell is selected?
Here is my code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
//retrieve the destination view controller
let navVc = segue.destination as! UINavigationController
let channelVc = navVc.viewControllers.chatViewController as! ViewController
// set the senderDisplayName in Viewcontroller to the email provided in the email field by the user
channelVc.senderDisplayName = userEmailTextField?.text
}
Here is the error msg
Assuming your ChatViewController is the root view controller of your navigation controller you should change your code to this:
let channelVc = navVc.viewControllers.first as! ChatViewController
navVC.viewControllers is an array of the UIViewControllers managed by that UINavigationController, so that array would not have a property called chatViewController unless you implemented something custom.
EDIT: To put it all together, and also to demonstrate how to safely unwrap these optionals, your prepareForSegue should look like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if let navVc = segue.destination as? UINavigationController, let channelVc = navVc.viewControllers.first as? ChatViewController {
//Now you have a reference to your ChatVC, and you can set the DisplayName
channelVc.senderDisplayName = userEmailTextField?.text
}
}
You have to get the first view controller from the UINavigationController:
let channelVc = navVc.viewControllers.first as! ChatViewController
In various tutorials on how to use SegmentControllers, TabBarControllers, etc. it is configured such that the variable representing the view gets its value from an instantiation of the storyboard:
private lazy var summaryViewController: SummaryViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "SummaryViewController") as! SummaryViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
Why does this code not just get an instance of SummaryViewController?
Adding an instance of a VC from your Storyboard, adds all of the logic and outlets you add in the storyboard. Let's say you have the following (obviously simple) VC:
class MyVC : UIViewController {
func viewDidLoad() {
}
#IBAction buttonPressed(sender : UIButton) {
/// Do something
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mySegue"{
var vc = segue.destinationViewController as! WhateverViewController
}
}
}
where the buttonPressed: func is connected to a button in IB, and you also have a segue with a 'mySegue' identifier. Initializing your VC from the storyboard gives you access to all of these things. You absolutely can instantiate and push a VC, without the use of the storyboard, but you should not do so, when the VC you are pushing has wired IBOutlets, IBActions, etc.... If you want to do this in code, try the following:
let myNewVC = PushedViewController()
self.navigationController?.pushViewController(myNewVC, animated : true)
This will push the myNewVC onto your navigation stack, back button and all, and without using the storyboard.
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
I am developing an iOS application in Swift.
I want to send data from a view to an other one, using the prepareForSegue function.
However, my target view is preceded by a navigation controller, so it doesn't work. How can I set data on the VC contained within the navigation controller?
In prepareForSegue access the target navigation controller, and then its top:
let destinationNavigationController = segue.destination as! UINavigationController
let targetController = destinationNavigationController.topViewController
From the target controller you can access its view and pass data.
In old - now obsolete - versions of Swift and UIKit the code was slightly different:
let destinationNavigationController = segue.destinationViewController as UINavigationController
let targetController = destinationNavigationController.topViewController
Prepare the segue in the SendViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueShowNavigation" {
if let destVC = segue.destination as? UINavigationController,
let targetController = destVC.topViewController as? ReceiveViewController {
targetController.data = "hello from ReceiveVC !"
}
}
}
Edit the identifier segue to "showNavigationController"
In your ReceiveViewController add
this
var data : String = ""
override func viewDidLoad() {
super.viewDidLoad()
print("data from ReceiveViewController is \(data)")
}
Of course you can send any other type of data (int, Bool, JSON ...)
Complete answer using optional binding and Swift 3 & 4:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let navigationVC = segue.destination as? UINavigationController, let myViewController = navigationVC.topViewController as? MyViewControllerClass {
myViewController.yourProperty = myProperty
}
}
Here is the answer for Swift 3:
let svc = segue.destination as? UINavigationController
let controller: MyController = svc?.topViewController as! MyController
controller.myProperty = "Hi there"
A one liner in Swift 3:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination.childViewControllers[0] as? FooController {
vc.variable = localvariable
}
}
In Swift 5
If you must not only segue from a SourceViewController to a DestinationViewController embedded in a UINavigationController, but also to a new Storyboard also, then do the following...
Place a "Storyboard Reference" object from your Object Library next to your source ViewController in Interface Builder, and then drag a segue to it (from a button on the SourceViewController view, for instance). Name the segue identifier "ToOtherStoryboard", for example.
Go to NavigationViewController and give it a Storyboard ID using the Identity Inspector. "DestinationNavVC" would do.
Click the Storyboard Reference icon you created in step 1, and in its attribute inspector's 'Referenced ID' field, enter the Storyboard ID you wrote for the UINavigationController in step 2. This creates the segue from source to the DestinationViewController no matter what you write in source file of the source ViewController. This is because seguing to a NaviationController will automatically show the root ViewController (the first one) of the UINavigationController.
(OPTIONAL) If you need to attach data along with your segue and send it to properties within the DestinationViewController, you would write the following code inside a Prepare-For-Segue method in your SourceViewController file:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ToOtherStoryboard" {
let destinationNavVC = segue.destination as! UINavigationController
let destinationVC = destinationNavVC.topController as! DestinationViewController
destinationVC.name = nameTextField.text // for example
destinationVC.occupation = occupationTextField.text
}
}
You do not NEED to have a PrepareForSegue if you're simply trying to move from one ViewController to another, the methods above will work (w/o step 3)
In your IBAction Outlet method for your button you used to initiate the segue, you would write:
performSegue(withIdentifer: "ToOtherStoryboard", sender: self)
Set the identifier name in the segue arrow property in order to use in the the performeSegue.
Like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc: ProfileViewController = segue.destination as? ProfileViewController {
//do any setting to the next screen
}
}
and then:
performSegue(withIdentifier: "yourIdentifierOfViewProfile", sender: indexPath.row)
I hope it helps.
It's a good idea to skip the check for UINavigationController as there may be multiple segues that use a navigationController and so will go into that check for every segue that uses a navigationController. A better way is to check the first viewController of the children and cast it as the viewController you are looking for.
if let destVC = segue.destination.children.first as? MyViewController {
destVC.hideBottomBar = true
}