I can swap from one UIViewController to another within a UITabBarController using tabBarController?.selectedIndex = targetIndex. I would like to know how to trigger a segue after the switch.
I have tried doing if let vc = tabBarController?.viewControllers?[0] as MyViewController {} and calling vc.performSegue() but it errors out, complaining that it's a UINavigationController. (I couldn't even get vc.prepare() to work but I can't work out how to create a UIStoryBoardSegue, which it requires as the first argument, from scratch using the segue identifier.) I think this is because all of my UIViewControllers in the UITabBarController are within Navigation Controllers.
How do I switch from index 0 to 1 in the tab bar controller, and then trigger a segue in the view controller embedded in the navigation controller? Note, I need to pass in a copy of the calling object (my UIViewController) or a payload for contextual purposes to the UIViewController being segued to.
Well like you said the viewController which is presented at the index is a UINavigationController. You should get the rootViewController of the UINavigationController and perform the Segue on the RootViewController.
Below code should work:
self.tabBarController?.selectedIndex = 1
guard let vc = self.tabBarController?.viewControllers?[1] as? UINavigationController else {
return
}
guard let rootVC = vc.viewControllers.first as? SecondViewController else {
return
}
rootVC.vc = self
rootVC.performSegue(withIdentifier: "test", sender: self)
Related
I want to create a navigation hierarchy where I want to go to a SecondViewController from FirstViewController using a NavigationController. The FirstViewController contains a button B that I intend to use to go to a SecondViewController. I am employing the navigation controller concept since I want to return to the FirstViewController later. I have done the following steps to achieve it:
I have embedded a NavigationController into a storyboard X containing both the FirstViewController and SecondViewController.
I have created a segue from the button B present in the FirstView (has FirstViewController associated with it) to the SecondView (has SecondViewController).
I set the navigationViewController nvc as following after I have presented the firstViewController:
nvc = UINavigationController.init(rootViewController: firstViewController)
Later on, when the button B gets pressed, I execute the following code to push the secondViewController onto the navigation hierarchy:
self.nvc?.pushViewController(secondViewController, animated: true)
However, the secondView doesn't present itself even after pushing.
I have named the segue between the button B and secondViewController as kSegue, so I tried to perform the segue as an addition to see if the secondViewController presents itself or not:
self.performSegue(withIdentifier: "kSegue", sender: self)
An exception occurs when both the 4th and 5th steps are performed together. The exception states that I'm pushing a viewController that already exists on the navigation hierarchy, but the second view still doesn't open even if I comment the performSegue code.
May I ask what mistake I am making here?
In the storyboard, make sure there is a rootViewController segue from the navigation controller to the first view controller. Also, make sure the navigation controller is marked as the initial view controller.
In the code, change
self.nvc?.pushViewController...
To
self.navigationController?.pushViewController...
1) Take a navigation controller on your storyboard
2) Set navigation controller as initial view controller
3) set view controller A as a root view controller of your navigation controller
4) in "GoToB" method access View controller B's instance and push it in navigation controller
5) On View controller B's "Go Back" method write code to pop it.
6) Dont forget to set storyboard Id on both A & B view controller
class FirstViewController: UIViewController
{
lazy var secondViewController : SecondViewController? =
{
let secondViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
return secondViewController
}()
override func viewDidLoad()
{
super.viewDidLoad()
}
#IBAction func goToB(sender : UIButton)
{
guard let secondViewController = self.secondViewController else
{
return
}
self.navigationController?.pushViewController(secondViewController, animated: true)
}
}
class SecondViewController: UIViewController
{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func goBack(sender : UIButton)
{
self.navigationController?.popViewController(animated: true)
}
}
I have a view controller like below.
This view is attached with a tabBarController. The tabBarController has 5 viewControllers and I have to present the 5th viewController of tabBar from another page. So I used the below codes for present that viewController
#IBAction func onClickUserProfile(_ sender: Any) {
let navVc = self.storyboard?.instantiateViewController(withIdentifier: "ProfileVC")as! ProfileVC
navVc.userId = Int(self.userId)
navVc.navigationItem.hidesBackButton = true
navVc.tabBarController?.tabBar.isHidden = false
self.navigationController?.pushViewController(nxtVc, animated: true)
}
But after execute the code it resulting the view controller as the below image.
The view undergoes the tabBar. Anyone help me to push to a tabBar view.
You need to set the selected UIViewController from UITabBarController something like this should work .
self.tabBarController?.selectedViewController = self.tabBarController?.viewControllers![1]
where tabBarController?.viewControllers returns the array of current ViewControllers embedded in the UITabBarController .
Your code should be something like this.
#IBAction func onClickUserProfile(_ sender: Any) {
let vc = self.tabBarController?.viewControllers![1] as! ProfileVC // use your index
vc.userId = Int(self.userId)
self.tabBarController?.selectedViewController = vc
}
Note: Don't create an instance of the UIViewController as .instantiateViewController(withIdentifier:) use the already existed
ones in the array tabBarController?.viewControllers, creating new
instance will be treated as new one and gives the problem you have up
there .
I have a VC that I'm presenting modally however that VC is wrapped in a UINavigationController. To present the Navigation Controller connected to my VC I added an identifier in storyboard and present like below:
if let nvc = self.storyboard?.instantiateViewController(withIdentifier: "EditTaskNavController") {
self.present(nvc, animated: true){
success(true)
}
}
This presents fine.
The problem arrises when I try to pass data to my VC. Because nvc is the navigation controller I tried to get the vc with nvc.rootViewController but I get the error: Value of type 'UIViewController' has no member 'rootViewController'. If I print out nvc I see that it is in fact a UINavigationController. I assume this error occurs because I used instantiateViewController to get the Navigation Controller from storyboard, but I'm confused why it prints out as a UINavigationController.
I've also tried to cast nvc as? TaskEditViewController but nvc is the navigation controller and not the vc so this doesn't work.
Ultimately I want to pass data to my VC as such before presenting modally:
vc.detail = "example"
Any ideas how to do this?
You need to cast result of instantiateViewController to the appropriate type as the type of the vc you set the identifier to in storyboard as it returns a generic object of type UIViewController , so cast it to UINavigationController , same case also is for property topViewController that needs to be casted too
if let nvc = self.storyboard?.instantiateViewController(withIdentifier: "EditTaskNavController") as? UINavigationController, let top = nvc.topViewController as? TaskEditViewController {
top.someProperty = /* some value */
self.present(nvc, animated: true) {
/* some code */
}
}
I have a Main VC which has a connection to Tab Bar Controller via segue and that Tab Bar controller has 2 sections and those are 2 ViewControllers. I need to pass data from Main VC to that specific VC which derived from Tab Bar Controller using segue. Since i am able to use only one segue (to show only TBController), is there any way to achieve to pass data directly from main to another by skipping Tab Bar controller?
Ps. Those 2 VC have a navigation controller connection between Tab Bar Controller and themselves as well.
Try This
var secondTab = self.tabBarController?.viewControllers[1] as SecondViewController
secondTab.array = firstArray
On prepare for segue function you can get the tabBar associated controllers via TabBarControllers property viewControllers - that let you select one of a tab bar controller's view controllers to become the active view controller. then you use tabBarController's property selectedIndex to actually segue to that VC.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let index = 0//the index of the tab you wish to segue to
if let bar = segue.destination as? TabBarVC {
if let nav = bar.viewControllers[index] as? UINavigationController {
if let navChildVC = navigationController.viewControllers[0] as? YourVCType { // index of the controller of enumerate to find it
// pass data to navChildVC
bar.selectedIndex = index
}
}
}
}
I have a ViewController that I want to present when a push notification is opened from the locked screen.
The view controller is embedded inside a chain of view controllers, the chain is:
TabbedBarController(root) -> NavigationController0 -> ViewController0 -> ViewController1 -> ViewControllerIWant
I have two variables from the push notification I want to pass into ViewControllerIWant
So far I have:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootVC = storyboard.instantiateViewControllerWithIdentifier("HomeVC") as! UITabBarController // HomeVC is the TabbedBarController
let NaviVC = storyboard.instantiateViewControllerWithIdentifier("NavigationController0") as! UINavigationController // NavigationController0 is the Navigation Controller for this stack
let ViewController0 = storyBoard.instantiateViewControllerWithIdentifier("ViewController0") as ViewController0
let ViewController1 = storyBoard.instantiateViewControllerWithIdentifier("ViewController1") as ViewController1
let ViewControllerIWant = storyBoard.instantiateViewControllerWithIdentifier("ViewControllerIWant") as ViewControllerIWant
// Pass the Variables into ViewControllerIWant
ViewControllerIWant.Variable0 = "This is the first Variable"
ViewControllerIWant.Variable1 = "this is the second variable"
// Load the VC
self.presentViewController(ViewControllerIWant, animated:true, completion:nil)
I can load ViewControllerIWant directly but then loose the navigation and tab controllers so moving backwards requires you yo close the app and reload.
How do I nest the VC's so that when the user swipes this notification it loads ViewControllerIWant ?
You can just set the viewControllers of the navigationController. And show the current navigationController.
In your example you should add this after the line will be (I have change all to a lowercase. You really shouldn't be using a uppercase as variable)
viewControllerIWant.variable1 = "this is the second variable"
naviVC.viewControllers = [viewController0, viewController1, viewControllerIWant]
You can keep your navigation controller by using segues instead of presenting the View Controller
Once the segue is created from your starting VC into your desired VC, you give it an identifier (e.g. "segueFromVC1ToVCIWant").
Then call the method as an action of your notification:
self.performSegueWithIdentifier("segueFromVC1ToVCIWant", sender: nil)
Finally can pass the variables like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segueFromVC1ToVCIWant"){
let viewControllerIWant = segue.destinationViewController as! ViewControllerIWant
ViewControllerIWant.variable0 = self.variable0
ViewControllerIWant.variable1 = self.variable1
}
}
Make sure you create variables with the same name in ViewControllerIWant in order to receive the values.