Transfer data between uiviews in nil - ios

Hi I'm trying to pass an object from one tableview controller to another one.
I'm using prepare for segue and a didselectRowAt function:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.myDish = sections[indexPath.section].listofDishes?[indexPath.row]
self.sectionName = sections[indexPath.section].sectionName!
self.flag = 1
performSegue(withIdentifier: "chooseDish", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if self.flag != 0{
let selectedDishTableViewController = segue.destination as! SelectedDishViewController
selectedDishTableViewController.sectionName = self.sectionName
selectedDishTableViewController.dish = self.myDish
self.flag = 0
}
}
I created the flag because preparefor segue gets called before didselectrowat
Thant way I'm making sure to set myDish and sectionName before the prepare for segue.
by in my second view controller I still get both values as nil.
This is how I declare the vars in my second controller
var dish:Dish?
var sectionName:String?

If prepareForSegue is getting called before didSelectRow.., then it implies that the segue is already happening, so the second time when you force it to be invoked, by calling performSegue explicitly in didSelectRow <- it is ignored as the view controller has already been segued into with null values for those vars.
The solution here would be to do something like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableView.indexPathForSelectedRow {
let selectedDishTableViewController = segue.destination as! SelectedDishViewController
selectedDishTableViewController.dish = sections[indexPath.section].listofDishes?[indexPath.row]
selectedDishTableViewController.sectionName = sections[indexPath.section].sectionName!
}
}
You don't have to do anything else in tableView:didSelect... for this, with this.. Lemme know if it works out.

Related

Receiver has no segue with identifier 'goToDetail'

I've double-checked that my identifier is spelled correctly in the storyboard and that the segue is pointed in the right direction, but I keep receiving this error. Could someone take a look at my code to see if I'm missing something?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedData = tableData[indexPath.row]
tableView.deselectRow(at: indexPath, animated: true)
performSegue(withIdentifier: "goToDetail", sender: selectedData)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? DetailViewController {
destination.vocab.text = sender as? String
}
}
Edit:
segue can be used only with if you have a UINavigationController that will handle the navigation. You performing a segue to UINavigationController. UINavigationController should be at the root of your storyboard.
In your didSelectRowAt function change
performSegue(withIdentifier: "goToDetail", sender: selectedData)
To NavigationController Identifier (Note if you do not have identifier already set up in storyboard you will need to do that first)
performSegue(withIdentifier: "goToNavigationConrtoller", sender: selectedData)
And then change the prepareForSegue function to below.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//check destination for segue is Navigation controller
if let navVC = segue.destinationViewController as? UINavigationController {
// get hold of the first viewController.
if let destination = navVC.viewControllers[0] as? DetailViewController {
destination.vocab.text = sender as? String
//all the other code you need
}
}
}

Set a string as a var after selecting a UITableView cell in Swift 3

I am making an audio app, and I am populating a table view controller with data from JSON. Based on the user's selection, I want to pass the episode_name, shown in the cell, into the next view after segue.
So far, the table loads with data, I can pass a locally defined variable to the next view, but I can't copy the string from the cell into that variable.
Here's my code.
func extract_json(_ data: Data)
{
//... removed to condense
if let shows_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let shows_obj = shows_list[i] as? NSDictionary
{
let episode_name = shows_obj["episode"] as? String
let episode_date = shows_obj["date"] as? String
TableData.append(episode_date! + " | " + episode_name!)
}
}
}
DispatchQueue.main.async(execute: {self.do_table_refresh()})
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "passer", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "passer" {
let vc = segue.destination as! EpisodeViewController
vc.variableInSecondVc = "Pass Variable" // this is where I want to pass episode_name
}
} // end segue
Solutions I have tried:
1) if I call episode_name immediately, it flags it because that variable is contained in the prior function,
2) if I try to run the extract_json function in the ViewDidLoad, it's causing other issues in the code.
I'm new to Swift and unsure -- is there a better way to "copy" the string from that cell and pass it to the vc.variableInSecondVc?
EDIT: One point of clarification. If I ran this code, it would successfully change the subsequent UILabel to "Pass Variable" -- but of course, I actually want to turn PassVariable dynamic by changing it to reflect the string of episode_name.
The performSegue(withIdentifier:sender:) method takes two arguments, 1. The Segue identifier, 2. The parameter you want to pass of type AnyObject?
self.performSegue(withIdentifier: "passer", sender: indexPath)
In the prepare(for:sender:) method, you check the segue identifier and cast the sender parameter to the type you had passed earlier, and get data from the array list to pass it to next view controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "passer" {
guard let indexPath = sender as? IndexPath else { return }
let data = TableData[indexPath.row] //write your logic here to get value from your data list based on index path row value and pass value to view controller.
let vc = segue.destination as! EpisodeViewController
vc.variableInSecondVc = data //value which you got from list based on indexPath
}
}
Get data from indexPath
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "passer" {
let vc = segue.destination as! EpisodeViewController
vc.variableInSecondVc = TableData[indexPath.row]
}
}
And carefully, you should get the string in viewDidAppear in secondViewController
override func viewDidAppear() {
label.text = variableInSecondVc
}
Thanks for the help, all.
While these solutions didn't work, they helped me rethink the problem, and I found a solution that will pass the value to the label in the next VC successfully. Here's the approach:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "passer", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = self.tableView.indexPathForSelectedRow {
let controller = segue.destination as! EpisodeViewController
controller.variableInSecondVc = TableData[indexPath.row]
}
}
The label now updates in the next view controller.
try this it will send the selected string to next View controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "passer" , let indexPath = sender as? IndexPath{
let vc = segue.destination as! EpisodeViewController
vc.variableInSecondVc = TableData[indexPath.row]
}
}

Passing data to next view controller, but data disappears?

I have a tableview with cells that each contain a "Concept", CoreData Object. They all have an attribute called html, which are not nil (I checked that by printing in this tableview viewDidLoad()). The problem is when I try to pass it in prepareForSegue this data kind of disappears.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = tableView.indexPathForSelectedRow
if segue.identifier == "toConcept"{
let nc = segue.destination as! UINavigationController
let vc = nc.viewControllers.first as! PDFViewController
print(concepts[(indexPath?.row)!].html
//This prints nothing.
vc.html = concepts[(indexPath?.row)!].html
}
}
The problem is in the viewDidLoad() the html is fine and not nothing, but in the prepareForSegue is prints nothing and in the PDFViewController it is also gone.
Does anybody know how to fix this?
try this..
func tableView(_ messagesListTableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "toConcept", sender: indexPath)
print(indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toConcept"{
let indexPath = sender as? IndexPath
let nc = segue.destination as! UINavigationController
let vc = nc.viewControllers.first as! PDFViewController
print(concepts[(indexPath?.row)!].html
//This prints nothing.
vc.html = concepts[(indexPath?.row)!].html
}
}

Swift/XCode - Why can't I pass data to the next view controller when a tableViewCell is pressed?

Apologies if this has already been answered elsewhere- I have spent the last two hours trying different things using similar suggestions but I still can't solve it!
Basically, I have a UITableView with custom cells(for different quiz topics) that when pressed, should allow the app to go to the next VC and pass an integer so the next VC knows which quiz to load. In my table view VC I have this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
rowPressed = indexPath.row
print ("rowPressed = \(rowPressed) before VC changes")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is GameVC {
let vc = segue.destination as? GameVC
vc?.toPass = rowPressed
print("I ran...")
} else {
print("You suck")
}
and in my GameVC I have this:
var toPass = Int()
override func viewDidLoad() {
super.viewDidLoad()
print("toPass = \(toPass)")
The console output when run is this:
I ran...
toPass = 0
rowPressed = 3 before VC changes
So it looks like the VC changes before the previous one can send the correct value of toPass. How do I fix this?
Many thanks in advance!
According to the segue description:
For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped.
So, sender is a UITableViewCell and you can do like below:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let cell = sender as? UITableViewCell else { return }
guard let idx = tableView.indexPath(for: cell) else { return }
guard let vc = segue.destination as? GameVC else { return }
vc.toPass = idx.row
}
The problem of your code was prepare(segue:) was invoked before tableView(didSelectRowAt:).
As suggested you have to call the performSegue method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// not necessary
//rowPressed = indexPath.row
//print ("rowPressed = \(rowPressed) before VC changes")
performSegueWithIdentifier("Your id", sender: indexPath)
//You can set the identifier in the storyboard, by clicking on the segue
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Your id"{
var vc = segue.destinationViewController as! GameVC
vc.toPass = (sender as! IndexPath).row
}
}
Because prepareForSegue is called before didSelectRowAt, you can also remove the segue from the Storyboard, drag and drop holding ctrl from the UITableViewController's yellow icon in the top to the second ViewController, click on the segue, give it an identifier, so now you can call performSegue:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.pressed = indexPath.row
self.performSegue(withIdentifier: "segue identifier", sender: nil)
}

Passing data from table view cell to new view controller error

I'm trying to pass data from my tableViewCell, however, keep getting an error on my let cell = sender as! UITableViewCell
fatal error: unexpectedly found nil while unwrapping an Optional
value.
How would I make my cell an optional? I'm trying to pass the data from an array.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? UsersProfileViewController{
let cell = sender as! UITableViewCell
let selectedRow = partnerTable.indexPath(for: cell)?.row
if let name = usersArray[selectedRow!].firstLastName{
destination.name = name
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "toUser", sender: nil)
}
}
You should trigger this way
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "toUser", sender: indexPath.row)
}
and in prepare for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let selectedRow = sender as? Int
if let name = usersArray[selectedRow!].firstLastName{
destination.name = name
}
}
If you prefer to use segue using storyboard:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableView.indexPathForSelectedRow {
guard let destinationVC = segue.destination as? UsersProfileViewController else {return}
let selectedRow = indexPath.row
destinationVC.name = usersArray[selectedRow].firstLastName
}
}
You can also segue to the destination ViewController programatically and pass the required value to that Controller.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sb = UIStoryboard.init(name: "StoryboardName", bundle: nil)
let destinationVC = sb.instantiateViewController(
withIdentifier: "StoryboardId for viewcontroller set in storyboard") as? UsersProfileViewController
guard let name = usersArray[selectedRow!].firstLastName { else return }
destinationVC.name = name
self.presentViewController(destinationVC, animated: true, completion: nil)
}
Steps:
Get an instance of the storyboard.
Instantiate the required viewcontroller with the help of storyboard Id which is assigned in the storyboard.
Pass the data required to destination VC.
Present the destination View Controller.
I got one thing from you post, you want to get current selected tableview data and want to pass it to next view controller, you can do this in a simple way. Check tableview cell selected delegate method
didSelectRowAtIndexPath
From this delegate method you can save selected row index and in prepare segue method call this function in this way , selectedRow value you can set in didSelectRowAtIndexPath delegate method.
usersArray[selectedRow!].firstLastName
Steps To Do:
First of all create the property selectedIndex in View controller.
set the selectedIndex property value with indexPath from didSelectRowAtIndexPath table view's delegate methods.
In Prepare for Segue method you can find the data value from your datasource object as you have selected index.
4.Pass the value from datasource for selected index and make selectedIndex property to nil.

Resources