On photo below I show you my storyboard. On TableView Controller I have expandable menu. What I cannot achieve right now is to switch on second tab bar item (green one) and let it know what is ID of selected menu item. Between tab bar controller and green and blue controllers I have relationship "view controllers". I tried to call directly green view controller with this code:
let secondTab = self.storyboard?.instantiateViewController(withIdentifier: "secondstoryboardid") as! SecondVC
self.present(plavi, animated: true, completion: nil)
This code displays second tab view but without tab bar and everything derived from container view.
If you configured correctly such storyboard, you should get the reference to the UITabBarController through prepare(segue), doing so:
private weak var tabBarViewController:UITabBarController?
// be sure to add prepare inside your segue manager (the container in this case)
override public func prepare(for segue: UIStoryboardSegue, sender: Any?) {
self.tabBarViewController = segue.destination as? UITabBarController
}
besides you may want to declare an enum to have a clean access to the embedded view controllers:
enum TabType:Int {
case blue
case green
}
thus you might define a function for injecting the selected indexPath, doing something like:
func inject(indexPath:IndexPath, into tab:TabType) {
if let greenVc = tabBarViewController?.viewControllers?[tab.rawValue] as? GreenViewController {
greenVc.selectedIndexPath = indexPath
tabBarViewController?.selectedIndex = tab.rawValue // this will auto select the TabType viewcontroller
}
}
finally whenever you have to change the current selection, you may call inject with the new indexPath:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.inject(indexPath: indexPath, into: .green)
}
Note
Call instantiateViewController means making a new "fake" view controller, which is a wrong approach, since you already have it embedded in your UITabBarController.viewControllers
Related
Here is the thing: my storyboard as a SettingViewController (after named SVC) and a StationsViewcontroller from which the latter is wired to a details view controller by the "openDetails" segue.
For practical reasons, my SVC can push several levels of specifics settings view controllers, which I did not defined in the storyboard.
At one point, one of those vc pushes the StationsViewController, which then can issue a performSegue to the detail: unfortunately, I get the "openDetails" segue is not defined.
What should I do to fix the issue?
--
The code has nothing peculiar, in the SettingsViewController, I push others VC, including the StationsViewController:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Gracefully remove the selection
tableView.deselectRow(at: indexPath, animated: true)
guard let section = MainSections(rawValue: indexPath.section) else { return }
switch section {
case .stations:
let vc = StationsViewController()
// Do no segue at dismiss
vc.onlyDismiss = true
vc.didDismiss = {
self.navigationController?.popViewController(animated: true)
}
push(vc, hideNavigation: true)
// Blah blah
And in the StationsViewController where I want to get to the details vc, I perform the segue:
func openPoiDetails(_ gs: GasStation) {
poiDetails = gs
performSegue(withIdentifier: "poiDetails", sender: nil)
// Blah blah
The storyboard is as follows, and we can see the StationsViewController on the left is wired to the details vc via the "openDetails" segue:
The problem is that your StationsViewController was just created from its initializer and the storyboard knows nothing about it. Therefore, you can't use the segue. Instead, you need to ask the storyboard to instantiate the StationsViewController:
Replace:
let vc = StationsViewController()
with:
let vc = self.storyboard?.instantiateViewController(withIdentifier: "StationsViewController") as! StationsViewController
Make sure you set the "StationsViewController" as the Storyboard ID in the Identify Inspector in Xcode.
I have a bit of a unique UISplitViewController setup (I think). My master view controller is a table view controller within a navigation controller, which is normal. However, my detail view controller is a UITabBarController. Each tab has a navigation controller to wrap the content within.
When the user selects a table row within the master view controller, I select a tab in the detail view controller and start pushing a view controller:
let detailViewController = masterViewController?.splitViewController
.viewControllers[1] as? UITabBarController
let selectIndex = 2
let viewController = (detailViewController.viewControllers?[selectIndex] as? UINavigationController)?.viewControllers.first
detailViewController.selectIndex = selectIndex
viewController.show(myCustomViewController)
This works great on iPad.. selecting an item in the master view controller table selects the tab in the detail view controller and pushes view controllers to it.
On iPhone, I have the master view controller shown on initial load using this post:
class MasterViewController: UIViewController {
private var collapseDetailViewController = true
override func viewDidLoad() {
super.viewDidLoad()
splitViewController?.delegate = self
}
// ...
}
extension MasterViewController: UISplitViewControllerDelegate {
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
return collapseDetailViewController
}
}
extension MasterViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
collapseDetailViewController = false
selectTabAndPush(index: 2, vc: myCustomViewController)
// Tried swapping the order of these but still no luck
}
}
However, selecting a row in the master view controller table literally does nothing. How can I get the detail view controller to show when a row in the master table is selected?
At very first, why you need both functionalities : Master-Detail and Tab. Because if you are implementing master detail with tab then you must have equal number of tab with the equal number of rows in Master, then only you can navigate to the tab when you select row. In iPhone there would be max 5 tabs only and if you have 7 rows in Master then what happened if user will click on row 6 and 7?
Because as per design concept : You should choose only one as per requirements.
Master-Detail -> Provides facilities like side menu, so user can access many more option from these.
TabBar -> Provide facilities of more option with having menu at bottom.
Anyway, you might have some unique requirement.
As you said on iPad it is working fine. So for iPhone you can go with below way :
UI in storyboard should be like :
Create or add UITabBarController in your project. (I have created tabbarcontroller named CustomTabController).
Then in didSelectRowAt, push tabbarcontroller :
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.phone {
let tabController = self.storyboard?.instantiateViewController(withIdentifier: "CustomTabController") as! CustomTabController
self.navigationController?.pushViewController(tabController, animated: true)
tab.selectedIndex = indexPath.row
}
else {
// iPad code here
}
}
But make sure, that you have enough number of tabs same as number of rows.
Back Button shows in StoryBoard but not Simulator.
I added a segue from TableViewController to DetailViewController to pass data. The Storyboard automatically shows a Back Button on DetailViewController, but when I run the Simulator, the button doesn't show up.
This is my Storyboard:
A closer look of TableViewController and DetailViewController:
But in my Simulator the button doesn't show up:
The hierarchy of the whole project:
I want to know where to configure the back button(in my segue?), or instead deleting the button(not letting it show in the Storyboard) so I can create one myself.
The code of my segue in my TableViewController:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showDetailView", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch (segue.destination, sender) {
case (let controller as DetailViewController, let indexPath as NSIndexPath):
controller.receivedName = storeList[indexPath.row].name!
controller.receivedDesc = storeList[indexPath.row].desc!
controller.receivedRate = storeList[indexPath.row].rate
controller.receivedUrl = storeList[indexPath.row].url!
default:
print("unknown segue")
break
}
}
Your initial view controller in the storyboard is actually the TabelViewController (you can see there is an arrow to it).
Thats' why when you start the scene and the TableViewController shows but it is not embedded in the navigation because the navigation controller has never been created.
Just change which is the initial view controller to be the navigation controller or any other before the navigation controller which holds the TableViewController.
You can just drag the arrow to change the initial controller in the storyboard
In my project, I have a few View Controllers and I use UINavigationController.
When I make a seque from UIViewController to UITableViewController I have "back" button added automatically, but when I make seque from UITableViewController to UIContentViewController tapped a cell, I don't have a "back" button in UIContentViewController.
Plan of my app is here
The segue in UITableViewController looks like that:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let CV = self.storyboard?.instantiateViewControllerWithIdentifier("ContentViewController") as! ContentViewController
let tnumber = tableView.indexPathForSelectedRow?.item
CV.titleContent = daneOWpisach?[tnumber!]["title"]
CV.webContent = daneOWpisach?[tnumber!]["description"]
CV.category = category
self.presentViewController(CV, animated: true, completion: nil)
}
What should I do, if I would like to have a back button in UIContentViewController added automatically?
In your code you are presenting the view controller on cell selection..! Back button will show only if you perform push not on presenting the view.
In the above diagram what I have seen is you already make the segue from UITableViewController to UIContentViewController. select the segue and go to attribute inspector give the identifier for the segue.
On the cell selection perform the segue action with the identifier you specified
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("Your Segue name", sender: sender)
}
If you want to pass data to destination view controller, do it like below..
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "Your Segue name" {
let CV = segue.destinationViewController as! ContentViewController
let tnumber = tableView.indexPathForSelectedRow?.item
CV.titleContent = daneOWpisach?[tnumber!]["title"]
CV.webContent = daneOWpisach?[tnumber!]["description"]
CV.category = category
}
}
Hope this helps you
So I have a table view that I want to segue to specific views when a specific cell is selected. For example, if the cell at index 0 is selected, I want the "RedViewController" to be visible.
The example I keep receiving looks something like this (located in the VC with the table view)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let destination = segue.destinationViewController as? RedViewController {
if let blogIndex = tableView.indexPathForSelectedRow()?.row {
destination.blogName = swiftBlogs[blogIndex]
}
}
}
}
(Where blogName and swiftBlogs are random examples)
But this just loads specific data into a singular view controller. Preferably I want a switch statement for each index path that makes a specific VC visible.
Ok, so write your code that way. Don't link your table views directly with a segue. Instead write code in your didSelectRowAtIndexPath.
In that method you can write a switch statement, or use an array lookup, to load a view controller using a unique ID and the instantiateViewControllerWithIdentifier, or trigger a through code using performSegueWithIdentifier.
I figured it out thanks to Duncan C and a little more research!
In the table view VC I added the handy function
func switchToViewController(identifier: String) {
let viewController = self.storyboard?.instantiateViewControllerWithIdentifier(identifier) as! UIViewController
self.navigationController?.setViewControllers([viewController], animated: false)
}
And in the didselectRowAtIndexPath
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedRow = indexPath.indexAtPosition(indexPath.row)
switch selectedRow {
case 0:
switchToViewController("RedVCIdentifier")
default:
println("Unknown Cell selected")
}
}
But also had to add a Storyboard ID to the RedViewController in the storyboard as "RedVCIdentifier"