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

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.

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.

Swift 2 passing data through segue from UIcollectionview (indexpath.row)

I'm trying to pass data (value of dict["IDD"] ) via segue.
(XCODE 7.2)
Here my code:
var arrRes = [[String:AnyObject]]()
var dict = [:]
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("imageCell", forIndexPath: indexPath) as! collectionViewCell
var dict = arrRes[indexPath.row]
let newPrice = dict["price"]!.stringByReplacingOccurrencesOfString(".", withString: ",")
cell.lblPrice.text = "€\(newPrice)"
cell.lblTitle.text = dict["title"] as? String
cell.lblBrand.text = dict["brand"] as? String
cell.lblIDD.text = dict["IDD"] as? String
cell.imageView!.image = UIImage(named: "loading.gif") //set placeholder image first.
dispatch_async(dispatch_get_main_queue()) {
cell.imageView!.downloadImageFrom(link: dict["image"] as! String, contentMode: UIViewContentMode.ScaleAspectFit) //set your image from link array.
}
return cell
}
But I don't kwon how to setup the performseguewithidentifier for segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "homeDetailSegue")
{
let cell = sender as! UICollectionViewCell
let detailView = segue.destinationViewController as! dettaglio
let dict = arrRes[(self.myCollectionView.indexPathForCell(cell))!]
detailView.setnomeOggetto(arrRes["IDD"] as! String)
}
}
I have no problem with tableviews but this is my first time with collectionView
Could please someone help me?
Kind Regards
Thank You very Much
Fabio
I tried the solution for your question and now I got it.Try below the solution.
Before that go to stroyborad click the destinationViewController(here detailViewController)
Then click Identity Inspector of the Right Navigation bar.
Once you click the you can see the Identity under Custom Class
No click Identity then give 'detailsVC' or whatever you want in Storyboard ID.
Now in CollectionViewDelegate Method
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
// handle tap events
print("You selected cell #\(indexPath.item)!")
let detailVC = self.storyboard?.instantiateViewControllerWithIdentifier("detailVC") as! DetailViewController
detailVC.passDataToLabel = String (indexPath.item )
print(detailVC.passDataToLabel)
self.navigationController?.pushViewController(detailVC, animated: true)
}

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 pass data from UITableViewCell to ViewController?

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)
}

Still showing nil variable on didSelectRow method

courseData is an array of struct objects. Each object has 9 string variables (holeNumber, Par, SI, plan etc) The cells populate fine but when you click on a row I am trying to get a detail view controller to populate an image view with the image held under variable plan for the relevant struct in the array. Complete code shown. courseImage1 variable still shows nil. Please help I have been stuck for 3-days!
Note: var CourseImage1: UIImage? is declared publicly at the top of my code.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return courseData.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Hole") as UITableViewCell
let item = courseData[indexPath.row]
let holeLabel = cell.viewWithTag(1) as UILabel
holeLabel.text = item.holeNumber
let parLabel = cell.viewWithTag(2) as UILabel
parLabel.text = item.par
let siLabel = cell.viewWithTag(3) as UILabel
siLabel.text = item.SI
let wyLabel = cell.viewWithTag(4) as UILabel
wyLabel.text = item.whiteYards
let yyLabel = cell.viewWithTag(5) as UILabel
yyLabel.text = item.yellowYards
let ryLabel = cell.viewWithTag(6) as UILabel
ryLabel.text = item.redYards
let photo = item.image
let placeholder = cell.viewWithTag(7) as UIImageView
placeholder.image = UIImage(named: photo)
return cell
}
func tableView(tableView: UITableView, didselectRowAtIndexPath indexPath: NSIndexPath) {
let courseSection = courseData[indexPath.row]
var tipPic = courseSection.plan
var courseImage : UIImage = UIImage(named: tipPic)!
courseImage1 = courseImage
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var destViewController : DetailViewController = segue.destinationViewController as DetailViewController
if (segue.identifier == "courseTip") {
destViewController.receivedPic = courseImage1
}
}
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
(indexPath, animated: false)
}
Because didSelectRowAtIndexPath fires after the prepareForSegue, put all the relevant logic in the latter method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var destViewController : DetailViewController = segue.destinationViewController as DetailViewController
if (segue.identifier == "courseTip") {
// I assume "self.tableView" points to your TV, if not amend the name below
if let indexPath = self.tableView.indexPathForSelectedRow() {
let courseSection = courseData[indexPath.row]
var tipPic = courseSection.plan
var courseImage : UIImage = UIImage(named: tipPic)!
destViewController.receivedPic = courseImage
} else {
println("Oops, no row has been selected")
}
}
}

Resources