Need Help Transfer Data From Button

I am not sure how to transfer data a name from a button in my UITableViewCell to a UIViewController. I have a prepare function but it doesn't work gives me an unrecognized selector error. Is there a way to use it for this instance?

func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "hs" {
    let destinationController = segue.destination as! HighlightsVC
      destinationController.playerName = (item.yahooName!)
  }
  }

gives me an unrecognized selector error

Which line ?

You need to retrieve the item, which is the selected cell.
You can do this in didSelectRow to keep the selectedCell reference. Or you could also extract there item.yahooName and keep it in a var selectedName in VC and use it in prepare.

Another point: don't force downcast. If it fails, app will crash

      let destinationController = segue.destination as! HighlightsVC
      destinationController.playerName = (item.yahooName!)

But conditionnal unwrap:

if  let destinationController = segue.destination as? HighlightsVC {
      destinationController.playerName = selectedName      // Set in didSelectRow
}

No need either for parenthesis around name, but for such code, use ?? operator:

      destinationController.playerName = item.yahooName ?? ""

The thing is I don't want the whole cell to be selected. I just want the button to be.

The thing is I don't want the whole cell to be selected. I just want the button to be.

When you tap the button, de facto you select cell. So probably you can use

optional func tableView(_ tableView: UITableView,  willSelectRowAt indexPath: IndexPath) -> IndexPath?

to get the select and immediately deselect it so that it is not highlighted. But in the meantime, get the cell reference and name.

I am still confused I don't understand what you mean by cell reference. I am also not sure how to reference the VC inside of UITableViewController.

What exactly should I put in my button in my cell that does the action?

   @IBAction func CamButtonA(_ sender: UIButton) {
    //?
    }

what you mean by cell reference

In willSelectRowAt, you can either get the cell itself or you can directly get the yahooName of the cell. And save it in a global var of the VC.

Then, the segue is from the VC (not the cell) to the next VC. In prepare, you set the name as you have done.

In IBAction, just call the segue:

@IBAction func CamButtonA(_ sender: UIButton) {
              performSegue(withIdentifier: "hs", sender: nil)  // No need to know  the sender here
    }

This is the best I could do with making a global variable but what do I put inside exactly willSelectRowAt, You can't put any segues in there.

struct GlobalFavPlayer {
   
  func addObserver() {
  NotificationCenter.default.post(name: .passFavNotification,
                  object: GlobalFavPlayer.globalFav)
  }
   
  static var globalFav: CurrentPlayers?
   
   
}

You can't put any segues in there.

What is the problem ? Do you get an error message ? Did you create the segue between VC (not cell) and the destination ?

I get the error '-[LearnHockey.FavCell CamButtonA:]: unrecognized selector sent to instance 0x7f9fe6038800'. I created the segue too but I don't know what to put in my willrowat exactly.

   override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "hs" {
    let destinationController = segue.destination as? HighlightsVC
      destinationController!.playerName = GlobalFavPlayer.globalFav!.yahooName!
  }
  }

   override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    //not sure what to put in here
  }

What I would do here, which is probably the simplest:

  • declare a var in VC
var selectedNameInCell : String?
  • get it in willSelectRowAt or in didSelectRowAt if you prefer:
   override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    // what is the dataSource ? I assume it is an [Item] ; I assume you have a single section
    selectedNameInCell = dataSource[indexPath.row].yahooName
    return nil    // cancel selection of cell
  }
  • Now you can use in prepare
   override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      if segue.identifier == "hs"  {
          if let destinationController = segue.destination as? HighlightsVC, let destName = selectedNameInCell {
              destinationController!.playerName = destName
           }
      }
  }

Oh I forgot to mention that my data source is a set so it is not like that. I'm not sure how to get the value from the cell like you have done.

var favSet: Set<CurrentPlayers> {
    FavouriteManager.shared.favSet
  }

It's risky to have dataSource as a Set, because order will change. Don't you mind ?

Anyway, just replace

selectedNameInCell = dataSource[indexPath.row].yahooName

with

   override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
      if let cell = cellForRow(at: IndexPath) {
         selectedNameInCell = cell.yahooName     // I assume you have such a property in the cell ; or maybe it is just cell.textLabel!.text if the name is the textLabel
     }
  }

Okay I will do that. I have a set because I wanted to avoid duplicates with an array.

I wanted to avoid duplicates with an array.

Yes, that's logical, but you should handle it when you populate the array. Or remove any duplicate in the array:

var myArray = [/* Some items*/]
var noReplicateArray = [] // with same types of items

for item in myArray {
    if !noReplicateArray.contains(item) {
        noReplicateArray.append(item)
    }
}

You can also use reduce:

noReplicateArray = myArray.reduce([]) {array, item in if array.contains(item) { return array } else { return array+[item] } }

But the first appears to be much faster.

Okay so I downloaded a swift package that gives me access to an orderedSet so I can solve that issue but thanks for your suggestion. Anyways I get the cannot find cellForRow error in scope.

   override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    if let cell = cellForRow(at: IndexPath) {
      selectedNameInCell = cell.yahooName
    }
  }

My mistake, just change:

    if let cell = tableView.cellForRow(at: IndexPath) {

Okay I have a new error this time. Cannot convert value of type 'IndexPath.Type' to expected argument type 'IndexPath'

override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    if let cell = tableView.cellForRow(at: IndexPath) {
      selectedNameInCell = cell.yahooName
    }
  }

Just a typo, working in forum editor and not XCode: it is indexPath and not IndexPath

    if let cell = tableView.cellForRow(at: indexPath) {

okay thanks but I get the error Value of type 'UITableViewCell' has no member 'yahooName'. I should show my cellForRowAt. I can also access yahooName with currentFav.yahooName but not sure how to access from the cell.

   override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "favcell", for: indexPath) as! FavCell
    let itemFav = favSet[favSet.index(favSet.startIndex, offsetBy: indexPath.row)]
    cell.layer.borderColor = UIColor.black.cgColor
    cell.layer.borderWidth = 0.5
    cell.contentView.backgroundColor = .random

    cell.update(with: itemFav)
    return cell
  }

I don't know enough about cell. Is it a custom class ? Or is yahooName the title of the standard cell ? In such a case, just call:

selectedNameInCell = cell.textLabel!.text!

I have a custom cell actually. I have the function I used to display it at the bottom. I am curious with willRowAt do I return indexPath?


   func update(with itemFav: CurrentPlayers) {
    name.text = itemFav.yahooName
    team.text = itemFav.team
    position.text = itemFav.position
    favImg.image = UIImage(named: "Grey")
    let url = URL(string: itemFav.photoUrl!)
    imageController.fetchItemArtwork(url: url!) {
      (image) in
      if let image = image {
        DispatchQueue.main.async {
          self.favImg.image = image
          self.favImg.roundedImage()
    }
   }
 }
}

So you should:

    if let cell = tableView.cellForRow(at: IndexPath) as? FavCell {
      selectedNameInCell = cell.name.text
    }

To be sure to understand where you stand now, could you repost the most recent code:

  • IBAction
  • prepare for segue

Another option is to get the name directly in the IBAction:

@IBAction func CamButtonA(_ sender: UIButton) {

     // Find the cell which contains this button
    var superview = sender.superview
    while let view = superview, !(view is UITableViewCell) {
        superview = view.superview
    }
    guard let cell = superview as? FavCell else {
        return
    }
    selectedNameInCell = cell.name.text
    performSegue(withIdentifier: "hs", sender: nil) 
}

Okay I get the same error unrecognized selector sent to instance '0x7fd4c4098200'. I tried both ways but end up with the same error. You don't but the button in the cell?

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
      if segue.identifier == "hs" {
        if let destinationController = segue.destination as? HighlightsVC, let destName = selectedNameInCell {
          destinationController.playerName = destName
         }
      }
    }
   
   
   
  override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
    if let cell = tableView.cellForRow(at: indexPath) as? FavCell {
      selectedNameInCell = cell.name.text
    }
    return indexPath
  }
   
  @IBAction func CamButtonA(_ sender: UIButton) {
    performSegue(withIdentifier: "hs", sender: nil)
  }
Need Help Transfer Data From Button
 
 
Q