Short explanation.
I have a ContainerViewController that I'm pushing to a navigationStack.
The ContainerViewController has 2 child UIViewController. A SlidePanelViewController (a slide-out menu) and a CenterViewController (the content), my CenterViewController is a UITabBarController which currently has 2 UIViewController.
I have a button in my menu to that I need to change the UIViewController in my tabBarController. However nothing happens when I call my functions.
Here's the function I am trying to call:
func premiumFormulaTapGesture() {
tabBarController?.selectedIndex = 1
}
(I also tried setting it to 0 and 2. Still with no results.) I tried putting self. in front of it without any luck.
I also tried putting it as a function in my ContainerViewController. But that didn't seem to work either.
Here's how I'm setting up my UITabBarController:
var centerTabBarController: UITabBarController!
in ViewDidLoad():
let tabBarController = UITabBarController()
let suggestionsVC = mySuggestions_VC()
let testVC = detaiLSuggestion_VC()
let controllers = [suggestionsVC,testVC]
tabBarController.setViewControllers(controllers, animated: false)
centerViewController = tabBarController
view.addSubview(tabBarController.view)
addChildViewController(tabBarController)
my tabBarController does show up. And I am able to manually tap the 2 buttons on it, where it switches between the viewControllers as expected. I later plan on hiding the UITabBarController, and use the menu. The UITabBarController is going to be my method of changing UIViewController from the menu.
Also just to clarify. I'm not using storyboards.
Any help changing the viewControllers in my tabBarController would be greatly appreciated!
Phillip Mills didn't post an answer yet. So I'm just closing the open question.
My problem was that there was 2 UITabBarControllers named the same "tabBarController" after changing the name to "menuTabBarController" everything worked fine.
again thanks to Phillip Mills for solving my issue in the comments.
Related
I have two view controllers which are defined in storyboard as,
UIViewController -> (Show Detail Segue) -> UITabBarController
My problem is; I am using a library called netfox to debug my HTTP requests. And this librarie's UI is being triggered by Shake Gesture. But when I come to the UITabBarController Shake Gesture on the simulator does not work at first time. At the second time it dismisses the ViewController that is currently on the screen and obviously a child of UITabBarController and goes back to the initial UIViewController. This is exactly like connecting two ViewControllers with modal segue and calling self.dismiss() from the child one.
I tried to change rootViewController of the UIApplication by,
UIApplication.shared.keyWindow.rootViewController = self
in the viewDidLoad() method of the UITabBarController and it worked. However, for this solution, the items(buttons, titles) in the UINavigationBar of any UINavigationController that is the child of UITabBarController are missing.
I have no idea why this is happening. If someone helps me while I am solving this, I would be really appreciated.
Instead of using
self.performSegue(withIdentifier:_)
calling this in the UIViewController have worked:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = mainStoryboard.instantiateViewControllerWithIdentifier("tabBarcontroller") as UITabBarController
UIApplication.sharedApplication().keyWindow?.rootViewController = viewController;
Respect to this answer.
I have a UITabBarController which is connected to 3 UINavigationControllers. Each of these have a UIViewController as a root view controller. When I click on a button in one of these ViewControllers (VC1), I want it to segue to new ViewController (VC2). Simple enough.
During runtime, when the segue takes place and VC2 appears, the navigation bar disappears.
The navigation bar is present in the storyBoard / interface builder, and the segue is the type: present (Push).
Code in VC1:
#IBAction func create_clicked(_ sender: Any) {
performSegue(withIdentifier: "segueIdentifier", sender: self)
}
It might help to know how the UITabBarController tabBar is instantiated:
func login() {
let storyBoard = UIStoryBoard(name: "Main", bundle: nil)
let tabBar = storyBoard.instantiateViewController(withIdentifier: "tabBar")
window?.rootViewController = tabBar
}
Please help.
After a great deal of experimentation, we determined that even though Interface Builder said this was a Show (Push) segue, and even though we were in a Navigation Controller to start with,
it was behaving as a Present Modally segue.
So we changed the segue type, using the pop-up menu in Interface Builder, from Show (Push) to plain Push, even though that's deprecated — and the interface worked correctly.
And then we changed it back to Show (Push) and the interface continued to work correctly. Problem solved!
I suspect the storyboard was corrupted in some way. Obviously Interface Builder should not lie to you about what kind of segue this is, but basically, that is what it seems to have been doing.
EDIT The problem was caused, apparently, by making a Show Detail segue and changing it to Show. When you do that, it's still a Show Detail segue, which in this context behaves as a modal presentation. That's an Xcode bug! I reported it, and Apple now says this will be fixed in Xcode 9.3.
I have a viewController related to a Tab Bar Controller: the first one.
Clicking on a cell of its tableview, I'll show programmatically another viewController that's not linked to the first viewController with no segue (because of right reasons).
Now, my goal is to present/instantiate the second viewController related to the tab bar mentioned at the beginning of this question.
If I'll use this:
let vc=storyboard?.instantiateViewController(withIdentifier: "offerteView") as! SecondViewController
It'll be presented the mentioned viewController without the tab bar of course.
How can I solve it?
Embed the first view controller in a navigation controller and use its pushViewController function to show the second view controller.
let vc = storyboard?.instantiateViewController(withIdentifier: "offerteView") as! SecondViewController
navigationController?.pushViewController(vc, animated: true)
when using tab bars the view controllers are called on the basis of their Index and because of this the tab bars are still maintained and this can be done like this.
self.tabBarController!.selectedViewController! = self.tabBarController!.viewControllers[3]
where [3] is the index position of the View Controller.
or
self.tabBarController.selectedIndex = 1;
//Hope it was helpful. Happy Coding.
I searched for similar problems, but none solved my problem. I need to call a new view controller , but when I do it a black screen appears , as I do to associate with the main storyboard screen?
Here is my suggestion:
let vc = storyboard!.instantiateViewControllerWithIdentifier("viewIdentifier") as! viewNeedToCall
You can init and use vc with navigation.
A black screen appears when the simulator can't figure what to present. It seems lost in the sense which controller it has to present.
If it is the first screen then make sure that you have set the initial view controller in Main.storyboard. If not, then you have to call(instantiate) that view controller using the instantiateViewControllerWithIdentifier property of a storyboard.
For e.g.If you want to present a ViewController named as YourViewController then
Make an object of YourViewController, say yourVCObject.
let yourVCObject = self.storyboard?.instantiateViewControllerWithIdentifier("YourViewController") as? YourViewController
Present your desired ViewController with -
self.presentViewController(yourVCObj!, animated: true, completion: nil)
Here connect your class YourViewController in the Identity Inspector in the "Class" and type "YourViewController" in the Storyboard ID and check the "Use Storyboard ID"
Looks like you forgot to connect your VCs to a NAVIGATION CONTROLLER.
Without Navigation controller you cannot move to another VC.
I've got an app with a mapViewController embedded in a navController. In the mapVC ive got a single bar button item which when clicked I want to conditionally "push" segue to one of a number of different view controllers. To achieve that ive set up an ibaction on the button and have the conditional "performSegueWithIdentifier" code in the relevant buttons ibaction method ie
#IBAction func addButtonClicked(sender: UIBarButtonItem) {
let lastAdd = "addItem"
if lastAdd == "addItem"{
self.performSegueWithIdentifier("addItem", sender: self)
} else {
self.performSegueWithIdentifier("addEvent", sender: self)
}
}
this will take me to either the addItemVC or the addEventVC. in each of those viewControllers (ie the addItemVC and the addEventVC) I want to have a segmented control in the navigation bar which, when clicked, will take me to the alternative VC ie if addItemVC is currently displayed, and the addEvent section of the segmented control is clicked, I want to display the addEventVC. Im following Red Artisans page on how to do this but in his example he is instantiating all view controller options upfront in the app delegate and so can easily get reference to each view controller and link it to the clicked segement of the segmented control within his rootVC
Where im confused is .. seeing Im using conditional code before performing each segue, i assume that im only instantiating one viewController at a time when the bar button item is pressed. So how can i get an array of view controllers to pass to the VC im segueing to so that i can create the required segmented control in that VC. I assume i could manually create the destination VC array in my mapViewController and pass these across but wouldnt that mean im instatiating a different instance to the ones automatically created by the segue process?
Yes, you are right: if you manually create the two VCs in your mapViewController, they will be different instances from those created by a segue. So if you want to stick with Red Artisan's solution, present the VCs using code rather than segues. You can still design the two VCs in your storyboard, give them each a unique identifier and then use the instantiateViewControllerWithIdentifier function of self.storyboard to create the instances.
You can use most of Red Artisan's app delegate code in your mapViewController, but with a few tweaks: eg. to use the existing navigation controller (in which your mapViewController is embedded), and the [window ...] lines are superfluous. The thing to watch out for will be the indexDidChangeForSegmentedControl function, which assumes that the VCs you are switching between are the rootViewControllers for the navigation controller (ie. that they are the only item in the navigation controller's viewControllers array). In your case you have mapViewController as (I assume) the rootViewController, so you will have to amend the indexDidChangeForSegmentedControl function to create an array with the mapViewController at index 0 and the relevant (addItem or addEvent) VC at index 1. I don't know how well this method will animate, nor whether back buttons etc will be properly set.
If you want to stick with segues, there are a couple of solutions: one would be to use a UITabBarController (and hide the tabBar). You would have the addItem and addEvent VCs as separate tabs, and when you segue to the tabBarController, you could set which tab is selected. But my preferred solution would be to segue to a UIPageViewController. You would could either create the VCs in mapViewController and pass them as part of the segue, or just pass an indicator as to which was selected, and have the pageViewController instantiate them and present the relevant one. You could then use the UISegmentedControl to trigger switching between VCs. See this answer for something similar.
thanks pbasdf for your detailed instructions. its taken me quite a while but i seem to be close to getting it working. i followed most of your instructions and you were spot on with what you said.
first from the mapVC on press of the + bar button item i create the addItem and addEvent VCs using instantiateViewControllerWithIdentifier and create the segmented control.
#IBAction func addButtonClicked(sender: UIBarButtonItem) {
//at this stage just manually set default target VC
let lastAdd = "addItem"
//get an array of the target viewcontrollers
var viewControllers = segmentViewControllers()
//initz the segmentscontoller with the current navcontroller if doesnt already exist
if segmentsController == nil {
segmentsController = SegmentsController(navController: self.navigationController!, viewControllers: viewControllers)
segmentedControl.addTarget(segmentsController, action: "indexDidChangeForSegmentControl:", forControlEvents: UIControlEvents.ValueChanged)
//add the segmented control to the VC by setting first user experience which calls indexdidchangeforsegmentedcontrol
firstUserExperience()
}
segmentsController?.indexDidChangeForSegmentControl(segmentedControl)
}
//create an array of the target view controller. called from addbutton clicked
func segmentViewControllers() -> [UIViewController] {
//create an instance of the viewcontrollers
let addItemVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("AddItemVC") as ViewController
let addEventVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("AddEventVC") as ViewController
var viewControllers = [addItemVC, addEventVC]
return viewControllers
}
in the segmentsController i created 2 arrays, 1 to hold the different VCs and one to hold the navigation stack. i also set the passed the segmented control to the incoming VCs
class SegmentsController: NSObject {
var navController: UINavigationController?
var viewControllersOptionsArray: [UIViewController] = []
var viewControllersNavArray: [UIViewController] = []
//MARK: - INITIALIZER
init(navController: UINavigationController, viewControllers: [UIViewController]) {
self.navController = navController
self.viewControllersOptionsArray = viewControllers
}
//MARK: SEGMENT INDEX METHOD
func indexDidChangeForSegmentControl(segmentedControl: UISegmentedControl) {
var index = segmentedControl.selectedSegmentIndex
var incomingViewController = viewControllersOptionsArray[index]
//set the viewControllersNavArray
if let mapVC = navController?.viewControllers[0] as? MapViewController {
viewControllersNavArray = [mapVC, incomingViewController]
}
//set the navcontroller with a new array of viewcontrollers
navController?.setViewControllers(viewControllersNavArray, animated: true)
//set the title of the incoming view controller
incomingViewController.navigationItem.titleView = segmentedControl
//set the seg control variable of the incoming VCs
if let iVC = incomingViewController as? AddItemViewController {
iVC.segmentedControl = segmentedControl
} else if let iVC = incomingViewController as? AddEventViewController {
iVC.segmentedControl = segmentedControl
}
}
}
In the addItem and addEvent VCs i figured i needed to pass the current selectedSegmentIndex back to the mapVC if the user presses the back button - wasnt sure how to do this and ended up using an extension i downloaded called UIViewController+BackButtonHandler to handle it.
Im sure my code could be much better written but the only thing that im still having trouble with is that i want the VC transitions to be animated. the navigation seems to work fine if i set animated to false in the navController?.setViewControllers(viewControllersNavArray, animated: true) line but if i set it to true, the segmentedControl briefly flashes an appearance on the nav bar of the incoming VC but then disappears. Its still there so i can still navigate but you cant see it. i figure that it has something to do with setting it before the view has properly loaded but even if i put the code to set it ie
incomingViewController.navigationItem.titleView = segmentedControl
in the new top VC in viewDidLoad or viewDidAppear i still get the same problem. i also thought i might be able to fix it if i could put the incomingViewController.navigationItem.titleView = segmentedControl code in a completion block but the setViewControllers method doesnt appear to have a completion handler.
Any suggestions?