3

As the title say I have a tableView with prototype cell; cell is a custom cell (so I made a class called CustomCell.swift in witch I created the IBOutlet for image, label, button etc); here my class

import UIKit

class CustomCell: UITableViewCell
{
    @IBOutlet var imageSquadra: UIImageView!
    @IBOutlet var button: UIButton!

    override func awakeFromNib()
    {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool)
    {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }
}

then I made the UITableViewController:

import UIKit

class SquadreController: UITableViewController
{
    var index: NSIndexPath?
    var isScrolling = Bool()

    override func viewDidLoad()
    {
        super.viewDidLoad()

        DataManager.sharedInstance.createCori()

    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
    }

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int
    {
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return DataManager.sharedInstance.arrayCori.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell
        let squadra = DataManager.sharedInstance.arrayCori[indexPath.row]

        cell.backgroundColor = UIColor.clearColor()

        if (indexPath.row % 2 == 0)
        {
            cell.backgroundColor = UIColor.greenColor()
        }
        else
        {
            cell.backgroundColor = UIColor.blueColor()
        }

        //Here I added target to the button in the cell, and below in the class I implemented the fun makeSegue()
        cell.button.addTarget(self, action: "makeSegue", forControlEvents: UIControlEvents.TouchUpInside)

        return cell
    }


    //Following 4 method are used to detect UIScollView scrolling and to change cell height.
    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
    {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        index = indexPath
        tableView.beginUpdates()
        tableView.endUpdates()
    }

    override func scrollViewWillBeginDragging(scrollView: UIScrollView)
    {
        isScrolling = true
        tableView.beginUpdates()
        tableView.endUpdates()
    }

    override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool)
    {
        isScrolling = false
    }

    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
    {
        if isScrolling
        {
            return 100
        }

        if index == indexPath
        {
            return 200
        }
        else
        {
            return 100
        }
    }

    //Here I implemented the makeSegue() func, the action I had made as target of the button.
    func makeSegue() {
        self.performSegueWithIdentifier("toCoriViewController", sender: self)
    }
}

Ok ok now comes the hard part: to make the prepareForSegue; I do not have any ideas how to solve this problem, I tried

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
    {
        if segue.identifier == "toCoriViewController"
        {
            if let indexPath = ?????????????
            {
                let controller = segue.destinationViewController as! CoriViewController
                controller.coriSquadra = DataManager.sharedInstance.arrayCori[indexPath.row]
            }
        }
    }

but I don't know how to set the constant indexPath.

Oh, first of all I made a segue by control-right from the button to the second controller: maybe I must make that segue from the tableView cell???

Hope someone could help me!

Fabio Cenni
  • 841
  • 3
  • 16
  • 30

3 Answers3

3

You could get the index path of the cell like so

let indexPath : NSIndexPath
if let button = sender as? UIButton {
    let cell = button.superview?.superview as! UITableViewCell
    indexPath = self.tableView.indexPathForCell(cell)
}

You'll also need to change your makeSegue like this:

func makeSegue(button:UIButton) {
    self.performSegueWithIdentifier("toCoriViewController", sender: button)
}

and in your cellForRowAtIndexPath just change the line where you set the action to cell.button.addTarget(self, action: "makeSegue:", forControlEvents: UIControlEvents.TouchUpInside).

Alternatively, you could create a squadra property inside your custom cell class to hold the arrayCori value of that cell, so you'd have some code that looks like this:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell
    let squadra = DataManager.sharedInstance.arrayCori[indexPath.row]
    cell.squadra = squadra #add this line
    cell.backgroundColor = UIColor.clearColor()
    if (indexPath.row % 2 == 0)
    {
        cell.backgroundColor = UIColor.greenColor()
    }
    else
    {
        cell.backgroundColor = UIColor.blueColor()
    }

    //Here I added target to the button in the cell, and below in the class I implemented the fun makeSegue()
    cell.button.addTarget(self, action: "makeSegue", forControlEvents: UIControlEvents.TouchUpInside)

    return cell
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
    if segue.identifier == "toCoriViewController"
    {
        let controller = segue.destinationViewController as! CoriViewController
        if let button = sender as? UIButton {
            let cell = button.superview?.superview as! CustomCell
            controller.coriSquadra = cell.squadra
        }
    }
}
The Beanstalk
  • 798
  • 1
  • 5
  • 20
  • Thanks for your answer! but when I run the project and I touch up inside the button Xcode gives me this error "Could not cast value of type 'UIButton' (0x10d693f68) to 'UITableViewCell' (0x10d6939f0)." and If I made the control-right segue from the cell to the second controller Xcode gives me this other error "Could not cast value of type 'Cori_da_stadio.SquadreController' (0x1084610e0) to 'UITableViewCell' (0x10ab8a9f0)." – Fabio Cenni Sep 01 '15 at 22:30
  • Oh, that's my bad, forgot the sender's a button not the cell. Just have to call `sender.superview` to get the cell. Will edit – The Beanstalk Sep 01 '15 at 22:32
  • So you have to add another `.superview`, or maybe a couple -- I don't know how your cells are laid out, so I can't tell you how many times you'll need to call it, but just keep adding them till it works. Like: `let cell = sender.superview?.superview as! UITableViewCell` – The Beanstalk Sep 01 '15 at 22:52
  • If I add two time .superview Xcode suggest me to change code in let cell = sender!.superview!!.superview as! CustomCell otherwise it does not compile and if I do this, Xcode tell me "found nil while unwrapping an Optional value" – Fabio Cenni Sep 01 '15 at 22:57
  • If your trying to get the indexPath for the cell, put `UITableViewCell` after `as`, but if you're doing it with the property, then put `CustomCell` after `as`. It should let you if you do `sender.superview?.superview as! CustomCell`. I've found that if you just delete all the "!" and "?", and then let xcode add them back in, it add's them the right way, otherwise it acts up like it's doing for you now. (Sorry for being confusing) – The Beanstalk Sep 01 '15 at 23:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/88522/discussion-between-fabio-celli-and-the-beanstalk). – Fabio Cenni Sep 01 '15 at 23:16
2

Storyboard + prepareForSegue

It can be done with just about no code by adding a separate UIStoryboardSegue with its own identifier for the button in the Storyboard.

enter image description here

prepareForSegue becomes:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if "fromButtonToViewController" == segue.identifier {
        if let button = sender as? UIButton {
            // ... The UIButton is the sender
        }
    }
}

This handles both tap on cell and tap on button either jointly or separately, passes the proper sender to prepare:segue:sender, thus allowing customization of the cell, the button, the transition, and ultimately the target view. The demonstration of this statement can be found in the compact project below.


► Find this solution on GitHub and additional details on Swift Recipes.

Community
  • 1
  • 1
SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
1

For those who are looking for a generic approach.

/* Generic function to get uitableviewcell from any UIKit controllers which stored in deep level of views or stackviews */

func getCell<T>(_ view: T) -> UITableViewCell? {

    guard let view = view as? UIView else {
        return nil
    }

    return view as? UITableViewCell ?? getCell(view.superview)
}


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
    if segue.identifier == "toCoriViewController"
    {
        if let button = sender as? UIButton,
            let cell = getCell(button),
            let indexPath = tableView.indexPath(for: cell)
        {
            let controller = segue.destinationViewController as! CoriViewController
            controller.coriSquadra = DataManager.sharedInstance.arrayCori[indexPath.row]
        }
    }
}
YSaddiq
  • 23
  • 1
  • 7