I have a TableViewController that has static cells inside groups. I want to perform segue from some of the static cells, but also somehow know which cell triggered the segue in the segued TableViewController.
I ctrl + dragged from each cell to the destination TVC, added segue identifiers and created a class for TVC.
I this approach, but the segue doesn't work. It doesn't print XOX either
class MainTableViewController {
viewDidLoad() {
tableView.delegate = self
tableView.dataSource = self
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
print("XOX")
if segue.identifier == "MainToA" {
if let destination = segue.destinationViewController as? DetailTableViewController {
destination.page = "A"
}
}
if segue.identifier == "MainToB" {
if let destination = segue.destinationViewController as? DetailTableViewController {
destination.page = "B"
}
}
}
class DetailTableViewController {
var page = String()
viewDidLoad() {
print(page)
}
}
Am I missing out something or completely out of track? Why doesn't the segue work?
I tried and the segues work correctly with connection to individual UITableViewCell
You can see in the image the triggered segue part
Please make sure you have connected the segue from the cell instead of any subview inside it.
Edit
From the chat it appears you have a UITapGesture which takes up the touch of the view.
To avoid this i would suggest to removeTapGesture and use tableView didSelectRow methods to detect touches on view
Did you subclass UITableViewController? Did you assign that subclass in Interface Builder? Did you make sure NOT to implement override func numberOfSectionsInTableView(tableView: UITableView) -> Int
so that your static cells actually appear?
When you ctrl+dragged did you link up Triggered Segues by Selection, not Accessory action?
Try enabling the Selection property in your Table View Static Content
Attributes inspector, as shown in the picture here.
Related
How do you segue from a ViewController to a ViewController located in a UITableView (which is in a NavigationController)?
Image of Storyboard with description
I can Control-Drag from my "DetailButton" to the "DetailViewController", but it is not displayed in the navigation controller if you do that. The NavigationBar should still allow a user to go back to the UITableView, even if they segued here from the "DetailButton".
Here's the solution I came up with. If anyone else has a better way, please post it here.
Storyboard Steps:
Control-Drag from the "DetailButton" to the Navigation Controller to create a segue. Give this segue the identifier "detailSegue".
Control-Drag from the UITableViewCell you would like to link to DetailViewController to the DetailViewController. Give this segue the name "showDetail".
UITableViewController Steps:
var shouldSegueToDetail = false
override func viewDidLoad() {
...
// Check if we should segue to DetailViewController
if shouldSegueToDetail {
// Reset flag
shouldSegueToDetail = false
// Go to DetailViewController
performSegue(withIdentifier: "showDetail", sender: self)
}
}
OriginalViewController Steps:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSegue" {
let navigationVC = segue.destination as! UINavigationController
let tableVC = navigationVC.viewControllers[0] as! UITableViewController
tableVC.shouldSegueToDetail = true
}
}
Option:
Instead of making a segue from the UITableViewCell to the DetailViewController, you could probably call UITableView's selectRow method with the IndexPath of the item you want selected.
I've got a table view with X rows and i want that if I tap one of the rows it displays a view controller with a different value each.
I know that i have to use prepareForSegue but i don't know how.
I'll make an example:
There's the table view with 4 rows.
I tap the first row and it opens a view controller with a label that shows "1".
I tap the second row and it opens a view controller with a label that shows "2".
And so on.
I don't want to create milions of view controllers, i'd like to have only one of them that changes every time.
You'll make me a big favor.
Thanks
First you need to detect the user's selection.
This is done with the -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath function.
Once you have detected the selected row, you need to invoke the the next view controller. You can do this by instantiating the viewController you want to show using the storyboard:
UIViewController* viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Your VC identifier here"];
To pass information to another controller you can create a property and set it like any other object:
[viewController setCustomArray:#[]]
Finally you need to present the view controller:
[self presentViewController:viewController animated:YES completion:nil]
You need to add the following in your tableViewController class
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
text = indexPath.row
performSegueWithIdentifier("YourSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YourSegue" {
let yourOtherVC = segue.destinationViewController as? SecondViewController
yourOtherVC?.text = text
}
}
In your SecondViewController class, add the following:
class SecondViewController: UITableViewController {
var text:String = ""
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if text != nil {
myLabel.text = text
}
}
Hope this helps. :)
I am now implementing a tableview with 3 different cells that should be lead to 3different pages. should I implement the code in prepareforsegue or didselectrowatIndexPath?
I have already created 3 different viewController which should be linked by each of the cell and also set up the segue identifier in storyboard.
Could anyone be kind enough to including some example code and important code description?
Thank you advance for your help.
I would also choose the prepareForSegue function, but I used indexPath for the selected row instead of the identifier.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath: NSIndexPath = self.tableView.indexPathForSelectedRow!
if indexPath.row == 0 {
var DestViewController = segue.destinationViewController as! TargetViewController1
} else if indexPath.row == 1 {
var DestViewController = segue.destinationViewController as! TargetViewController2
} else {
var DestViewController = segue.destinationViewController as! TargetViewController3
}
}
It works in my application :)
Since I usually use Storyboards to create my applications flow, I setup different segues to each target viewcontroller.
Then in didSelectCell I call performSegue and the corresponding identifier.
In prepare(for:), I check for the segue identifier if necessary (for example to set the current view controller as the delegate of the destination.
If you go fully programmatically, there is no need for segues and you could just show/present the new view controller in didSelectCell
I have this:
MyTableViewController (inherits from UITableViewController)
It has a dynamic tableview with a few cells (foo, bar, qux)
MyViewController (inherits from UIViewController)
There are some "show" segues from this controller to other view controllers
It has a UIContainerView that embeds MyTableViewController
A picture speaks a thousand words:
When a certain cell is selected, I want to perform a segue of the parent view (MyViewController)
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if (indexPath.section == 1 && indexPath.row == 1) {
self.WHAT.performSegueWithIdentifier("someShowSegue1", sender: self)
}
}
Is it possible? what should I use in «WHAT»?
In the prepareForSegue: for your embedded segue set the viewController in a new property in your tableViewController, let's name it parentController. And then you'll have just to call self.parentController.performSegueWithIdentifier().
EDIT: But first of all, maybe you can use the existing parentViewController if it contains the embedding view controller.
You may want to consider using delegation to solve this problem since the child tableView doesn't seem like it should be responsible for the segue. For example:
// MyViewController
class MyViewController: UIViewController, MyTableViewControllerDelegate {
func selectedMyTableViewControllerCell(cell: UITableViewCell) {
// ... check cell type or index or whatever
self.performSegueWithIdentifier("someValueFromCellType", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == myTableViewControllerIdentifier {
if let vc = segue.destinationViewController as MyTableViewController? {
vc.delegate = self
}
}
}
}
// MyTableViewController
protocol MyTableViewControllerDelegate: class {
func selectedMyTableViewControllerCell(cell: UITableViewCell)
}
class MyTableViewController: UITableViewController {
weak var delegate: MyTableViewControllerDelegate?
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// ... get the cell
delegate?.selectedMyTableViewControllerCell(cell)
}
}
No need to create a property. Just this
self.parent?.performSegue(withIdentifier: "ID", sender: self)
SWIFT 4
Swift 4 no longer has parentViewController. You must use parent to access the parent object and is an optional so be sure to check for nil where necessary.
self.parent?.performSegue(withIdentifier: "IdentifierHere", sender: self)
Hook your segue up to the embedded table view controller's cell instead. You can use different segues per cell prototype. This saves you from checking index paths or even implementing didSelectRow at all.
Segue is defined from one view controller to another and is only invoke from the view controller in which it is defined. So you would need to store the reference of the parentViewController.
Like from MyViewController
if ([segueName isEqualToString: #"embedseg"]) {
MyTableViewController * tblViewController = (MyTableViewController *) [segue destinationViewController];
tblViewController.parentController=self; //Storing reference of parentViewController i.e MyViewController
}
Now you can simply invoke segues like
self.parentController.performSegueWithIdentifier("someShowSegue1", sender: self)
Hope this helps
Alright, so I have a TableView scene and I'm going to put a button in each of the cells and I need each cell to, when clicked, segue to its own ViewController scene. In other words, I need to know how to connect a button to a scene (The only button I have right now is "milk")
I know how to create an IBAction linked to a button, but what would I put in the IBAction?
I'm a beginner so I need a step-by-step explanation here. I've included a picture of my storyboard. I haven't written any code yet.
If you want to have a button trigger the segue transition, the easiest thing to do is Control+Click from the button to the view controller and choose a Segue option (like push). This will wire it up in IB for you.
If you want to write the code to do this yourself manually, you can do so by naming the segue (there's an identifier option which you can set once you've created it - you still need to create the segue in IB before you can do it) and then you can trigger it with this code:
V2
#IBAction func about(sender: AnyObject) {
performSegueWithIdentifier("about", sender: sender)
}
V3
#IBAction func about(_ sender: Any) {
performSegue(withIdentifier: "about", sender: sender)
}
You can use the delegation pattern. Presuming that you have implemented a custom table cell, you can define a property in its class to hold whatever you think is helpful to identify the row - it can be its index, or (my preferred way) an instance of a class which represents the data displayed in the cell (I'm calling it MyCellData.
The idea is to let the cell notify the table view controller about a tap on that button, passing relevant data about (the data displayed in) the row. The table view controller then launches a segue, and in the overridden prepareForSegue method it stores the data passed by the cell to the destination controller. This way if you have to display details data about the row, you have all the relevant info, such as the details data itself, or an identifier the destination view controller can use to retrieve the data for example from a local database or a remote service.
Define a protocol:
protocol MyCellDelegate {
func didTapMilk(data: MyCellData)
}
then declare a property named delegate in the cell class, and call its didTapMilk method from the IBAction
class MyTableCell : UITableViewCell {
var delegate: MyCellDelegate?
var data: MyCellData!
#IBAction func didTapMilk() {
if let delegate = self.delegate {
delegate.didTapMilk(self.data)
}
}
}
Next, implement the protocol in your table view controller, along with an override of prepareForSegue
extension MyTableViewController : MyCellDelegate {
func didTapMilk(data: MyCellData) {
performSegueWithIdentifier("mySegueId", sender: data)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "mySegueId" {
let vc: MyDestinationViewController = segue.destinationViewController as MyDestinationViewController
vc.data = sender as? MyCellData
}
}
}
Of course you need a data property on your destination view controller for that to work. As mentioned above, if what it does is displaying details about the row, you can embed all required data into your MyCellData class - or at least what you need to retrieve the data from any source (such as a local DB, a remote service, etc.).
Last, in cellForRowAtIndexPath, store the data in the cell and set its delegate property to self:
extension MyTableViewController : UITableViewDataSource {
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let data: MyCellData = retrieveDataForCell(indexPath.row) // Retrieve the data to pass to the cell
let cell = self.tableView.dequeueReusableCellWithIdentifier("myCellIdentifier") as MyTableCell
cell.data = data
cell.delegate = self
// ... other initializations
return cell
}
}
Use self.performSegueWithIdentifier("yourViewSegue", sender: sender) under your event for handling button's click:
#IBAction func redButtonClicked(sender: AnyObject) {
self.performSegueWithIdentifier("redView", sender: sender)
}
In the above code, redView is the segue identifier.