fisherM
fisherM

Reputation: 177

Value of type 'Any?' has no member 'tag'

I added a button on my custom tableViewCell and i want that after the button is tapped it pass data according to the cell on which the button is located, so to find out on which cell is the button i added a tag, but i'm getting this error

enter image description here

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "goToLast" && sender is IndexPath {


             let dvc = segue.destination as! FinalClass 
            dvc.index = (sender as! IndexPath).row
            dvc.places = sortedArray
            dvc.userLocation = currentLocation

            }
        if segue.identifier == "goToReview" {

            let index3 = IndexPath(row: sender.tag, section: 0)
            let rev = segue.destination as! ReviewClass


        }

}

How can i adjust it?

(cell.reviewButton.tag = indexPath.row) this is the tag i added in my cellForRowAt

UPDATE

this is the button action in my VC

  @IBAction func ReviewButtonTap(_ sender: UIButton) {

      performSegue(withIdentifier: "goToReview" , sender: self)
    }

and this is the outlet in my custom tableViewCell class

 @IBOutlet weak var reviewButton: UIButton!

i do not understand how can i associate this button with

else if segue.identifier == "goToReview", let button = sender as? UIButton, let rev = segue.destination as? ReviewClass {
            let index3 = IndexPath(row: button.tag, section: 0)

Upvotes: 1

Views: 3052

Answers (5)

Susim Samanta
Susim Samanta

Reputation: 1593

You can get UIButton object from sender then UIButton object have tag property to access.

let button = sender as? UIButton
let index3 = IndexPath(row: button?.tag ?? 0, section: 0)

Upvotes: 0

Kuldip
Kuldip

Reputation: 1

For manage this problem first you need to first assign tag of button in cell for row which is you already done.

cell.reviewButton.tag = indexPath.row

After that you can add target actions on your button in the cell for row

cell.reviewButton.addTarget(self, action:#selector(self. 
func_Navigate(_:)), for:UIControlEvents.touchUpInside)

Out side cell for row make implement one function like that:-

@IBAction func func_Navigate(_ sender: UIButton)
{

If sender.tag == 0

 {

    \\ here sender.tag = 0 means button belong from fist row of your table view. you can perform navigation like that :-
    self.performSegue(withIdentifier: "goToLast", sender: self)

}
else If sender.tag == 1

{
    \\ here sender.tag = 1 means button belong from second row of your table view. you can perform navigation like that :-
    self.performSegue(withIdentifier: "goToReview", sender: self)
}

Upvotes: 0

Rakesh Patel
Rakesh Patel

Reputation: 1701

Just you need to change your segue method with below method

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goToLast" && sender is IndexPath {


         let dvc = segue.destination as! FinalClass 
        dvc.index = (sender as! IndexPath).row
        dvc.places = sortedArray
        dvc.userLocation = currentLocation

        }
    if segue.identifier == "goToReview" {

        let index3 = IndexPath(row: (sender as! UIButton).tag, section: 0)
        let rev = segue.destination as! ReviewClass


    }
}

Upvotes: 0

vadian
vadian

Reputation: 285082

Force downcast the optional Any to expected UIButton and IndexPath and use an else clause instead of if twice

 if segue.identifier == "goToLast" {
        let dvc = segue.destination as! FinalClass 
        let indexPath = sender as! IndexPath
        dvc.index = indexPath.row
        dvc.places = sortedArray
        dvc.userLocation = currentLocation
}
else if segue.identifier == "goToReview" {
        let button = sender as! UIButton
        let index3 = IndexPath(row: button.tag, section: 0)
        let rev = segue.destination as! ReviewClass
}

or use a switch

 switch segue.identifier {
     case "goToLast":
        let dvc = segue.destination as! FinalClass 
        let indexPath = sender as! IndexPath
        dvc.index = indexPath.row
        dvc.places = sortedArray
        dvc.userLocation = currentLocation

    case "goToReview":
        let button = sender as! UIButton
        let index3 = IndexPath(row: button.tag, section: 0)
        let rev = segue.destination as! ReviewClass

    default: break
}

The code must not crash. If it does you made a design mistake.

Edit

You have to pass the UIButton instance (the sender) from the IBAction to the sender parameter of performSegue. The outlet is irrelevant.

performSegue(withIdentifier: "goToReview" , sender: sender)

Upvotes: 1

Matic Oblak
Matic Oblak

Reputation: 16774

It is common you will provide a sender as Any in your method in cases where it can be different object. In such case you will need to typecast it to a specific object which is best done as let button = sender as? UIButton in swift.

By doing so you will receive an optional value and its tag would be button?.tag and its type Int? which then again disallows you to create an index path.

So a proper way for this method should be as follows:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goToLast", let indexPath = sender as? IndexPath {
            let dvc = segue.destination as! FinalClass 
            dvc.index = indexPath.row
            dvc.places = sortedArray
            dvc.userLocation = currentLocation
    } else if segue.identifier == "goToReview", let button = sender as? UIButton {
            let index3 = IndexPath(row: button.tag, section: 0)
            let rev = segue.destination as! ReviewClass
    }
}

Note that I have changed both if statements and managed to even remove an extra force-unwrap (The ! thing). If you don't mind force-unwrapping which crashes your app when incorrect then you could as well just do:

let index3 = IndexPath(row: (sender as! UIButton).tag, section: 0) 

But I suggest you avoid all force-unwrapping to improve stability. For your case that would be:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goToLast", let indexPath = sender as? IndexPath, let dvc = segue.destination as? FinalClass  {
            dvc.index = indexPath.row
            dvc.places = sortedArray
            dvc.userLocation = currentLocation
    } else if segue.identifier == "goToReview", let button = sender as? UIButton, let rev = segue.destination as? ReviewClass {
            let index3 = IndexPath(row: button.tag, section: 0)
    } else {
         print("ERROR: Incorrect configuration!") // Put a breakpoint here and analyze what was the identifier and what the sender so none of the statements were hit
    }
}

Upvotes: 1

Related Questions