How to pass data from UITableViewCell to ViewController? - ios

I have followed multiple tutorials, and have tried many answers on here, but I cannot seem to figure out what the problem is.
Here is my code:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
println("You selected cell #\(indexPath.row)!")
let indexPath = homeTimelineTableView.indexPathForSelectedRow()
let currentCell = homeTimelineTableView.cellForRowAtIndexPath(indexPath!) as! ProductTableViewCell
productTitle = currentCell.productCellTitleLabel!.text
println("The product title is \(productTitle)")
self.performSegueWithIdentifier("timelineDetail", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var titleLables: String!
if (segue.identifier == "timelineDetail") {
println("it works thus far!")
let viewController = segue.destinationViewController as! ProductDetailViewController
let selectedRow = homeTimelineTableView.indexPathForSelectedRow()!.row
viewController.titleLabel?.text = productTitle
}else {
println("wtf is wrong with your code?!")
}
}
I do not receive any errors. However, when I go to run my app, my product title still refuses to pass to the viewcontroller.
Any suggestions? Thanks.

At this stage viewController.titleLabel is nil (you are using ?, that's why there is no error)
The correct approach should be:
add var productTitle: String! in ProductDetailViewController
pass productTitle not to UILabel, but to viewController.productTitle
in viewDidLoad of ProductDetailViewController set viewController.titleLabel?.text = self.productTitle

The productTitle variable must be assigned from the currently selected cell in the prepareForSegue method.
Check the code below.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var titleLables: String!
if (segue.identifier == "timelineDetail") {
println("it works thus far!")
let viewController = segue.destinationViewController as! ProductDetailViewController
let selectedRow = homeTimelineTableView.indexPathForSelectedRow()!.row
let currentCell = homeTimelineTableView.cellForRowAtIndexPath(indexPath!) as! ProductTableViewCell
productTitle = currentCell.productCellTitleLabel!.text
viewController.titleLabel?.text = productTitle
} else {
println("wtf is wrong with your code?!")
}
}

The problem is most lilely in these two lines:
let indexPath = homeTimelineTableView.indexPathForSelectedRow()
let currentCell = homeTimelineTableView.cellForRowAtIndexPath(indexPath!) as! ProductTableViewCell
First, func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) provides already the selected indexPath. There is no need for let indexPath = ....
Second, don't load the cell's value (which returns nil, if the cell is visible and tableView.visibleCells() brings other issues), but use your data source to get the item at the selected indexPath.
In total it might look like:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// `indexPath` comes from the function parameters
let item = model.itemAtIndexPath(indexPath)
// the 'model'-part should be the same like in `cellForRow:indexPath`
// your mission is only to get the item at the path, not the cell
productTitle = item.title
self.performSegueWithIdentifier("timelineDetail", sender: self)
}

Related

TableViewCell to ViewController - Swift

I tried many ways but still can't go to another ViewController. This is my storyboard.
and This is my code.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HistoryMainTableViewCell {
listItems.sort() { $0.seq > $1.seq }
let history = listItems[indexPath.row]
cell.setValue(history: history)
return cell
} else {
return HistoryMainTableViewCell()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
if let productHistoryPage = segue.destination as? HistoryProductViewController {
if let IndexPath = tableViewHistory.indexPathForSelectedRow {
let historyArray = listItems[IndexPath.row]
productHistoryPage.history = historyArray
}
}
}
}
Try this code:(Untested Code)
Note: Below code will trigger prepare segue. When, tableViewCell selected.Let me know the code works...
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
// let indexPath = myTableView!.indexPathForSelectedRow
// let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
//sendSelectedData = (currentCell.textLabel?.text)! as String as NSString
performSegue(withIdentifier: "vcProductHistory", sender: self)
}
Based on your answer to my initial comment: dragging the line from initial vc to destination vc tells your vc about the segue, preparing for segue tells your vc what do when the segue is triggered, but you still need to let your vc know when it should perform the segue. try this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HistoryMainTableViewCell else { return HistoryMainTableViewCell() }
let history = listItems[indexPath.row]
cell.setValue(history: history)
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
guard let productHistoryPage = segue.destination as? HistoryProductViewController, let indexPath = sender as? IndexPath else { return }
let historyArray = listItems[indexPath.row]
productHistoryPage.history = historyArray
}
}
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
performSegue(withIdentifier: "vcProductHistory", sender: indexPath)
}
I think this is a little bit neater using the guard syntax. Also, it uses sender as the indexPath, not using tableView.indexPathForSelectedRow.
Also, I noticed you have this in your cellForRowAtIndexPath:
listItems.sort() { $0.seq > $1.seq }
Your sorting your data source in cellForRowAtIndexPath. You can't do that there because that function gets called for each row individually. You need to do the sorting before that is called, somewhere like viewWillAppear. Also, to sort an array in place you need to remove the () after sort. try this.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
listItems.sort { $0.0.seq > $0.1.seq }
tableView.reloadData()
}
I think you should connect to UINavigationController , not to HistoryProductionViewController
Because your MainViewController's segue connected to Navigation Controller
I guess in func prepareForSegue segue.identifier == "vcProductHistory" it works but, if let productHistoryPage = segue.destination as? HistoryProductViewController not works
because your destination ViewController's class is NavigationController
you should call
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
if let naviVC = segue.destination as? UINavigationController {
if let productHistoryPage = naviVC.topViewController as? HistoryProductViewController {
if let IndexPath = tableViewHistory.indexPathForSelectedRow {
let historyArray = listItems[IndexPath.row]
productHistoryPage.history = historyArray
}
}
}
}
}
Your solution - as you described it - should work.
The most work here can be done directly in the Storyboard.
Select the prototype cell in the TableView, drag the Segue to the NavigationController and select presentModally.
This should work without further magic.
When selecting the TableViewCell the NavigationController should be presented. There is also no code needed at this point (except for the population of the TableView).
If not - you maybe misconfigured your TableView by setting the selection to No Selection:
or you set User Interaction Enabled to false somewhere.

Segue from CollectionViewCell to Another View Controller Passing IndexPath

I'm trying to pass the indexPath of a cell tapped in a UICollectionView to another view controller. I can't seem to get the indexPath of what was selected and segue it to the next view controller
I get this error: "Could not cast value of type Post to PostCell"
View Controller #1:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let post = posts[indexPath.row]
if let cell = collectionView.dequeueReusableCellWithReuseIdentifier("PostCell", forIndexPath: indexPath) as? PostCell {
cell.configureCell(post)
}
return cell
}
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let selectedPost: Post!
selectedPost = posts[indexPath.row]
performSegueWithIdentifier("PostDetailVC", sender: selectedPost)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "PostDetailVC" {
//Error here: Could not cast value of Post to PostCell
if let selectedIndex = self.collection.indexPathForCell(sender as! PostCell){
print(selectedIndex)
}
if let detailsVC = segue.destinationViewController as? PostDetailVC {
if let selectedPost = sender as? Post {
print(selectedIndex)
detailsVC.post = selectedPost
detailsVC.myId = self.myId!
detailsVC.indexNum = selectedIndex
}
}
}
}
View Controller #2:
var indexNum: NSIndexPath!
override func viewDidLoad() {
super.viewDidLoad()
print(indexNum)
}
You are passing a Post instance which doesn't match the expected PostCell instance.
I recommend to pass the index path
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("PostDetailVC", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "PostDetailVC" {
guard let selectedIndexPath = sender as? NSIndexPath,
detailsVC = segue.destinationViewController as? PostDetailVC else { return }
print(selectedIndexPath)
let selectedPost = posts[selectedIndexPath.row]
detailsVC.post = selectedPost
detailsVC.myId = self.myId!
detailsVC.indexNum = selectedIndexPath
}
}
You are passing a Post and not a PostCell as sender. You don't need that anyway, as the collectionView keeps track of selected items.
Try this:
if let selectedIndex = self.collection.indexPathsForSelectedItems()?.first {
print(selectedIndex)
}
It's because you are giving argument which has type of Post and you are trying to cast it as PostCell. Which will not work.

Swift 2.1 - How to pass index path row of collectionView cell to segue

From the main controller that I have integrated collection view, I want to pass selected cell index path to another view controller (detail view)
so I can use it for updating a specific record.
I have the following working prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "RecipeDetailVC" {
let detailVC = segue.destinationViewController as? RecipeDetailVC
if let recipeCell = sender as? Recipe {
detailVC!.recipe = recipeCell
}
}
}
And I've tried including let indexPath = collection.indexPathForCell(sender as! UICollectionViewCell) but I get Could not cast value of type 'xxx.Recipe' (0x7fae7580c950) to 'UICollectionViewCell' at runtime.
I also have performSegueWithIdentifier("RecipeDetailVC", sender: recipeCell) and I wonder if I can use this to pass the selected cell's index path but not sure I can add this index to the sender.
I am not clear about the hierarchy of your collectionViewCell. But I think the sender maybe not a cell. Try to use
let indexPath = collection.indexPathForCell(sender.superView as! UICollectionViewCell)
or
let indexPath = collection.indexPathForCell(sender.superView!.superView as! UICollectionViewCell)
That may work.
I've wrote up a quick example to show you, it uses a tableView but the concept is the same:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var things = [1,2,3,4,5,6,7,8] // These can be anything...
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let objectForCell = self.things[indexPath.row] // Regular stuff
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let objectAtIndex = self.things[indexPath.row]
let indexOfObject = indexPath.row
self.performSegueWithIdentifier("next", sender: indexOfObject)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "next" {
// On this View Controller make an Index property, like var index
let nextVC = segue.destinationViewController as! UIViewController
nextVC.index = sender as! Int
}
}
}
Here you can see you get the actual object itself and use it as the sender in the perform segue method. You can access it in prepareForSegue and assign it directly to a property on the destination view controller.

Why is this value not passing between my ViewControllers?

I'm coding a multi-step sign-up flow, but cannot get the cell labels to pass between each viewcontroller.
Here is the code from the first viewController:
class FirstVC: UIViewController, UITableViewDelegate {
#IBOutlet weak var tableview: UITableView!
var userGoalOptions = ["Lose Fat","Gain Muscle", "Be Awesome"]
var selectedGoal: String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.title = "What Is Your Goal?"
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell1")! as UITableViewCell
cell.textLabel?.text = userGoalOptions[indexPath.row]
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userGoalOptions.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
selectedGoal = currentCell.textLabel!.text!
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "secondSeque") {
let vc = segue.destinationViewController as! SecondVC
vc.userGoal = selectedGoal
}
}
The segue (secondSegue) is connected to the table view cell in Interface Builder
In my destination viewcontroller (vc) I have an empty userGoal variable, but when I try to print the contents it returns nothing.
I know variations of this question have been asked numerous times, but I can't seem to find (or maybe understand) what I'm messing up.
Assuming the segue is connected to the table view cell in Interface Builder the cell is passed as the sender parameter in prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "secondSeque") {
let selectedIndexPath = tableView.indexPathForCell(sender as! UITableViewCell)!
let vc = segue.destinationViewController as! SecondVC
vc.userGoal = userGoalOptions[selectedIndexPath.row]
}
In this case didSelectRowAtIndexPath is not needed and can be deleted.
Apart from that it is always the better way to retrieve the data from the model (userGoalOptions) than from the view (the table view cell) for example
Do
let indexPath = tableView.indexPathForSelectedRow
selectedGoal = userGoalOptions[indexPath.row]
Do not
let indexPath = tableView.indexPathForSelectedRow
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
selectedGoal = currentCell.textLabel!.text!
The prepareForSegue Should not be part of the table view function, you have copied it into a place where it is never called. Move it out and place a break point on it to see that it is being called.

How to Segue from Custom TableViewCell to Another ViewController (Swift)

I am attempting to navigate from a TableViewCell to a detail VC indicating details about the current business that is on the cell. Here are the stub tableiewMethods:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("YelpTableBusinessCell", forIndexPath: indexPath) as! YelpTableBusinessCell
var business:Business = Business(dictionary: businessArray[indexPath.row] as! NSDictionary)
cell.business = business
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
businessToUseTVC = Business(dictionary: businessArray[indexPath.row] as! NSDictionary)
performSegueWithIdentifier("businessToDetailVC", sender: view);
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let cell = sender as? YelpTableBusinessCell {
if segue.identifier == "businessToDetailVC" {
if let ivc = segue.destinationViewController as? AttractionsDetailViewController {
ivc.businessToUse = self.businessToUseTVC
}
}
}
}
I set two breakpoints, one at the prepareForSegue method and one at the didSelectRowAtIndexPath method
I initialize businessToUseVTC variable in didSelectRowAtIndexPath, but when I run the app, when I am at the AttractionsDetailVC, which contains this bit of code,
func loadYelpInfo() {
businessName.text = businessToUse.name
let thumbImageViewData = NSData(contentsOfURL: businessToUse.imageURL!)
thumbImage.image = UIImage(data: thumbImageViewData!)
let reviewImageData = NSData(contentsOfURL: businessToUse.ratingImageURL!)
reviewImage.image = UIImage(data: reviewImageData!)
categories.text = businessToUse.categories
reviews.text = "\(businessToUse.reviewCount!) Reviews"
distanceLabel.text = businessToUse.distance
}
the code breaks, saying that businessToUse, set inside DetailsVC, is nil.
Any help would be greatly appreciated.
First, make sure you ctrl drag from the VIEWCONTROLLER, not the individual cell.
Second, the sender is not of type YelpTablseBusinessCell, i believe this is your problem.

Resources