Variable nil when trying to pass variable to view controller with segue - ios

Here is the code I currently have:
var valueToPass:String!
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel.text // valueToPass now equals "test"
performSegueWithIdentifier("yourSegueIdentifer", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var viewController = segue.destinationViewController as AnotherViewController
viewController.passedValue = valueToPass // valueToPass equals nil here? Why?
}
as you can see, i have assigned "test" to valueToPass in didSelectRowAtIndexPath but in prepareForSegue, valueToPass is nil? why?

Try replacing:
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
let indexPath = tableView.indexPathForSelectedRow();
let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!;
valueToPass = currentCell.textLabel.text // valueToPass now equals "test"
performSegueWithIdentifier("yourSegueIdentifer", sender: self)
}
With this:
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath.row) as! UITableViewCell;
valueToPass = currentCell.textLabel.text! // valueToPass now equals "test"
performSegueWithIdentifier("yourSegueIdentifer", sender: self)
}
Also, when you made the segue in the storyboard, make sure you DID NOT drag from the tableview to the next viewcontroller. This is because if you do this automatically, then perform segue will be called BEFORE didSelectRow... making it perform the segue before you set the value. Just drag from the viewcontroller that hosts the table view to the new view.

Related

When transferring text from a cell to a label in the next VC, it doesn't appear the first time the VC is opened

I am making an app where you create memories into a tableView and you tap on a cell and it takes you to its designated VC, I am trying to make it so that when the cell is tapped, the text from inside that table cell is taken into the label in the next VC. This works but the problem is that when the view is first opened, the text doesn't appear inside the label, even though the console says that the text has loaded correctly:
TableView with a test cell in it.
First time the VC for the cell is opened
Console saying that the text has transferred and loaded correctly
Second time the VC is opened.
Here is my transferring and loading code for the ViewControllers:
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
var selectedRow : Int = 0 + 1
var selectedNameVC : String?
var favourites: [NSManagedObject] = []
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//MARK:- New CoreData methods
let favMemory = favourites[indexPath.row]
cell.textLabel?.text = favMemory.value(forKeyPath: "name") as? String
if #available(iOS 13.0, *) {
cell.backgroundColor = UIColor.secondarySystemGroupedBackground
} else {
// Fallback on earlier versions
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedRow = indexPath.row
let cell = tableView.cellForRow(at: indexPath)
let favMemory = cell?.textLabel?.text
selectedNameVC = favMemory
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? FavouritesViewController {
destination.row = selectedRow
destination.selectedName = selectedNameVC
}
}
class FavouritesViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate, UITextViewDelegate {
var selectedName: String?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
labelText.text = selectedName
if labelText.text == "" as String? {
print("Title has not loaded correctly.")
} else {
print("Title has loaded correctly.")
}
}
First, you shouldn't get values from cells... get them from your data source (which is what you used to set the values).
So, in didSelectRowAt instead of:
let cell = tableView.cellForRow(at: indexPath)
let favMemory = cell?.textLabel?.text
you should do:
let favMemory = favourites[indexPath.row]
However, that's not what's causing your issue.
I'm assuming in your Storyboard you have a "Show Segue" connected from your prototype cell to your FavouritesViewController. If that's the case, prepare(for segue: ...) is called before didSelectRowAt. To confirm this, change your code to:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedRow = indexPath.row
// don't do this
//let cell = tableView.cellForRow(at: indexPath)
//let favMemory = cell?.textLabel?.text
let favMemory = favourites[indexPath.row]
print("In didSelectRowAt --- Setting var selectedNameVC = favMemory: \(favMemory)")
selectedNameVC = favMemory
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? FavouritesViewController {
print("In prepareForSegue --- Setting selectedName in FavouritesViewController = selectedNameVC: \(selectedNameVC)")
destination.row = selectedRow
destination.selectedName = selectedNameVC
}
}
This is what you should see in the debug console:
In prepareForSegue --- Setting selectedName in FavouritesViewController = selectedNameVC: nil
In didSelectRowAt --- Setting var selectedNameVC = favMemory: Test
Title has loaded correctly.
As you see, prepareForSegue runs first, so the value of selectedNameVC is nil (it hasn't been set yet).
To fix this, you could set your variables in willSelectRowAt instead of in didSelectRowAt:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
selectedRow = indexPath.row
let favMemory = favourites[indexPath.row]
selectedNameVC = favMemory
return indexPath
}
Alternatively, create your Segue in Storyboard, but do not connect it to the cell. Instead, give it an Identifier (such as "ShowFavourites"), and in didSelectRowAt, set your vars and call performSegue:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedRow = indexPath.row
let favMemory = favourites[indexPath.row]
selectedNameVC = favMemory
performSegue(withIdentifier: "ShowFavourites", sender: nil)
}

how to get data when cell accessory is tapped in ios

i have tableview which has several cells, when user tap on the cell it does some other function and when cell accessory is tapped i want it to segue to another view controller
i am able to do so but the problem is i am not able to send the data at cell indexpath row, in order to send data i first have to touch the cell then tap on accessory to send the data here is the code for tableview
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
myTableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.identifier {
case "ShowDetail":
if let ip = myTableView.indexPathForSelectedRow{
if let svc = segue.destination as? DetailViewController{
svc.selectedObject = (myCounters?[ip.row])!
}
}
default:
print("error in segue")
}
}
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "detailvc") as? DetailViewController
vc?.selectedObject = (myCounters?[indexPath.row])!
navigationController?.present(vc!, animated: true, completion: nil)
}
thanks to #shahzaib qureshi
i removed the segue connection and use instantiate view controller method as detail view controller in cell accessory tapped method
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let vc = storyboard?.instantiateViewController(withIdentifier: "detailvc") as? DetailViewController
vc?.selectedObject = (myCounters?[indexPath.row])!
navigationController?.present(vc!, animated: true, completion: nil)
print("ewwewew")
}
Just pass the index path in the sender parameter, pretty easy:
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
performSegue(withIdentifier: "ShowDetail", sender: indexPath)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowDetail" {
let indexPath = sender as! IndexPath
let svc = segue.destination as! DetailViewController
svc.selectedObject = myCounters![indexPath.row]
}
}
Try this,

func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
print("Index : \(indexPath.row)")
var tempIndex: Int!
var tempIdentifier: String!
self.tempIndex = indexPath.row //You can store indexpath.row value in temp variable. And then then you can access it while performing any other functions.
self.tempIdentifier = "ShowDetail" //This upto your needs.
}
//UIStoryboardSegue to move over another controller accordingly.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
switch segue.identifier {
case "ShowDetail":
if let ip = myTableView.indexPathForSelectedRow{
if let svc = segue.destination as? DetailViewController{
svc.selectedObject = (myCounters?[tempIndex])!
}
}
default:
print("error in segue")
}
}

Not receiving data in detailview

I have a TableView that when the user clicks it needs to show a detail view displaying the name of the row it clicked. I populate the tableView using a Json call but I can't figure out what I'm doing wrong.
This is my bits of code of ViewController
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var valueToPass:String!
#IBOutlet weak var tableView: UITableView!
// Instantiate animals class
var items = [animalObject]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
addDummyData()
}
func addDummyData() {
RestManager.sharedInstance.getRandomUser(onCompletion: {(json) in
if let results = json.array {
for entry in results {
self.items.append(animalObject(json: entry))
}
DispatchQueue.main.sync{
self.tableView.reloadData()
}
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier") as! CustomTableViewCell!
let animal = self.items[indexPath.row]
cell?.label.text = animal.name
return cell! //4.
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.row)!")
// Get Cell Label
// let indexPath = tableView.indexPathForSelectedRow!
var currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
print(currentCell.textLabel?.text)
valueToPass = currentCell.textLabel?.text
print(valueToPass)
performSegue(withIdentifier: "detailView", sender: indexPath)
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "detailView") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destination as! DetailViewController
// your new view controller should have property that will store passed value
viewController.passedValue = valueToPass
}
}
}
and my detailView only has the following info
#IBOutlet weak var tooPass: UILabel!
var passedValue: String!
override func viewDidLoad() {
super.viewDidLoad()
print("DETAILS")
print(passedValue)
tooPass.text = passedValue
}
I'm not sure if the preparedToSegue is firing a little bit earlier because my terminal looks like this:
I used as a reference the following question any guidance will be appreciated
Try this for didSelectRowAt method.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
valueToPass = self.items[indexPath.row]
print(valueToPass)
performSegue(withIdentifier: "detailView", sender: indexPath)
}
Just pass the entire item in the items array.
Make the DetailViewController's passed item of type item. Then just access item.whateverPropertyRequired.
The problem is that in your cellForRow method, you are casting your cell to CustomTableViewCell type while in your didSelectRow, you are casting it as UITableViewCell. Change
var currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
in your didSelectRow to
var currentCell = tableView.cellForRow(at: indexPath)! as CustomTableViewCell
Good approach here is to use view model. Create model which contains your items and data associated with them. And at segue pass your data model. It’s bad coding, getting data right from cell on didSelect method, unless some ui working on that cell

Can't get Label value from UITableViewCell

I'm trying to pass a value from one view controller to another. As i tested this is ok and runs with my code. But i have a problem when i'm trying to take cell label value. I tried everything i found on google and stackoverflow, but there is no answer on this. I added a comment on code which says // I THINK THE PROBLEM IS ON THE NEXT LINE OF CODE to help you find where i'm stuck.
My code:
import UIKit
class PlacesTableViewController: UITableViewController {
#IBOutlet weak var placesTableView: UITableView!
var places = [Places]()
var valueToPass:String!
let cellIdentifier = "PlacesTableViewCell"
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
// Table view cells are reused and should be dequeued using a cell identifier.
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? PlacesTableViewCell else {
fatalError("The dequeued cell is not an instance of PlacesTableView Cell.")
}
let place = places[indexPath.row]
cell.placeLabel.text = place.name
cell.ratingControl.rating = place.rating
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
print("You selected cell #\(indexPath.item)!")
let indexPath = placesTableView.indexPathForSelectedRow
// I THINK THE PROBLEM IS ON THE NEXT LINE OF CODE
let currentCell = placesTableView.cellForRow(at: indexPath!)! as UITableViewCell
print(currentCell.detailTextLabel?.text ?? "Failed to use Cell Label")
valueToPass = currentCell.detailTextLabel?.text ?? "Failed to use Cell Label"
performSegue(withIdentifier: "ShowCommentsTableViewController", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "ShowCommentsTableViewController") {
// initialize new view controller and cast it as your view controller
let viewController = segue.destination as! CommentsTableViewController
// will be stored in passedValue on the CommentsTableViewController
viewController.passedValue = valueToPass
//print (viewController.passedValue ?? "")
}
}
}
Why don't you just use the raw value from the data store like you do in the cellForRowAt?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let place = places[indexPath.row]
valueToPass = place.name
performSegue(withIdentifier: "ShowCommentsTableViewController", sender: self)
}
Replace your didselect method with the given below
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let place = places[indexPath.row]
valueToPass = place.name
performSegue(withIdentifier: "ShowCommentsTableViewController", sender: self)
}
There are two basic issues in your current code, first you are casting your cell into UITableViewCell instead of PlacesTableViewCell and the second issue is that you are accessing detailTextLabel which is not available in your custom cell instead of using placeLabel. The correct code will be like that:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
print("You selected cell #\(indexPath.item)!")
let indexPath = placesTableView.indexPathForSelectedRow
// I THINK THE PROBLEM IS ON THE NEXT LINE OF CODE
let currentCell = placesTableView.cellForRow(at: indexPath!)! as PlacesTableViewCell
print(currentCell. placeLabel?.text ?? "Failed to use Cell Label")
valueToPass = currentCell. placeLabel?.text ?? "Failed to use Cell Label"
performSegue(withIdentifier: "ShowCommentsTableViewController", sender: self)
}
And if you dont want to go through this hassle , you can simply get the values from the original array like that:
let place = places[indexPath.row]
valueToPass = place.name

Passing data from tableviewcell to another viewcontroller

I am trying to pass cell's tag data to another viewcontroller's variable
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let currentCell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell!
print(currentCell.tag)
valueToPass = currentCell.tag
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let Dest = segue.destinationViewController as! EventDetail
if (segue.identifier == "eDetay") {
Dest.eventid = String(valueToPass)
}
}
When i try as it is, valueToPass is always passed the previous data.
Is there a way to get selected cell data from prepareForSegue ?
What is the best way to do that?
You are overriding the indexPath parameter that is being passed to you.
Remove the first let statement. Use the indexPath that is being passed to you.
func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!)
{
let cell = tableView.cellForRowAtIndexPath(indexPath) as YourCell
//cell.value, cell.text, cell.randomValue or whatever you need
}

Resources