0

I have a custom cell class given below:

class SizeAndQuantityCellView:UITableViewCell
{

@IBOutlet weak var imageview: UIImageView!
@IBOutlet weak var plusButton4x4: UIButton!
@IBOutlet weak var plusButton4x6: UIButton!
@IBOutlet weak var plusButton5x7: UIButton!
@IBOutlet weak var plusButton8x10: UIButton!
@IBOutlet weak var minusButton4x4: UIButton!
@IBOutlet weak var minusButton4x6: UIButton!
@IBOutlet weak var minusButton5x7: UIButton!
@IBOutlet weak var minusButton8x10: UIButton!
@IBOutlet weak var quantity4x4: UILabel!
@IBOutlet weak var quantity4x6: UILabel!
@IBOutlet weak var quantity5x7: UILabel!
@IBOutlet weak var quantity8x10: UILabel!

let sizeAndQuantityController = SizeAndQuantityController()
@IBAction func plusButtonClick(sender: UIButton)
{
    let btnTag:Int = sender.tag
    let tableView = sender.superview!.superview?.superview as! UITableView
    let cellRow = tableView.indexPathForCell(self)?.row
    sizeAndQuantityController.plusButtonClick(btnTag,cellRow: cellRow!)
}

@IBAction func minusButtonClick(sender: UIButton)
{
    let btnTag:Int = sender.tag
    let tableView = sender.superview!.superview?.superview as! UITableView
    let cellRow = tableView.indexPathForCell(self)?.row
    sizeAndQuantityController.plusButtonClick(btnTag,cellRow: cellRow!)
}
}

What i want to do is when i click the plus button the quantity should increase by one and when i click the minus button it should decrease by one. Here's my controller class for that:

class SizeAndQuantityController
{
func plusButtonClick(tag:Int,cellRow:Int)
{
    switch tag
    {
    case 13:
        let quant = quantity4x4[cellRow]
        quantity4x4[cellRow] = quant+1
        break;
    case 14:
        let quant = quantity4x6[cellRow]
        quantity4x6[cellRow] = quant+1
        break;
    case 15:
        let quant = quantity5x7[cellRow]
        quantity5x7[cellRow] = quant+1
        break;
    case 16:
        let quant = quantity8x10[cellRow]
        quantity8x10[cellRow] = quant+1
        break;
    default:
        break
    }
}

func minusButtonClick(tag:Int,cellRow:Int)
{
    switch tag
    {
    case 17:
        let quant = quantity4x4[cellRow]
        quantity4x4[cellRow] = quant-1
        break;
    case 18:
        let quant = quantity4x6[cellRow]
        quantity4x6[cellRow] = quant-1
        break;
    case 19:
        let quant = quantity5x7[cellRow]
        quantity5x7[cellRow] = quant-1
        break;
    case 20:
        let quant = quantity8x10[cellRow]
        quantity8x10[cellRow] = quant-1
        break;
    default:
        break
    }
}

i have given different tags to all the buttons. when i run the app it gives me the following error: "Could not cast value of type UITableViewWrapperView to UITableView" at the line where i set my tableview.

Ankit0047
  • 144
  • 1
  • 10
  • 2
    doing `sender.superview!.superview?.superview` is a bad idea and also if apple decides to change their implementation in a future update your code will break. you should have an explicit variable for your tableview linked from the storyboard or something – Fonix Nov 14 '16 at 07:38
  • set the tag in cellForRow for buttons as indexPath.row – Rajan Maheshwari Nov 14 '16 at 07:42
  • i have the explicit tableView variable linked from storyboard but its in the ViewController class,but i want to access tableView in the custom UITableViewCell class as given above – Ankit0047 Nov 14 '16 at 07:44
  • @RajanMaheshwari but i have multiple buttons in the cell,and have different functionalities – Ankit0047 Nov 14 '16 at 07:46
  • 1
    Actually I don't understand why the `UITableViewCell` responsibilities should include this. The tableViewController should handle it. – Luca D'Alberti Nov 14 '16 at 07:46
  • if you have multiple buttons, then also you can provide the same tag. Ultimate goal is to find out which indexPath.row was clicked – Rajan Maheshwari Nov 14 '16 at 07:51
  • DUPLICATE of http://stackoverflow.com/questions/28659845/swift-how-to-get-the-indexpath-row-when-a-button-in-a-cell-is-tapped/29792963#29792963 – Jacob King Nov 14 '16 at 08:01

2 Answers2

0

Doing sender.superview!.superview?.superview as! UITableView is very dangerous. In the transition between iOS6 and iOS7, an extra layer was actually introduced and that kind of call failed.

Rather just have a property rowIndex in cell, which you set in your cellForRowAtIndexPath. For Example:

class SizeAndQuantityCellView:UITableViewCell
{
    var rowIndex: Int = 0
    ...
}

In your TableViewController

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
   let cell = tableView.dequeueReusableCell(withIdentifier: "myTableViewCell", for: indexPath) as! SizeAndQuantityCellView
   cell.rowIndex = indexPath.row
   ...
   return cell
}

From your code, it is not clear where quantity4x4[cellRow], for example, fits in but it seems to me that a Delegation Pattern might also be handy. I.o.w. Create a delegate protocol for SizeAndQuantityCellView and let your ViewController be the delegate of SizeAndQuantityCellView. When the buttons is tapped, fire an event to the delegate. That way your ViewController can handle the logic upon the pressing of the buttons.

Carien van Zyl
  • 2,853
  • 22
  • 30
  • thanks, I used your code and was able to get the indexpath row for the different cell where the button was clicked.Will also look into the delegation pattern for my logic. – Ankit0047 Nov 14 '16 at 07:58
0

A more sofisticated approach, involves the use of extensions and bitwise operator. Simplifying, you can use the tag property built-in with every UIButton, to store the whole value of of and IndexPath (that is identified by a row and a section) by packing it using bitwise operators and shifting.

Once the value is stored, you can use the computed property technique by extending your UIButton class and returning a new IndexPath that is created by unpacking the original values.

Below there's a simple extension that do the job:

extension UIButton {
    func packing(low:Int, high:Int) -> Int {
        //With the packing function we force our Packed number to be a 64 bit one
        //we shift the high part 32bits to the left and OR the resulting value with the low part
        return ((high << 32) | low)
    }
    func unpackHigh(packed:Int) -> Int {
        //Unpacking the high part involve swifting to right the 
        //number in order to zero'ing all the non relevant bits.
        return packed >> 32
    }
    func unpackLow(packed:Int) -> Int {
        //Unpacking the low part involve masking the whole packed number with the max value allowed for an Int.
        //note that using the Int.max function does not work as expected, returning a compile error. 
        //Maybe, it's a bug of compiler.
        let mask = 2147483647
        return mask & packed
    }

    //since we cannot use stored property on extensions, we need to compute realtime, every time the
    //right value of our indexPath.
    var indexPath:IndexPath {
        get {
            return IndexPath(row: unpackLow(packed: self.tag), section: unpackHigh(packed: self.tag))
        }
        set {
            self.tag = packing(low: newValue.row, high: newValue.section)
        }
    }
}

and here you can find a simple application on a prototype cellForRowAtIndexPath:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let aCell = tableView.dequeueReusableCell(withIdentifier: "reuseCell") as! CustomTableViewCell

...

aCell.aButton.indexPath = indexPath

...

return aCell

}

note that you need to pass, after the dequeue, the right indexPath to the cell, in order to trigger the extension methods.

valvoline
  • 7,737
  • 3
  • 47
  • 52