1
votes

You're probably asking yourselves what I mean by "conditionally tappable":

I have an UITableView with some generated cells in it. The cells have some images and labels on them. Wherever I tap on the cell area (on the cell itself, or on the image or the label), it takes me to a CellDetailViewController - ref. image.

Now, in the navigation bar I want to have a "Filter" button, that when tapped (or rather triggered - tap to on, tap to off), makes the UI elements on those cells perform an element-specific action when tapped, instead of segueing to the ViewController as they would normally do. I plan on making an action that takes the element and filters the table to show just the cells containing a particular piece of data, according to what the element shown - that means I need to know exactly what element was tapped in what cell (best case is it gets passed as a sender in an argument so I can access its properties).

What I initially though of was I can make the UI elements always call the action, that will check if the Filter button was tapped and then either segue to the ViewController (if it was not), or perform the Filter action (if it was). The problem with this approach is that it seems really clunky and slow-ish and I don't really want to reinvent the wheel if there exists a better solution.

So, the question is - is there another way to accomplish this? Make UI elements clickable on demand?

E: Added more info about the question.

2
I think the simplest way would be to use a class property that would track the state of the filter, then in your didSelectRowAtIndexPath you can check the state and do what you want based on it. - Fred Faust
Could you show some code? - Ducky
@Ducky Code? Do you have any particular thing on mind? - Eugleo
@thefredelement Well, I fear that's not possible, as I need to know exactly what element was tapped, not just the row of the cell. So, like firstLabel in row 20, or rankImage on row 15. - Eugleo
I would like to see your code because only with the given information, anybody could come up with the same similar obvious answer... - Ducky

2 Answers

1
votes

I recommend you to use UIGestureRecognizer to achieve what you want because UIImageViews and UILabel don't respond to user's touch event by default.

Here is some code you may want to look at ( It's a working example)

protocol TableViewCellDelegate: class {
    func labelTouchedInCell(cell: TableViewCell)
    func imagedTouchedInCell(cell: TableViewCell)
}

class TableViewCell: UITableViewCell {
    // You can drag & drop a label and an imageView to the prototype cell and create these IBOutlets
    @IBOutlet weak var label: UILabel! {
        didSet {
            label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "_labelTouched"))
            label.userInteractionEnabled = true
        }
    }
    @IBOutlet weak var cardImage: UIImageView! {
        didSet {
            cardImage.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "_imageTouched"))
            cardImage.userInteractionEnabled = true
        }
    }

    var delegate: TableViewCellDelegate?

    func _labelTouched() {
        delegate?.labelTouchedInCell(self)
    }

    func _imageTouched() {
        delegate?.imagedTouchedInCell(self)
    }
}

class TableViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.rowHeight = 80
    }

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

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! TableViewCell
        cell.delegate = self
        return cell
    }
}

extension TableViewController: TableViewCellDelegate {
    func labelTouchedInCell(cell: TableViewCell) {
        let indexPath = tableView.indexPathForCell(cell)!
        print("Touched label in cell at row \(indexPath.row)")
    }

    func imagedTouchedInCell(cell: TableViewCell) {
        let indexPath = tableView.indexPathForCell(cell)!
        print("Touched image in cell at row \(indexPath.row)")
    }
}

If you only want to handle the tap when certain conditions are met, then check it in the delegate method

0
votes

There is a little tricky an hacky way of doing this. I remember doing something similar for one of my project requirement. Try below code. I just quickly wrote it, didn't test so please bear with me.

// Set the tag of the imageview/label to be equal to the row number
cell.imageView.tag = indexPath.row;
cell.label.tag = indexPath.row;


// Sets up taprecognizer for each imageview
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnImageView:)];
[cell.imageView addGestureRecognizer:tap];

// Sets up taprecognizer for each label
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)];
[cell.label addGestureRecognizer:tap];

// Enable the image/label to be clicked
cell.imageView.userInteractionEnabled = YES;
cell.label.userInteractionEnabled = YES;



- (void)handleTapOnImageView:(UITapGestureRecognizer *)recognizer
{
    // Do your handling
    //recognizer.view.tag
}

- (void)handleTapOnLabel:(UITapGestureRecognizer *)recognizer
{

}