How to open a TabBarController in ViewControllers that Instantiate from SlideMenuViewController - ios

I want to have tabBar in all my ViewControllers. I have implemented the SWRevealViewController, which have two view controllers linked one is TabBarController and another is TableViewController I wants to have the same tabBar in all my ViewControllers that Segues from TableViewController.

I want to have tabBar in all my ViewControllers. I have implemented
the SWRevealViewController, which have two view controllers linked one
is TabBarController and another is TableViewController I wants to have
the same tabBar in all my ViewControllers that Segues from
TableViewController.
You can do some tweak like this, create the custom delegate and set delegate of TableViewController and TabBarController to SWRevealViewController. Now first time lets say you open the TableViewController and when tap on any cell then just invoke the delegate method which should execute inside SWRevealViewController class and then perform segue inside that class which should open the TabBarController and when click back button of TabBarController again invoke the delegate method which should execute inside SWRevealViewController class and perform segue to open the TableViewController

This shouldn't be too difficult. Your RevealViewController (the initial controller of your storyboard) already seems to be a subclass of SWRevealViewController. This is good, but it's not essentially needed for this scenario. What you do need is a UIViewController implementation for your side menu. The Storyboard alone won't do.
Also, I wouldn't use segues here to switch between the tabs because a segue has a destination view controller. So every time you perform a segue, you would create a new instance of the destination view controller. I would rather implement a UIViewController that handles the cell selection of the UITableView
Now let's say you create a UIViewController or UITableViewController as your menu (actually the RearViewController). Make sure to assign this class to your UITableViewController in your storyboard.
class MenuViewController: UITableViewController {
// MARK: UITableViewDelegate
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let tabIndex = indexPath.row
let tabbarVC = (revealViewController().frontViewController as! UITabBarController)
tabbarVC.selectedIndex = tabIndex // this assumes the menu items are in the same order as the tabs. if not you need to adjust this
revealViewController().revealToggle(animated: true) // close the side menu after switching tabs
}
}
this should be all you need

Related

How I could clean UINavigationBar transitions history?

I currently have parental "menu" TableView with UINavigationBar and from each cell there is a segues by reference outlet to 3 similar Views with different information.
In each View there is a buttons to other 2 Views.
With every button's segue opens another View.
The problem:
From every View UINavigationBar's back button returns me to previous View but i tries to make back button to "menu".
Additional Bar Button Item and segue from it makes very close effect but segue animation is not like in UINavigationController.
How I could clean UINavigationBar transitions history in segue to initial View?
You can try pop to root view controller or You can edit navigation controller viewControllers property and remove/add some VC in between.
You can try Unwind Segue mechanism too.
Here are some methods(function) that navigation controller providing for pop operations. They are returning optional UIViewController (intance) from it’s navigation stack, that is popped.
open func popViewController(animated: Bool) -> UIViewController? // Returns the popped controller.
open func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? // Pops view controllers until the one specified is on top. Returns the popped controllers.
open func popToRootViewController(animated: Bool) -> [UIViewController]?
Here is sample code as a solution to your query::
// if you want to back to root of your app
if let rootNavigationController = self.window?.rootViewController as? UINavigationController {
rootNavigationController.popToRootViewControllerAnimated(true)
}
// But if you want to back to root of your current navigation
if let viewcontroller = self.storyboard?.instantiateViewController(withIdentifier: "NewViewController") as? NewViewController { // or instantiate view controller using any other method
viewcontroller.navigationController?.popToRootViewControllerAnimated(true)
}

Push to navigation controller from UICollectionViewCell in UITableViewCell

Hi people and please help
Am not using interface builder.
In AppDelegate:
...
tabBarController.viewControllers = [tabOne, tabTwo, tabThree, tabFour]
window?.rootViewController = UINavigationController(rootViewController: tabBarController)
...
in tabOne witch is UIViewController, lazy load UITableView with custom cell and in that cell row I lazy load UICollectionView.
And i need to push in navigationController some viewController?
Thanks
Please make tabBarController as rootViewController of window
Then create 4 navigationControllers for 4 viewController (tabOne, tabTwo, tabThree, tabFour) of tabbarController.
Like these codes:
let viewControllers = [UINavigationController(rootViewController: tabOne),
UINavigationController(rootViewController: tabTwo),
UINavigationController(rootViewController: tabThree),
UINavigationController(rootViewController: tabFour)]
tabBarController.viewControllers = viewControllers
window?.rootViewController = tabBarController
TabbarController is a containerViewController, so:
you need to create 4 navigationController for 4 tabs, because they have the particular flows.
as your code, tabOne.navigationViewController is nil so you can't push other viewcontroller because your navigationController doesn't belong to tabOne, it belongs to tabBarController.
You have to take callback from Collection view cell to the controller or class(which contains the UICollectionView, I think in your case table view cell's .m file)
Then callback on the controller which contains the main table view.
Now you are on the root view controller of the tab.
Push the navigation controller from here (If you already have any navigation controller)
Your case:
TabBarViewController -> RootViewController(One out of four tab) ->
TableView -> TableViewCell -> UICollectionView -> UICollectionViewCell
Take the callback on RootViewController Then you can do anything.
Remember You should have navigation controller to push another controller.
I find solution.
In super class for ViewControllers, create function that push to navigation, and from View (Cell) need to delegate self, and call that function. Thanks to all who try to help

How to test a segue attached in storyboard

I want to attach a Show segue to a property cell of a table view in storyboard. Now I want to test this binding is exists. i.e. I want to have a test that would fall if I delete the segue in the Interface Builder or changed the segue identifier.
From a user perspective, user taps on a cell of the table view and then this segue should be performed.
From testing perspective, I can swizzle the prepare(for:sender:) method the verify the performing of a segue, but I don't know how to trigger a "tap" programmatically.
I've tried tableView.selectRow(at:animated:scrollPosition), cell.setSelected(_:animated:) and those both didn't work.
How to programmatically trigger the segue added via InterfaceBuilder ?
Or is there any other way to test this segue binding?
Update for further clarification
I know how to trigger a segue programmatically -- thus that is not what being asked here.
The segue triggering work is done behind the scene by storyboard and there is no segue triggering code in the production code (there is only a overriden prepare(for:sender:)). And the app work as intended. The problem here is I need a test to guarantee this behind-the-scene triggering always exists, that is: if someday I got drunk and mis-edited the storyboard to connect the original segue to somewhere irrelevant, there would be a test to go red and kick me in the ass.
I was able to test a segue that is wired up to the view controller instead of the table view cell.
In the app, I perform the segue when the cell is tapped. This is roughly equivalent to what you get if you wire up the segue to the table view cell.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showNextView", sender: nil)
}
This allows the segue to be tested as follows:
func testSegue() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
// create the view controller that has the segue to be tested
let viewController = appDelegate.window?.rootViewController?.storyboard?.instantiateViewController(withIdentifier: "TableViewController") as! TableViewController
// assuming the view controller does a Show segue, put it in a navigation controller
let navigationController = UINavigationController(rootViewController: viewController)
// this is needed or test fails!
navigationController.view.layoutIfNeeded()
// replace the root view controller with the navigation controller
appDelegate.window!.rootViewController = navigationController
// finally, select the row! this fires the segue
viewController.tableView(viewController.tableView, didSelectRowAt: IndexPath(row: 0, section: 0))
// assert something about the result of the segue
XCTAssertTrue(navigationController.visibleViewController is SeguedViewController)
}
Just use this func :
[self performSegueWithIdentifier:#"YOURIDENTIFIER" sender:self];
It may help you test your segue.

Performing Segue from - TableViewCell.xib to UIView - and - TableView.xib to UIViewController -

How can I perform segue from one .xib (TableCell.xib) to another .xib(UIViewController) on click on TableCell?
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print(results?[indexPath.row]) // prints successfully
...
// apply below chunks here...
}
First, I tried setting up a manual segue in Storyboard. Note that, TableView & TableViewCell are external xib but parent of TableView is in Storyboard. So, I tried control-dragging from parent-ViewController to the detailViewController (and identifier: showDetails)..However, doesn't work.
...
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.performSegueWithIdentifier("showDetails", sender: AnyObject?())
})
...
Obviously, I received this error (because TableView is in .xib whereas parent and detailsVC are in Storyboard and I ctrl+dragged from parentVC to detailsVC but TableView1 apparently doesn't recognise the segue in Main Storyboard):
TableView1: 0x7f97626a3280>) has no segue with identifier 'showDetails''
Then I tried creating a ViewController and tried adding var parentNavigationController : UINavigationController! at the top and referencing below chunk inside didSelectRowAtIndex
...
let newVC : UIViewController = CellDetailsVC()
newVC.title = "DetailsView"
print(newVC)
parentNavigationController!.pushViewController(newVC, animated: true)
However, I am receiving an error:
DetailsVC: 0x7ff7794ae550>
fatal error: unexpectedly found nil while unwrapping an Optional value
Finally, I also tried below chunk inside didSelectRowAtIndex, and got something close (as I can use dismissView to < Back); but not exactly what I want. Its problem is that the segue animation looks like the page is coming from bottom.
...
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("VideoDetailsVC") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
I am also receiving a warning (but not crash) for some reason I couldn't figure out:
Presenting view controllers on detached view controllers is discouraged
Which approach is the right one? Or is there a better way to adapt to achieve what I am trying to?
Firstly, what is the right way to perform segue from didSelectRowAtIndex (in TableView.xib class) to a new ViewController (either Storyboard or xib)?
Secondly, assume that there is an image thumbnail in the TableViewCell.xib. What is right way of performing segue to a new UIView on click on that view? (like full-screen image / dismiss-able)
Your second approach looks to be right one but of course you are doing something wrong there. You need to drag and drop Segue from Parent ViewController to Detail ViewController and give it a Segue identifier in Storyboard (check attached Image below). IN didSelectRowAtIndexPath you need to do below code:
self.performSegueWithIdentifier("showDetailViewController", sender: nil)
As I can see you already tried that but it looks like you missed some step either setting Segue Identifier or giving wrong identifier in performSegueWithIdentifier method. Also your Parent ViewController should be embedded in UINavigationController in case you are missing that thing :)
Also keep in mind you should perform navigation (here its Segue) from one ViewController to another ViewController at same level not from one View to another ViewController.

UIPageViewControllerDataSource - segueForUnwindingToViewController not being called

I have a UIViewController that is set up as a UIPageViewControllerDataSource named MainPVC.swift. It is my entry point on my Storyboard. In it, I am instantiating another UIViewController named TrackerVC which is set up in my Storyboard - but not connected to MainPVC by segue.
Instantiating TrackerVC:
func viewControllerAtIndex(index: Int) -> TrackerVC {
let childViewController = self.storyboard?.instantiateViewControllerWithIdentifier("trackerVC") as TrackerVC
childViewController.screenIndex = index
return childViewController
}
I have another UIViewController named NotesVC. When I push a button on TrackerVC, I use a custom UIStoryBoardSegue to display NotesVC. On NotesVC, I have another button that I want to return to TrackerVC using another custom UIStoryboardSegue via Unwind.
Cancel button tapped in NotesVC:
#IBAction func cancelButtonTapped(sender: AnyObject) {
self.performSegueWithIdentifier("unwindFromNotesSegue", sender: self)
}
Now to my issue -
When I push the button on TrackerVC, the custom UIStoryboardSegue works great and displays NotesVC as expected. However, when I tap the button on NotesVC, the custom UIStoryboardSegue is ignored and the generic segue that slides the view down is being used.
I have traced the issue down to segueForUnwindingToViewController not being called in TrackerVC. I have all of my connections set up properly in my Storyboard and all of the required methods (the method unwind uses).
I have also tried creating a 3rd UIViewController and displaying it using the custom UIStoryboardSegues along with Unwind and calling it from NotesVC and everything works fine. segueForUnwindingToViewController is being called on NotesVC as expected.
Thanks for any help!
Do you have NavigationController attached? You may need to subclass the NavigationController and add the segueForUnwindingToViewController there. It's what solved the similar problem I was having.

Resources