Send information to another View controller - ios

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
}

Related

Passing a Struct Array through a TableViewCell to another ViewController

I'm new to swift and I've been stuck on this for a while now I'm trying to pass a Struct Array from a tableview cell to another view
If you want to share specific cell's data into table of other view controller, then you need to create an object of Model struct rather than its array just like below:
var newFeed: Model?
Next you can assign it value of particular cell before navigation in main did select method as below:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let vc = storyboard?.instantiateViewController(withIdentifier: "Comment") as? Comment else {return}
vc.newFeed = getInfo[indexPath.row]
navigationController?.pushViewController(vc, animated: true)
}
It will assign that cell's data to newFeed Variable and relaod table with that data.
For any question, feel free to ask.

How to pass data From VC to Cell?

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
}

Master Detail View With Segue

I'm Trying to learn how to do a detail view for my project .
I have a simple tableView with a simple Array data to fill it.
The Table View :
TableView Example
I designed a detail View as well, with static tableViewCells
Detail View example :
Example
I'v Connected both with a segue :
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("Profile", sender: indexPath);
}
I also connected all the labels and images with Outlets i want to change between each cell but i don't how to advance from here.
right now every cell shows the same thing but i want to change the data between rows . So i would like to change the data through the segue and create a master detail application like in my tableview. Can anybody help me ?
Am using Swift 2.3 and Xcode 8.1
If I understand your question correctly, you just want to pass dataSource element to the next viewController. So you can just pick it using indexPath.row and use sender parameter to set it in prepareForSegue method.
The code below assumes your dataSource is self.users array.
Swift 3
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let user = self.users[indexPath.row]
self.performSegueWithIdentifier("Profile", sender: user)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let segueId = segue.identifier? else { return }
if segueId == "Profile" {
guard let profileVC = segue.destination as? ProfileViewController else { return }
profileVC.user = sender as? User
}
}
Swift 2
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let user = self.users[indexPath.row]
self.performSegueWithIdentifier("Profile", sender: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let segueId = segue.identifier else { return }
if segueId == "Profile" {
guard let profileVC = segue.destinationViewController as? ProfileViewController else { return }
profileVC.user = sender as? User
}
}
Edit
im trying to change data like al the labels you saw between rows like
for example shalvata will have a different data from light house and
so , change the labels and images and so on
It is still unclear for me what data you want to change exactly. Also I don't understand the language on your screenshots, but since you name the relationship as master-detail, I suppose the second screen is meant to show more info about the entity selected on the first screen.
If so, you should start from designing you model so that it contains all those fields you need on the second screen. Judging by the icons it would be something like
struct Person {
var name: String?
var image: UIImage?
var age: Int?
var address: String?
var phone: String?
var schedule: String?
var music: String?
var smoking: Bool?
var car: String?
var info: String?
var hobby: String?
}
Note: Remove ? for those fields which aren't optionals, i.e. always must be set for every entity (perhaps name field)
Usage
I don't known how and when you create your Person array, but basically there are two approaches:
Use a list of entities with all fields filled on MasterVC and just pass the selected person to the DetailVC in didSelectRowAtIndexPath
Use a list of entities with some basic data (name, address, image) required for MasterVC and fill the rest of the fields only when required (didSelectRowAtIndexPath method)
In any case you'll get selected person in DetailVC and now everything you need is to use that data in cellForRow method, just as you did on MasterVC. Perhaps it would be a better option to use static TableViewController for Details screen.
Sounds like what you're trying to do does not involve segues at all. You can change data of cells using the cellForRow method in your tableViewController.
https://developer.apple.com/reference/uikit/uitableview/1614983-cellforrow
For example
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "foo"
return cell
}
If that sounds confusing to you then you should take a step back and do some tutorials then post specific questions on SO.

ios Swift: Passing data from tableView to view controller

enter image description herewell this is my first time posting a question so not to sure how it works. I am doing an assignment on swift. I am stuck at a point where I need to pass the data from tableview to viewcontroller. On my first ViewController, I have a list of data (Categories) coming from the database table and when the user clicks on any of the cell, it should go to the next viewcontroller the label becomes the heading. Please find my screen shot attached.
Thanks
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return values.count;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CategoryList_TableViewCell
let maindata = values[indexPath.row]
cell.categoryLabel.text = maindata["NAME"] as? String
return cell;
}
I tried using didSelectRowAtIndexpath and perparesegue functions but not getting my head around.
can anyone can guide me.
Thanks a million in advance :)
You don't need to implement didSelectRow just to pass data through a segue. Assuming you're using Storyboards.
CTRL drag from the prototype TableViewCell in your storyboard to the ViewController you want to pass data to.
Choose Show for the segue type and give it an identifier
Do this in prepareForSegue:
if segue.identifier == "catView" {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destinationViewController as! YourViewController
let value = values[indexPath.row]
controller.catTitleRec = value["NAME"] as! String
}
Do this in your didSelectRowAtIndexPath:
let maindata = values[indexPath.row]
self.performSegueWithIdentifier("yourIdentifier", sender: mainData)
Then, in your prepareForSegue function do this:
let destinationVC = segue.destinationViewcontroller as! YourCustomViewController
destinationVC.yourObject = sender as! YourObject

How do I segue from a tableviewcell and pass different data from each cell to the next tableview?

I'm trying to practice making list apps with models by making a class to represent each list item. I have a Category class which contains three properties - two strings and one array of strings. Here is the class:
class Category {
var name: String
var emoji: String
var topics: [String]
// (the getCategories method listed below goes here) //
init(name: String, emoji: String, topics: [String]) {
self.name = name
self.emoji = emoji
self.topics = topics
}
In my Category class I have a method to assign values to the categories so I can keep them out of the view controller. This method is listed below:
class func getCategories() -> [Category]
{
let categories =
[Category(name:"cat", emoji:"😸", topics:["paws","tails", "fur", "pussyfoot","purr", "kitten", "meow"]),
Category(name: "car", emoji: "🚗", topics: ["motor", "speed", "shift", "wheel", "tire"])
]
return categories
}
I have two UITableViewControllers - CategoryTableViewController and TopicsTableViewController; I want the user to be able to tap a category cell in the CategoryTableViewController and then be taken to the TopicsTableViewController where the topics for the category they selected are displayed in a tableview.
So far I am able to get the cell to segue to the TopicsTableViewController but it displays the same topics no matter which category I select. Here is how I have my didSelectRowAtIndexPath and prepareForSegue set up in the CategoriesTableViewController...
override func tableView(tableView: UITableView,didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "fromCategorySegue") {
let vc = segue.destinationViewController as! TopicsTableViewController
vc.categories = categories
}
}
It displays the first category (cat) topics on the TopicsTableViewController even if I select the second category (car).
In case it is helpful here is a snippet of some of my code in the TopicsTableViewController...
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let topic = categories[indexPath.section].topics[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("topicCell",forIndexPath: indexPath)
cell.textLabel?.text = topic
return cell
}
I also have categories defined at the top of TopicsTableViewController as well so I could get the correct row count based on the topics count...
var categories = Category.getCategories()
I think I'm missing something in my didSelectRowAtIndexPath or in my prepareForSegue. I think the fact that my topics are an array that is returned within an array of Category from the getCategories() function is screwing me up somehow.
Note:
My segue between the CategoryTableViewController and the TopicsTableViewController was created on the storyboard by ctrl + dragging from the cell in CategoryTableViewController to the TopicsTableViewController.
Any help is greatly appreciate!
Thanks :)
This is difficult to answer without seeing the full view controllers. From viewing the code you have posted it seems that there is no relationship between the selected cell and the prepare for segue method. For example do you actually use the variable you create in the didSelectCell method? Looks like you didn't. In prepare for segue you just show the same thing over and over so the result is pretty obvious to be honest.
You need to store the index for the selected cell. Then show the corresponding data from your array using that index. Something like the below may work. Need to create a variable at class level called indexForCatergoryToShow.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
self.indexForCatergoryToShow = indexPath.row
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "fromCategorySegue")
{
let vc = segue.destinationViewController as! TopicsTableViewController
vc.categories = categories[indexForCatergoryToShow]
}
}
In your cell for row at indexPath:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.tag = indexPath.row
return cell
}
In your prepare for segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "fromCategorySegue") {
if let cell = sender as? UITableViewCell {
let row = cell.tag
// pass data to segue.destination
}
}
}
So you can know from which cell you are selecting.

Resources