I am writing a Xcode program in Swift. I have a tableview controller with some labels and an image per cell. They have their data from a first view controller. So far so good. Now i would like the user to tap a cell which opens a new controller which contains the same label and image data as the cell. With the following code the new controller opens, nonetheless I don't no how to transfer the data. If someone could help me I would be so so grateful.
Here are the codes that i tend to use:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "TableView"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! TableView
let picture = pic[indexPath.row]
cell.label1.text = picture.name1
cell.photoImage.image = picture.photo
cell.label2.text = picture.name2
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let cell = indexPath.row
self.performSegueWithIdentifier("segue", sender: cell)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue"{
var row = tableView.indexPathForSelectedRow
let viewController = segue.destinationViewController as! vc2
}
}
PS:I have a segue from the image to the new controller with the identifier "segue".
PPS: Of course i have tried the following method: Send data from TableView to DetailView Swift but when i run my program I get an error with the information that my labels were unexpectedly found nil
You should not need to override didSelectRowAtIndexPath or call performSegueWithIdentifier to do this. Connect your segue in the IB file dragging from a table view controller's cell to the second controller. You should then pass the data to the controller in prepareForSegue, in the segue.destinationViewController. You set the public properties on that destination controller.
Also make sure your labels in your prototype cell have been connected in IB. If they are they, should not be nil. Set a breakpoint on the cellForRowAtIndexPath to verify this.
Unless you are talking about the labels in your destination controller. These also need to be hooked up in IB. You would then set them in prepareForSegue. You would get the appropriate data from the
let viewController = segue.destinationViewController as! vc2
var path = tableView.indexPathForSelectedRow
viewController.destLabel.text = arrayData[path.row].myData // or whatever data you have from your model.
Related
How i can transfer data from cell of tableView to cell of another tableView. I am confused about it. I can try delegate method,closure but they doesn't work on it. I want to show data of cell to cell of another tableView by single click on button. So please help me.
I try closure and delegate method to transfer data .but it doesn't work.Actually i am confused in tableView didSelectRowat function. It deal with cell on which i tapped and next step is when i tap on button this cell data shown on another cell which has different tableView & different viewController.
Assuming first tableviewcontroller is called "ItemsViewController"
and your second view controller is called "DetailViewController"
in your ItemsViewController add this to your didSelectRowAt delegate function:
then you can send that cells information to the next view according to it's IndexPath
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "DetailViewController") as? DetailViewController {
vc.image = UIImage(named: images[indexPath.row])
vc.name = names[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
}
}
here is my code.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRow(at: indexPath!) as! monthTableViewCell!;
valueToPass = (currentCell?.monthOutlet.text)!
print(valueToPass)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "toMonthVC") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destination as! monthCellViewController
// your new view controller should have property that will store passed value
viewController.dataFromHoursVC = valueToPass
}
}
So basically I am trying to pass a value from one VC to another. the didSelectRow is working perfectly how expected. However, the prepare function is running late. For example, the first time the code is run, the second vc sees the passed value as nil. But when i go back and then do it again, it says the passed value, but the value is the one that was done before. So simply put it is acting like the prepare function is behind or being called late.
How did you set up your segue? It sounds like the value isn't being set before the segue is performed.
Try doing it this way:
After you give your segue an identification name set up your code like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow;
let currentCell = tableView.cellForRow(at: indexPath!) as! monthTableViewCell!;
valueToPass = (currentCell?.monthOutlet.text)!
print(valueToPass)
// invoke the segue manually after value is set
self.performSegue(withIdentifier: "SEGUEID", sender: self)
}
Quick Warning
You really should be careful using force unwraps optionalVar! and force downcasts thing = otherThing as! Type. It's far better to always use if-let and guard-let statements or fail-able down casts as?. Using these will decrease the chances of developing a hard to find nil value bug.
This could happen if you set your tableview's data source and/or delegate to you view controller in interface builder. Depending on the rest of your code, initialization of the view controller may need to access the tableview's delegate or dataSource during initialization (i.e. before the controller is passed to the prepareForSegue function). When it is set in IB, the delegate (or dataSource) allow the functions to be called.
I have a UITableviewController where I take a JSON file and break it down into various objects for display on my tableview (this works fine).
I've also created variables to store certain information from the cells generated in the tableview to be passed on to a detailViewController (text and images).
Using the 'prepare for segue' method I am able to pass the information I need to the detailViewController. However, I keep running into an issue where the data displayed in the detailViewController isn't the data of the cell selected but rather the data of what appears to the the last loaded cell on the table.
I am not using the didSelectRowAtIndexPath because it doesn't seem to do much better but create an additional segue screen (automatically).
In summary, my TableViewController displays about 4 cells at a time on the screen. regardless of which cell I select, the information passed to the DetailViewController is always the information on the fourth cell displayed on the screen.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Post Cell", forIndexPath: indexPath) as! TableViewCell
var dict = jsonArr[indexPath.row]
cell.postTitle.text = dict["data"]!["post_title"] as? String
cell.postTag.text = dict["data"]!["post_tags"] as? String
cell.postAddress.text = dict["data"]!["post_address"] as? String
titleToPass = cell.postTitle.text
postTagToPass = cell.postTag.text
addressToPass = cell.postAddress.text
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "ListToDetail") {
let viewController = segue.destinationViewController as! DetailPostViewController
viewController.passedTitle = titleToPass
viewController.passedPostTags = postTagToPass
viewController.passedAddress = addressToPass
}
}
cellForRowAtIndexPath gets called for every cell, so it makes sense that your variables will always have values matching the last cell to be updated.
Instead of setting the data you want to pass in that method, call indexPathForSelectedRow: in prepareForSegue and use the row of the index path to get the right things from jsonArr.
I'm trying since two weeks with the help of many Tutorials to get and segue data from a TableView cell. But it seems it is impossible to do that in XCode. It is only possible to get the selectedRow of a cell, but I cannot read out the Text labels of a selected cell.
If a user selects a Cell I want to segue the value of a label in the selected cell to a new View Controller.
I can only submit the selected row to a new View Controller but not the value of a label in this selected cell.
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
row = indexPath.row // here is the row the user has selcted
labelStringOfselectedCell = "??????" // how to retrieve data from a label in the cell?
return indexPath
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestViewController : ViewControllerDetail = segue.destinationViewController as! ViewControllerDetail
DestViewController.seguedData = labelStringOfselectedCell
}
If you really wanted to do something like that, you can do something like:
func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
let cell = tableView.cellForRowAtIndexPath(indexPath) // do not confuse this with the similarly named `tableView:cellForRowAtIndexPath:` method that you've implemented
let string = cell?.textLabel?.text
return indexPath
}
Clearly it depends upon whether you're custom cell subclass and what the label was, but that illustrates the basic idea.
Having said that, you should not do this. Your app should be following Model-View-Controller (MVC) programming paradigm. When determining what data to pass to the next scene, you should go back to the original model you used when originally populating the table, not referring to some control in the table.
For example, let's imagine that you populated the original cell by retrieving data from the objects model:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let object = objects[indexPath.row]
cell.textLabel?.text = object.name
return cell
}
Then you would just implement a prepareForSegue that retrieves the data from objects also (and you don't have to implement willSelectRowAtIndexPath at all):
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? ViewControllerDetail {
let object = objects[tableView.indexPathForSelectedRow!.row]
destination.seguedData = object.name
}
}
Clearly, this will change depending upon what your model was and how you originally populated the cell, but hopefully it illustrates the basic idea.
Update, see my comment below, as what I did wrong. Thought someone may benefit from all these newbie mistakes:) : I have a collectionView with section titles and cell titles (UILabels), which is dynamically fed in via cloudkit. I was able to get the code to finally work to select a cell and then send the cell's title to the 2nd ViewController's navigationItem.title property.
However, now the 2nd ViewController is reloading after it appears the first time. I embedded the first CollectionViewController within a navigation controller. And I created a push segue in storyboard from my prototype cell within the CollectionView to the 2nd ViewController, and provided an identifier for the segue. Any idea why it's reloading the 2nd ViewController again after appearing the first time?
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("selected", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "selected" {
let indexPaths : NSArray = self.collectionView!.indexPathsForSelectedItems()
let indexPath : NSIndexPath = indexPaths[0] as NSIndexPath
let theSelectedItem = sections[indexPath.section].category[indexPath.item]
let svc = segue.destinationViewController as TableViewControllerNew
svc.navigationItem.title = theSelectedItem
// I created the tableview controller in the storyboard, and then subclassed the UITableViewController, and set the storyboard tableview controller's class to the subclass in the identity inspector
}
Set the self.navigationItem.title = ... in the didSelect... method and then reset it to the old title in the viewWillAppear method of your first VC. This will save you from having to create a global variable in your first VC or having to pass the title as a property to your second VC, although both solutions should work as well.