I am using didDeselectRowAtIndexPath to navigate through different Storyboards like:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let story = ["News","Video","Twitter","Request Info","More"]
let vc = self.storyboard?.instantiateViewControllerWithIdentifier(story[indexPath.row]) as NewsFeedTableViewController
self.presentViewController(vc, animated: true, completion: nil)
}
However, the Navigation Bar does not show up when I use this function. But, when I use segue, the Navigation Bar shows up.
Now the Problem is:
Cells in a tableView cannot segue more than 1 storyboard
Need Navigation Bar to Scroll in the App (which seems to need Segue)
Any solutions through this?
You are using presentViewController, that's why you are not getting the NavigationBar, it presents the view modally. Instead of that pushViewController like:
self.navigationController?.pushViewController(vc, animated: true)
Probably in your storyboard the segue type will be push, that's why you are getting the Navigation Bar.
You might be interested in the following:
presentViewController:animated:completion:
ViewController Programming Guide for iOS
You are presenting the next view controller, not pushing it. (at least in case 1) So you have a modal link and no nav controller !
You can either use .pushViewController or simply performSegue and make sure your segue type is push. I would go for the performSegue and change the identifier name in each switchcase.
And you can still pass data in the prepareForSegue
Better to call performSegue in didSelectRow. And perform actions in perpareSegue Method .
Related
I have a view controller which has a table view and in the didSelectRowAt indexPath I instantiate the destination view controller, embed it into a navigation controller and present it... here is some code
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let destVC = DetailVC(frame: self.view.frame)
//passing data to the destVC ...
destVC.modalTransitionStyle = .partialCurl
let nav = UINavigationController(rootViewController: destVC)
present(nav, animated: true, completion: nil)
}
Working like a charm so far. When the detail VC appears everything's great, the tableView works perfectly, the leftBarButtonItem dismisses the detailVC as it should, but whenever I press anywhere on the upper side of the screen the detail view somehow automatically dismisses itself, which I don't want. I'm gonna leave a link to an image to see exactly what part of the screen automatically enables the dismiss function:
http://prntscr.com/kaibxg
I've been struggling with this and I didn't find any solution. I don't know why this is happening. When I use the pushViewController function, the detail VC is all right and is not popping itself out, but the thing is I really need to use the .partialCurl transition animation. I tried to find a way to somehow use .partialCurl when pushing the VC but I didn't find anything about that. Any help, answer, question is more than welcomed and appreciated. Thank you all in advance.
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.
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.
I have two TabBarController views embedded in a NavigationController and one View presented modally with its own NavigationController:
FeedView (1) (TableViewController -> Contained within TabBarController)
VenueView (2) (CollectionViewController -> Contained within TabBarController)
SelectView (3) (ViewController -> Modally presented from VenueView)
In VenueView (2), I have the following code to bring up SelectView (3):
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let selectNavigationController = self.storyboard?.instantiateViewControllerWithIdentifier("selectNavController") as! UINavigationController
let selectVC = selectNavigationController.topViewController as! SelectViewController
selectVC.currentVenue = venueItems[indexPath.row]
presentViewController(selectNavigationController, animated: true, completion: nil)
}
This allows me to bring up SelectView(3) without the tabbarcontroller, but still have a navbar. I have a button in SelectView(3) which I would like to use to persist an object's data and segue into FeedView(1).
I've tried many methods but the closest I've got to success was to segue to FeedView(1) but add more viewControllers (evidenced by buttons in navbar) and not have it contained within the TabBarController.
What is the correct way of creating this segue? I know how to persist the data but my question is more related to the proper technique of segueing from a modal View to the first tab of a BarTab.
Pic below for reference:
You could use an unwind segue if you stayed within the same navigation stack.
However, FeedView is on a different navigation controller.
One way to do it is to have your TabbarController switch back to FeedbackView (1st tab) using:
tabBarController.selectedIndex = 0
I am looking for the best-practice method of doing the following:
I have two TabBarController views embedded in a NavigationController and one which I don't want to include as a tab but is embedded in a NavigationController:
FeedView (TableViewController)
VenueView (CollectionViewController)
SelectView (ViewController)
In VenueView (2), I have the following code to bring up SelectView (3):
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let selectVC: SelectViewController! = self.storyboard?.instantiateViewControllerWithIdentifier("selectVC") as! SelectViewController
selectVC.currentVenue = venueItems[indexPath.row]
presentViewController(selectVC, animated: true, completion: nil)
}
This segue works, however SelectView(3) doesn't have a navigation bar at the top although it is embedded in a separate NavigationController. If I hook it up to the other NavController in the IB, it adopts/becomes the third tab in the BarTabController. I don't want that.
How do I hook it up so that there is a NavBar (with a back button that will go back to either view) but no Tab?
Also, there is a button on SelectView(3). When this button is tapped I'd like it to segue to FeedView(1) - while persisting some data ie 'pushing into the feed'. What kind of segue should I use for this? I've tried many combinations and ran into some strange bugs and I find View management very confusing.
Storyboard image below for reference
Views are in order from top to bottom (1-3):
In order to do this you need to fix your problem, you need to present the navigation controller which contains SelectView but not SelectView itself as you do now.
You should set a storyboard identifier to your 3'rd navigation controller and apply this change:
let selectNavigationController = self.storyboard?.instantiateViewControllerWithIdentifier("selectNavigationController") as! UINavigationController
let selectVC = selectNavigationController.topViewController as! SelectViewController
selectVC.currentVenue = venueItems[indexPath.row]
presentViewController(selectNavigationController, animated: true, completion: nil)