How to implement the navigation in a multiple cell uitableview using swift - ios

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

Related

UISplitViewController, reuse DetailViewController (Swift)

Setup: Create a boilerplate iOS Master-Detail app in Xcode and run it. Everytime you click on a master item it re-creates the detailVC and configures it to display the new data.
Premise: Often this is what I want, but other times it does little more than change the text in a single label. Doesn't it make more sense to re-use the existing DetailVC? (at least in this case)
So what's the best way to do this?
Contemplation: When I look at the boilerplate code I see the MasterVC creates class scoped var for the detailVC.
var detailViewController: DetailViewController? = nil
It sets this value in viewDidLoad, but it then never uses it for anything. Huh? In prepare(for:sender:) we get this code
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
which creates a new instance of the DetailViewController. If you put a break point in after it's created and compare it to the class var detailViewController you can see they are different.
The UIStoryboardSegue is creating my new DetailViewController which can easily be confirmed by looking at the documentation.
You do not create segue objects directly. Instead, the storyboard
runtime creates them when it must perform a segue between two view
controllers.
I could remove the segue on row select and manually call the method on my detailVC and that's probably the easiest, but segues are so purty and visual in IB. I could could probably create a custom segue. What else could I do and is there a clear "best" way?
In case someone else has this question, here is my solution until someone comes up with something better.
Go to Interface Builder and remove the showDetail segue which goes from the master tableview to the detailVC. Then go into the MasterViewController class and remove the prepare(for:sender:) function.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let indexPath = tableView.indexPathForSelectedRow {
let object = objects[indexPath.row] as! NSDate
let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
Create a new function in Table View area (this new function is part of UITableViewDelegate)
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
detailViewController?.detailItem = object
}

Segue from Static Cells to Dynamic TableViewController

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.

Pass Data from UICollectionView in Swift 2

My problem is I would like to Pass data from my collection view to a new view (tableview- but I think it's not necessary info)
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let myVC = DetailsTableViewController()
myVC.movieTitle = movies[indexPath.row].movieTitle!
print (movies[indexPath.row].movieTitle!)
performSegueWithIdentifier("DetailSegue", sender: reuseIdentifier)
}
Print is OK, but my movieTitle: String? will NIL on the DetailsViewController.
Help!
performSegueWithIdentifier will instantiate an appropriate destination controller and present it. The DetailsTableViewController you have instantiated here as myVC is never shown. You have a couple of options for how to approach this:
If you want to use segues then trigger the segue in didSelectItemAtIndexPath and implement prepareForSegue to pass data to the destination view controller (a destinationViewController will be available on the UIStoryboardSegue passed to that method).
Alternately you could avoid using segues and present your myVC directly by pushing it onto your current navigation controller (if one exists you could call self.navigationController.pushViewController(myVC, animated:true)), presenting it as a modal, or whatever makes sense in your app.
problem solved. thanks
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "DetailSegue" {
let detailsVC = segue.destinationViewController as! DetailsTableViewController
if let cell = sender as? UICollectionViewCell, indexPath = collectionView!.indexPathForCell(cell) {
// use indexPath
detailsVC.movieTitle = movies[indexPath.row].movieTitle!
}
}
}

Passing variable (Int) to a View Control (Popover)

I have a tableView with 7 rows, each row is a different action. When a user taps on a row, a view controller is shown as a popover and a text field is displayed for them to add a note. I then want to save this note to Parse but I need to save the passedChildID with it and therefore it (passedChildID) needs to be passed from the tableviewcontroller. I have this working in other area's of my app from one tableView to another tableView but for some reason it won't work with the popover.
The following code is in SingleChildViewTableViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "childToNote" {
let popoverViewController = segue.destinationViewController
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverViewController.popoverPresentationController!.delegate = self
let noteActionViewController = segue.destinationViewController as! actionNoteViewController
if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell) {
noteActionViewController.passedChildID = passedChildID
}
}
if segue.identifier == "childToItemNeeded" {
//other stuff
}
}
This builds and runs fine, but when I tap on that particular row, I get the following error.
Could not cast value of type '<<app_name>>.SingleChildTableViewController' (0x10e1bef60) to 'UITableViewCell' (0x110bca128).
I have tried moving the let noteActionViewController... code above the let popoverViewController figuring that the former was never getting run but that didn't change anything.
The segue is being performed as followed (in case it makes any difference).
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 0 {
performSegueWithIdentifier("childToNote", sender: self)
}
}
I'm stumped because this code has worked elsewhere in my app without fail.
it looks like your problem lies in this line
indexPathForCell(sender as! UITableViewCell)
the sender in this case you are telling it to be self, which is a UIViewController
performSegueWithIdentifier("childToNote", sender: self)
You are then telling in the indexPathForCell line to cast it as a UITableViewCell but its of type SingleChildTableViewController. So change the sender to be the UITableViewCell
Maybe you can save the indexPath as a local variable and when you select a cell you change the value of the variable.

How to access segue in 'didSelectRowAtIndexPath'?

I know how to pass data using segues from prepareForSegue function, but the thing i have a TableViewCell from which there are two possible segues to two different ViewControllers (let's just say A, B as of now).
It was suggested here that it is best to connect the segues to View controller rather than the tableCell itself, which infact worked perfectly. But i want to pass information to the second View controller when the cell is clicked, So how to access segue that i connected to the Source ViewController.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showQuestionnaire"{
if let indexPath = self.tableView.indexPathForSelectedRow() {
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! QuestionnaireController
let patientQuestionnaire = patientQuestionnaires[indexPath.row] as! PatientQuestionnaire
controller.selectedQuestionnaire = patientQuestionnaire
self.performSegueWithIdentifier("showQuestionnaire", sender: self)
}
}
}
Again: This question is not regarding passing information through prepareForSegue
You should be using the didSelectRowAtIndexPath method to determine whether or not a cell was selected, and send the indexPath as the sender of the segue:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showQuestionnaire", sender: indexPath);
}
Then in your prepareForSegue method, get the indexPath from the sender parameter and use that to pass the correct row/data:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "showQuestionnaire") {
let controller = (segue.destinationViewController as! UINavigationController).topViewController as! QuestionnaireController
let row = (sender as! NSIndexPath).row; //we know that sender is an NSIndexPath here.
let patientQuestionnaire = patientQuestionnaires[row] as! PatientQuestionnaire
controller.selectedQuestionnaire = patientQuestionnaire
}
}
To explain...
I used the index path as the sender so I can easily pass the index path. You could also check for the currently selected cell using other UITableView methods, but I've always done it this way with success
You can't put performSegueWithIdentifier within the prepare for segue method, because performSegueWithIdentifier leads to prepareForSegue; You are just looping around and around with no aim. (When you want to perform a segue, prepareForSegue is always executed)
prepareForSegue doesn't run by itself when a row is selected. This is where you need didSelectRowAtIndexPath. You need a performSegueWithIdentifier outside of the method as described previously, which should be in didSelectRowAtIndexPath

Resources