0
votes

I have a prototype cell that has some labels on it and a button (well, its actually an imageView, not a button):

Prototype cell

I want to achieve this behavior:

  1. Tap on the button executes certain code, say println("foo"), but doesn't perform the "show detail" segue
  2. Tap on the rest of the cell performs a show detail segue

If requirement #1 wasn't necessary, I'd do this:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    selectedPlace = places[indexPath.row]
    self.performSegueWithIdentifier("ShowPlaceSegue", sender: self)
}

What is the recommended way to achieve this?
This is not like HTML DOM events? (z-index, etc)

I tried (in a very naif attempt) the following:

class PlaceTableViewCell: UITableViewCell {

    @IBOutlet weak var favoritedImageView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var administrativeAreaLevel3: UILabel!

    func configureCellWith(place: Place) {
       nameLabel.text = place.name
       administrativeAreaLevel3.text = place.administrativeAreaLevel3
       favoritedImageView.addGestureRecognizer(UIGestureRecognizer(target: self, action:Selector("bookmarkTapped:")))
    }

    func bookmarkTapped(imageView: UIImageView) {
       println("foo")
    }
}

But no matter if I click the imageView or the rest of the cell, the "show detail" segue is performed and the "foo" isn't printed.

What do you think of putting a UIView, "v", inside the prototype cell that contains the labels and making "v" tappable? something like this:

UIView inside the table cell prototype

If I do that, will the cell be grayed while tapped? I'd like to keep that...

2

2 Answers

0
votes

Sorry, it was a stupid problem:

The "naif" way was indeed the way to go. Indeed it works like HTML DOM!...

But I changed this:

func configureCellWith(place: Place) {
   nameLabel.text = place.name
   administrativeAreaLevel3.text = place.administrativeAreaLevel3
   favoritedImageView.addGestureRecognizer(UIGestureRecognizer(target: self, action:Selector("bookmarkTapped:")))
}

func bookmarkTapped(imageView: UIImageView) {
   println("foo")
}

For this:

func configureCellWith(place: Place) {
   nameLabel.text = place.name
   administrativeAreaLevel3.text = place.administrativeAreaLevel3

   let gestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("bookmarkTapped:"))
   gestureRecognizer.numberOfTapsRequired = 1

   favoritedImageView.userInteractionEnabled = true
   favoritedImageView.addGestureRecognizer(gestureRecognizer)
}

func bookmarkTapped(sender: UIImageView!) {
   println("foo")
}

As you can see, I was using UIGestureRecognizer instead of UITapGestureRecognizer



EDIT:

So, the above is right, but now I think its better to have the action function in the class that contains the tableView, instead of having the action in the cell class itself.

So, i've moved the addGestureRecognizer to the cellForRowAtIndexPath method, ie:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("PlacePrototype", forIndexPath: indexPath) as! PlaceTableViewCell

    // Configure the cell
    let place = places[indexPath.row]

    cell.configureCellWith(place)

    // HERE!
    let gestureRecognizer = UITapGestureRecognizer(target: self, action:Selector("bookmarkTapped:"))
    gestureRecognizer.numberOfTapsRequired = 1

    cell.favoritedImageView.userInteractionEnabled = true
    cell.favoritedImageView.addGestureRecognizer(gestureRecognizer)

    return cell
}

And the action:

func bookmarkTapped(gestureRecognizer: UIGestureRecognizer) {
    // println("foo")

    var point = gestureRecognizer.locationInView(self.tableView)

    if let indexPath = self.tableView.indexPathForRowAtPoint(point)
    {
        places[indexPath.row].toggleBookmarked()
        self.tableView.reloadData()
    }
}
0
votes

Assuming your tableView can be displayed five cells, then the cellForRow will to be called five times, and you will add an UITapGestureRecognizer to five imageView of different. but when you scrolling to the seventh cell, you will got a reused cell(maybe the first cell) in the cellForRow, the imageView of the cell had an UITapGestureRecognizer, if you add UITapGestureRecognizer to the imageView again will cause you tap once trigger multiple times.

You can try this:

class PlaceTableViewCell: UITableViewCell {

    @IBOutlet weak var favoritedImageView: UIImageView!
    var favoritedTappedBlock: ((Void) -> Void)? // block as callback

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        commonInit()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        commonInit()
    }

    private func commonInit() {
        let gestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("favoritedImageViewTapped"))
        gestureRecognizer.numberOfTapsRequired = 1

        favoritedImageView.addGestureRecognizer(gestureRecognizer)
        favoritedImageView.userInteractionEnabled = true
    }

    private func favoritedImageViewTapped() {
        if let favoritedTappedBlock = self.favoritedTappedBlock {
            favoritedTappedBlock()
        }
    }
}

And cellForRow:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("PlacePrototype", forIndexPath: indexPath) as! PlaceTableViewCell

    // Configure the cell
    let place = places[indexPath.row]

    cell.configureCellWith(place)

    cell.favoritedTappedBlock = {
        println("tapped")
    }

    return cell
}