How to pass data From VC to Cell? - ios

i am wondering how I can pass data from a ViewController to a cell?
I am a beginner so I may oversee the obvious :P
I have a home VC and when you press on a commentButton you get to the CommentVC which holds the postId of the post.
As I want to be able to like a comment( which works perfectly) and to notice the user about his comment being liked(which does not work for now) I need to have the postId not only in the commentVC ( which holds the correct one) but also in the cell.
this is the code where I pass data from the HomeVc to the CommentVC
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "CommentSegue" {
let commentVC = segue.destination as! CommentViewController
let commentCell = CommentTableViewCell()
let postId = sender as! String
commentCell.postId = postId
commentVC.postId = postId
}
}
When I print out both variables in CommentVc and CommentCell, only the CommentVc shows the correct one whereas the Cell has "nil" as the print out statement.
Any Idea how I can pass it?

You shouldn't instantiate UITableViewCells yourself by calling your custom classes initialiser, but you should do it in your UITableViewController class (or a class that conforms to UITableViewContollerDataSource protocol).
Pass the data you want to show in your cells to your table view controller and in your data source methods (for example tableView(_:cellForRowAt:)) when creating your cell using dequeueReusableCell(withIdentifier:) assign the data to the specific cell.

You should not pass a table cell. Since you already passed the postId to your comment view controller, you can access to this id from a table view cell in your comment view controller in this way
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EditorFontCell") as! EditorFontCell
print(self.postId)
//do what you want with postId with the current cell object here
return cell
}
Now remove the cell in segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "CommentSegue" {
let commentVC = segue.destination as! CommentViewController
let postId = sender as! String
commentVC.postId = postId
}
}

Where did you perform the print out statement? Typically, it is recommended to pass any data to the cell view in cellForRow of TableView delegate method. Inside the cell class, you can have a configure(_ myId: String) method with the id as one of the parm to be passed in. Then print it inside that method.

//In cell table cell class create a variable to hold the data. Here I made postId as String variable.
class EditorFontCell: UITableViewCell{
var postId: String! // postId may be other type
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
// in this method you create an object i.e. cell, of type EditorFontCell
// and you can access that postId by this tableCell object. You can simple assign a value to as shown below.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EditorFontCell") as! EditorFontCell
cell.postId = "String data"
return cell
}

Related

How to preserve the original indexPath.row after applying filters to Table View?

My app uses "filter" buttons in which the whereField query is refined based on which filter buttons are pressed. This is an example before filtering:
But this is an example after filtering:
The issue is that when I click into one of the Rows, it takes me to the next page that corresponds to the original indexPath.row in my database belonging to that Row. How can I preserve the original indexPath.row? E.g., Cell B to always be indexPath.row = 1, even after filtering.
This is my cellForRowAt of my first View Controller.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get a cell
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
// Get the mealPlan that the tableView is asking about
let mealPlanInTable = mealPlan[indexPath.row]
// Customize the cell
cell.displayMealPlan(mealPlanInTable)
// Return the cell
return cell
}
And how I connect this View Controller's indexPath.row to the next View Controller after a cell is tapped:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Detect the indexPath the user selected
let indexPath = tableView.indexPathForSelectedRow
// Get the mealPlan the user selected
let mealPlanSelected = mealPlan[indexPath!.row]
// Get a reference to the NextiewController
let NextVC = segue.destination as! NextViewController
// Get a reference to the currentMealPlanIndex in the NextViewController
NextVC.currentMealPlanIndex = indexPath!.row
}
Any advice is much appreciated!
You are getting values from wrong array. Also it's better to pass the obj instead of index.
You need to have 2 variables - one for all data & other for filtered data.
Use filtered data var in tableview datasource & for passing to NextVC.
Considering your class name is MealPlan. Here is the source.
var allMealPlans: [MealPlan]
var filteredMealPlans: [MealPlan]
func onFilterButtonPressed() {
filteredMealPlans = allMealPlans.filter({
// return true/false here based on your filters
})
tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get a cell
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
// Get the mealPlan that the tableView is asking about
let mealPlanInTable = filteredMealPlans[indexPath.row]
// Customize the cell
cell.displayMealPlan(mealPlanInTable)
// Return the cell
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Detect the indexPath the user selected
let indexPath = tableView.indexPathForSelectedRow
// Get the mealPlan the user selected
let mealPlanSelected = filteredMealPlans[indexPath!.row]
// Get a reference to the NextiewController
let NextVC = segue.destination as! NextViewController
// Get a reference to the currentMealPlanIndex in the NextViewController
NextVC.currentMealPlan = mealPlanSelected
}
Add a variable in your NextVC for currentMealPlan
class NextVC: UIViewController {
var currentMealPlan: MealPlan?
}
Thank you all for the comments/advice! Instead of connecting the data in the view controllers through the indexPath, I used a document ID that is consistent with the data flowing between my view controllers. This works with all of my filtering.
This is in my first ViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableView.indexPathForSelectedRow {
let ingredientsVC = segue.destination as! IngredientsViewController
let documentID = mealPlan[indexPath.row].docID
ingredientsVC.currentMealPlanIndex = indexPath.row
ingredientsVC.passedDocID = documentID!
}
}
And this is in my second ViewController:
// This variable references the unique Document ID
var passedDocID = ""
// This is how I use that document ID to get a reference to the appropriate data
let selectedMealPlanIndex = mealPlan.firstIndex(where: {$0.docID == passedDocID})
let currentMealPlan = mealPlan[selectedMealPlanIndex!]

Send information to another View controller

I am having trouble getting the Indexpath of table view cell and sending it to a next page.
var bookName: String?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cells", for: indexPath) as! ProfileTableViewCell
print(posts[indexPath.row])
let post = self.posts[indexPath.row] as! [String: AnyObject]
self.bookName = post["title"] as? String
}
override public func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "sendMessageToUser", let chatVc = segue.destination as? SendMessageViewController else {
return
}
chatVc.bookName = self.bookName
}
So I am trying to capture the title of whatever cell I clicked and send it to SendMessageViewController. The issue is that it captures some titles accurately and sometimes it does not capture the titles accurately, and I am not sure why.
You need to implement the table view delegate method tableView(_:didSelectRowAt:). In that method, save the selected indexPath to an instance variable and invoke your segue.
Then in prepareForSegue, use the saved indexPath to index into your data model and fetch the data for the selected cell. (The title string, in your case.) Don't fetch the data from the cell's views. Views are not for storing data.
cellForRowAt method serves to create view, in this case a table view cell, and then provide that to table view to display. When table view loads data, you will see, say 10 cells. So this function is called 10 times to prepare the 10 cells. So in the last time, the index row will be 9 and your bookName property will be the 10th of your post array.
Now say you scroll down a bit and then scroll all the way up, the last cell getting prepared is the first cell. So now your bookName will be the first of your post array. That's why you are getting incorrect book name.
To fix your problem, you need to get the specific book name only after user clicked on a cell. So remove the code that assign values to bookName in your cellForRow method, and then add another delegate function like this
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let post = self.posts[indexPath.row] as! [String: AnyObject]
self.bookName = post["title"] as? String
}

PrepareForSegue with UITableViewController - Pass data

I am having a huge problem with my code. My question is, how can I get the data from the tableviewcell the user clicks on to a detail viewcontroller.
I am already getting the data out of an array and displaying it in my tableviewcontroller, but how can I pass that data through to a new viewcontroller using prepareForSegue?
This is my code for displaying data in the cells.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// Table view cells are reused and should be dequeued using a cell identifier.
let cellIdentifier = "MealTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MealTableViewCell
// Fetches the appropriate meal for the data source layout.
let meal = meals[indexPath.row]
cell.nameLabel.text = meal.name
cell.photoImageView.image = meal.photo
cell.descriptionLabel.text = meal.description
cell.servedWithLabel.text = meal.servedWith
return cell
}
The problem is, that calling a prepareForSegue outside of that means that I cannot use for instance meal.name to pass the name through to the detail viewcontroller? What to do?
Hope you guys can help me - I have been struggling with this for some time now :-(
As pointed out by Arun, you need to use -prepareForSegue. Here's what I'd do to pass something to your detail view controller (say meal.name)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Use this to determine the selected index path. You can also store the selected index path in a variable using -didSelectRowAtIndexPath
let cell = sender as! UITableViewCell
let indexPath = self.tableView.indexPathForCell(cell)
// Get the relevant detail
let meal = meals[indexPath.row]
// Create reference and pass it
let detailViewController = segue.destinationViewController as! MyDetailViewController
detailViewController.mealName = meal.name
}
And eventually, just hook up the UITableViewCell with MyDetailViewController using your Storyboard (and select whatever presentation and transition style you prefer)
Edited for Swift 4
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let cell = sender as! UITableViewCell
let indexPath = self.tableView.indexPath(for: cell)
// Get the relevant detail
let meal = meals[indexPath!.row]
// Create reference and pass it
let detailViewController = segue.destination as! MyDetailViewController
detailViewController.mealName = meal.name
}
When you click a cell. It will call didSelectRow. In didSelectRow save the indexPath.row and do performseguewithidentifier
Now in prepareForSegue get the object from the array using meals[selectedIndex] and assign to detailViewController Objects.
Make sure segue is from ViewController to ViewController.

Populate table view cells with data from text field in previous view

I want to populate the prototype cells in a table view with information that the user has typed into a text field in a previous view controller. I want to set the title and subtitle of each cell using this data instead of a pre-determined array. How can I pass data from the text field inside a view controller to the title/subtitle section of a table cell view?
Any help is appreciated.
First of all implement this at the point where you want to segue to your TableViewController:
let textFieldString = yourTextField.text ?? ""
performSegueWithIdentifier("exampleID", sender: textFieldString)
Now create the right segue in your StoryBoard!
1) From the whole ViewController to your destination ViewController
2) And don't forget your unique segue-ID
The delegate method prepareForSegue(...) will be called before you perform the segue. In this method you prepare your destination ViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let targetvc = segue.destinationViewController as? YourTableViewController
if let targetvc = targetvc
{
let yourTextFieldString = sender as? String
if let yourTextFieldString = yourTextFieldString
{
targetvc.yourTextFieldString = yourTextFieldString
}
}
}
When the segue to your destination ViewController is performed, your variable (here in this case "yourTextFieldString") has the previous set data.
class yourTableViewController : UITableViewController
{
var yourTextFieldString = "" // This variable has the previous set data
override func viewDidLoad()
{
super.viewDidLoad()
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier("yourCellID", forIndexPath: indexPath)
cell.textLabel.text = yourTextFieldString
return cell
}
}
Now adjust your prototype cell (don't forget the right identifier) and you're done.

Swift: Pass UITableViewCell label to new ViewController

I have a UITableView that populates Cells with data based on a JSON call. like so:
var items = ["Loading..."]
var indexValue = 0
// Here is SwiftyJSON code //
for (index, item) in enumerate(json) {
var indvItem = json[index]["Brand"]["Name"].stringValue
self.items.insert(indvItem, atIndex: indexValue)
indexValue++
}
self.tableView.reloadData()
How do I get the label of the cell when it is selected and then also pass that to another ViewController?
I have managed to get:
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
println(currentCell.textLabel.text)
}
I just cant figure out how to pass that as a variable to the next UIViewController.
Thanks
Passing data between two view controllers depends on how view controllers are linked to each other. If they are linked with segue you will need to use performSegueWithIdentifier method and override prepareForSegue method
var valueToPass:String!
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel.text
performSegueWithIdentifier("yourSegueIdentifer", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "yourSegueIdentifer") {
// initialize new view controller and cast it as your view controller
var viewController = segue.destinationViewController as AnotherViewController
// your new view controller should have property that will store passed value
viewController.passedValue = valueToPass
}
}
If your view controller are not linked with segue then you can pass values directly from your tableView function
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
let storyboard = UIStoryboard(name: "YourStoryBoardFileName", bundle: nil)
var viewController = storyboard.instantiateViewControllerWithIdentifier("viewControllerIdentifer") as AnotherViewController
viewController.passedValue = currentCell.textLabel.text
self.presentViewController(viewContoller, animated: true , completion: nil)
}
You asked:
How do I get the label of the cell when it is selected and then also pass that to another ViewController?
I might suggest rephrasing the question as follows: "How do I retrieve the data associated with the selected cell and pass it along to another view controller?"
That might sound like the same thing, but there's an important conceptual distinction here. You really don't want to retrieve the value from the cell label. Our apps employ a MVC paradigm, so when you want to pass data information from one scene to another, you want to go back to the model (the items array), not the view (the text property of the UILabel).
This is a trivial example, so this distinction is a bit academic, but as apps get more complicated, this pattern of going back to the model becomes increasingly important. The string representation from the cell is generally is a poor substitute for the actual model objects. And, as you'll see below, it's just as easy (if not easier) to retrieve the data from the model, so you should just do that.
As an aside, you don't really need a didSelectRowAtIndexPath method at all in this case. All you need is a segue from the table view cell to the destination scene, give that segue a unique identifier (Details in my example), and then implement prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DetailsViewController {
let selectedRow = tableView.indexPathForSelectedRow!.row
destination.selectedValue = items[selectedRow]
}
}
Alternatively, if your segue is between the cell and destination scene, you can also use the sender of the prepare(for:sender:):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DetailsViewController {
let cell = sender as! UITableViewCell
let selectedRow = tableView.indexPath(for: cell)!.row
destination.selectedValue = items[selectedRow]
}
}
But the idea is the same. Identify what row was selected, and retrieve the information from the model, the items array.
The above is Swift 3. For Swift 2.3, please see the previous version of this answer.
Okay..Its been 2 days I was searching for the answer that how could I be able to save the selected UITableViewCell label text data and display that data to an another label on an another View Controller which will come out after tapping on a cell. At last I have completed with the task and its successful. Here is the complete code with steps using Swift.I am using Xcode 6.4.
Step 1.
I have Two class assigned to the storyboard view controllers named "iOSTableViewControllerClass.swift" which is a Table View Controller and "iOSTutorialsViewControllerClass.swift" which is a normal View Controller.
Step 2.
Now make segue from iOSTableViewControllerClass to iOSTutorialsViewControllerClass by Control-dragging on the storyboard area and choose "show" from drop down menu. Click on this highlighted button according to the below image and perform the segue.
Step 3.
Now select the segue by clicking on the storyboard and give it an identifier on the Attributes Inspector. In this case I named it as "iOSTutorials"
Step 4.
Now on this step put a label on your cell as well as on the other view controller and make outlets of them on their corresponding classes.
In my case those are "#IBOutlet weak var iOSCellLbl: UILabel!" and " #IBOutlet weak var iOSTutsClassLbl: UILabel!".
Step 5.
Make a string type variable on the first Table View Controller Class. I did this as "var sendSelectedData = NSString()" also Make a string type variable on the second class. I did this as "var SecondArray:String!".
Step 6.
Now we are ready to go.
Here is the complete Code for first Class --
// iOSTableViewControllerClass.swift
import UIKit
class iOSTableViewControllerClass: UITableViewController, UITableViewDataSource,UITableViewDelegate {
// Creating A variable to save the text from the selected label and send it to the next view controller
var sendSelectedData = NSString()
//This is the outlet of the label but in my case I am using a fully customized cell so it is actually declared on a different class
#IBOutlet weak var iOSCellLbl: UILabel!
//Array for data to display on the Table View
var iOSTableData = ["Label", "Button", "Text Field", "Slider", "Switch"];
override func viewDidLoad() {
super.viewDidLoad()
//Setting the delegate and datasource of the table view
tableView.delegate = self
tableView.dataSource = self
//Registering the class here
tableView.registerClass(CustomTableViewCellClassiOS.self, forCellReuseIdentifier: "CellIDiOS")
//If your using a custom designed Cell then use this commented line to register the nib.
//tableView.registerNib(UINib(nibName: "CellForiOS", bundle: nil), forCellReuseIdentifier: "CellIDiOS")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return iOSTableData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let CellIDentifier = "CellIDiOS"
//In this case I have custom designed cells so here "CustomTableViewCellClassiOS" is the class name of the cell
var cell:CustomTableViewCellClassiOS! = tableView.dequeueReusableCellWithIdentifier(CellIDentifier, forIndexPath: indexPath) as? CustomTableViewCellClassiOS
if cell == nil{
tableView.registerNib(UINib(nibName: "CellForiOS", bundle: nil), forCellReuseIdentifier: CellIDentifier)
cell = tableView.dequeueReusableCellWithIdentifier(CellIDentifier) as? CustomTableViewCellClassiOS
}
//Here we are displaying the data to the cell label
cell.iOSCellLbl?.text = iOSTableData[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
// Get Cell Label text here and storing it to the variable
let indexPathVal: NSIndexPath = tableView.indexPathForSelectedRow()!
println("\(indexPathVal)")
let currentCell = tableView.cellForRowAtIndexPath(indexPathVal) as! CustomTableViewCellClassiOS!;
println("\(currentCell)")
println("\(currentCell.iOSCellLbl?.text!)")
//Storing the data to a string from the selected cell
sendSelectedData = currentCell.iOSCellLbl.text!
println(sendSelectedData)
//Now here I am performing the segue action after cell selection to the other view controller by using the segue Identifier Name
self.performSegueWithIdentifier("iOSTutorials", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Here i am checking the Segue and Saving the data to an array on the next view Controller also sending it to the next view COntroller
if segue.identifier == "iOSTutorials"{
//Creating an object of the second View controller
let controller = segue.destinationViewController as! iOSTutorialsViewControllerClass
//Sending the data here
controller.SecondArray = sendSelectedData as! String
}
Here is the complete code for the second Class..--
// iOSTutorialsViewControllerClass.swift
import UIKit
class iOSTutorialsViewControllerClass: UIViewController {
//Creating the Outlet for the Second Label on the Second View Controller Class
#IBOutlet weak var iOSTutsClassLbl: UILabel!
//Creating an array which will get the value from the first Table View Controller Class
var SecondArray:String!
override func viewDidLoad() {
super.viewDidLoad()
//Simply giving the value of the array to the newly created label's text on the second view controller
iOSTutsClassLbl.text = SecondArray
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I do it like this.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedName = nameArray[indexPath.row]
let newView: nextViewName = self.storyboard?.instantiateViewController(withIdentifier: "nextViewName") as! nextViewName
newView.label.text = selectedValue
self.present(newView, animated: true, completion: nil)
}

Resources