24

I am using a NIB file to layout a custom table view cell. This cell has a label with outlet called lblName. Adding a UITapGestureRecognizer to this label never fires the associated event. I have userInteractionEnabled = YES.

I'm guessing that the problem is that the UILabel is in a TableView and that the table/cell view is intercepting the taps. Can I do something about this?

All I want to do is perform some custom action when a UILabel is pressed! All of the solutions for doing this that I've seen are ridiculous. It should be easy using the standard tool set. But evidently not.

Here's the code I'm using:

- (void)tapAction {
    NSLog(@"Tap action");
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib

    UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)]; 
    [recognizer setNumberOfTapsRequired:1];
    //lblName.userInteractionEnabled = true;  (setting this in Interface Builder)
    [lblName addGestureRecognizer:recognizer];
}
Bryan
  • 8,748
  • 7
  • 41
  • 62

10 Answers10

23

EASY WAY:

You may also use a invisible button on the top of that label. So it will reduce your work of adding tapGesture for that label.

ALTERNATIVE WAY:

You should not create an IBOutlet for that UILabel. When you do that,you will add a outlet in custom class implementation file. You cannot access in other file. So set a tag for that label in custom class IB and write a code in cellForRowAtIndexPath: method.

UPDATED:

In cellForRowAtIndexPath: method,

for(UIView *view in cell.contentViews.subviews) {
    if(view.tag == 1) {
        UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
        [tap setNumberOfTapsRequired:1];
        [view addGestureRecognizer:tap];
    }
}
FreeAsInBeer
  • 12,937
  • 5
  • 50
  • 82
Dinesh Raja
  • 8,501
  • 5
  • 42
  • 81
  • Thank you, this did the trick! Fix a couple typos in your code though: first "if" should be a "for" of course and then you want to enumerate cell.contentViews.subviews. Thanks for the answer! – Bryan Apr 10 '12 at 19:11
  • Oh.I have typed myself here.So little mistakes are there.anyway i will update it. – Dinesh Raja Apr 11 '12 at 04:41
  • 4
    This didn't work for me until I've set "User Interaction Enabled" on the cell itself – Nemanja Kovacevic Aug 05 '14 at 15:16
  • There is a issues with these solution, What happen when reusing this particular cell ? – Ratul Sharker Sep 04 '15 at 17:34
  • "The easy way" is actually the coolest, at least in my case, as I want to make the click area wider and with a different shape than the label. Thumbs up. – Eir Jan 20 '17 at 05:56
14
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)]; 
[recognizer setNumberOfTapsRequired:1];
lblName.userInteractionEnabled = YES;  
[lblName addGestureRecognizer:recognizer];
pkamb
  • 33,281
  • 23
  • 160
  • 191
sarit bahuguna
  • 875
  • 8
  • 8
  • 1
    It is required to set the userInteractionEnabledProperty to YES. In my case, the label was inside another view, and without this, the gesture didn't work. – alcamla Dec 17 '15 at 23:14
9

Doing this works without problems:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
   ...
   // create you cell
   UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
   [lbl setText:@"example"];
   [lbl setUserInteractionEnabled:YES];
   [cell.contentView addSubview:lbl];
   UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self    action:@selector(tapAction:)];
   tap.tag = [NSIndexPath row];
   [tap setNumberOfTapsRequired:1];
   [lbl addGestureRecognizer:tap];
   ... 
}

- (void)tapAction:(id)sender {
  switch(((UITapGestureRecognizer *)sender).view.tag) {
     case 0:
          // code
          break;
     case 1:
         // code
         break;
      ....
     }
}

even in the case in which creates the UILabel with IB

Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
WhiteTiger
  • 1,691
  • 1
  • 18
  • 23
  • This will help him. But he needs to create a label inside the UITableViewCell with Tap gesture. – Dinesh Raja Apr 10 '12 at 10:34
  • then in the method of creating the cells, when you create a UITapGestureRecognizer, give it a tag and manage the event in the handler for differentiating tag. – WhiteTiger Apr 10 '12 at 12:18
  • in cellForRowAtIndexPath: method. ... UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)]; tap.tag = [NSIndexPath row]; ... handler method: - (void)tapAction:(id)sender { switch(((UITapGestureRecognizer *)sender).tag) { case 0:NSLog(@"Tap action 1"); break; case 1:NSLog(@"tap action 2");break; ..... } – WhiteTiger Apr 10 '12 at 13:18
  • What is the use of adding tag to a gesture.It's not a big deal.And you have to add your label as a subview of cell.Not to parent view. – Dinesh Raja Apr 10 '12 at 13:21
  • 2
    If I understand it, you want at the click of a cell is performed a certain action, make a tap right? So an idea might be to set for each cell (Label inside of it) an event of tapGesture, to assign a tag that corresponds to cell number, so when you click on your label, you know exactly which cell you basandoti clicking on tags. Then you can possibly manage it in ways more complex, but this is still a starting point for your problem. – WhiteTiger Apr 10 '12 at 13:41
  • Its a valid point..If you could edit your answer and added your whole code,then i will definitely upvote yours:-) – Dinesh Raja Apr 10 '12 at 14:06
  • Pls make "lbl" to subView of "cell" not to "self.view" – Dinesh Raja Apr 11 '12 at 04:44
7

You can use below code to add tap gesture on UILable in UITableView cell

UITapGestureRecognizer *tapGeature = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(lblClick:)];
tapGeature.delegate =self;
tapGeature.numberOfTapsRequired = 1;

cell.lbl.userInteractionEnabled = YES;
[cell.lbl addGestureRecognizer:tapGeature];

and to access the selector method

- (void)lblClick:(UITapGestureRecognizer *)tapGesture {
    UILabel *label = (UILabel *)tapGesture.view;
    NSLog(@"Lable tag is ::%ld",(long)label.tag);
}

For Swift

let tapGesture : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(lblClick(tapGesture:)))
tapGesture.delegate = self
tapGesture.numberOfTapsRequired = 1
cell.lbl.userInteractionEnabled = true
cell.lbl.tag = indexPath.row
cell.lbl.addGestureRecognizer(tapGesture)

func lblClick(tapGesture:UITapGestureRecognizer){
   print("Lable tag is:\(tapGesture.view!.tag)")
}
Hardik Thakkar
  • 15,269
  • 2
  • 94
  • 81
4

Update for 2019 with Swift 5, based upon Hardik Thakkar's solution. To detect taps on a UIlabel in a cell, find the cellForRowAt method in your view controller below.

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

Place the following code in to the method above before returning the cell:

let tapGesture : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(labelTap(tapGesture:)))
tapGesture.delegate = self
tapGesture.numberOfTapsRequired = 1
cell.yourLabel.isUserInteractionEnabled = true
cell.yourLabel.tag = indexPath.row
cell.yourLabel.addGestureRecognizer(tapGesture)            
return cell

Add a method to your view controller to handle the taps:

@objc func labelTap(tapGesture:UITapGestureRecognizer){
    print("Label tag is:\(tapGesture.view!.tag)")
}
mobileappz
  • 59
  • 7
1

You can add next in your cell's -awakeFromNib method

UITapGestureRecognizer* gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureRecognizerAction:)];
[self.yourLabel setUserInteractionEnabled:YES];
[self.yourLabel addGestureRecognizer:gesture];
landonandrey
  • 1,271
  • 1
  • 16
  • 26
1

Once you assign the tap gesture to the UILabel and set the user interaction to enabled, in your callback function you can find the indexpath from the cell view but searching through the nest of superviews:

- (UITableViewCell *) findCellInSuperview:(UIView *)view
{
UITableViewCell *cell = nil;

    NSString *className = NSStringFromClass([[view superview] class]);
    if ([className isEqualToString:@"UITableViewCell"]) {
        cell = (UITableViewCell *)[view superview];
    } else {
        if ([view superview] != nil) {
            cell = [self findCellInSuperview:[view superview]];
        }
    }

return cell;
}
Nike Kov
  • 12,630
  • 8
  • 75
  • 122
adjwilli
  • 9,658
  • 4
  • 35
  • 29
1

For Swift 3

let tapGesture : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: 
#selector(lblClick(tapGesture:)))
tapGesture.delegate = self
tapGesture.numberOfTapsRequired = 1
cell.lbl.isUserInteractionEnabled = true
cell.lbl.tag = indexPath.row
cell.lbl.addGestureRecognizer(tapGesture)

And then

func lblClick(tapGesture:UITapGestureRecognizer){
    print("Lable tag is:\(tapGesture.view!.tag)")
}
fegoulart
  • 1,706
  • 2
  • 12
  • 9
0

The way Dinesh suggested will work without the for loop using the property variable.

UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction)];
[tap setNumberOfTapsRequired:1];
[self.myUILabel addGestureRecognizer:tap];
tobias_k
  • 81,265
  • 12
  • 120
  • 179
Andrespch
  • 370
  • 3
  • 16
0

For Swift, you can add this inside your cellForRowAtIndexPath method.

var tap = UITapGestureRecognizer(target: self, action: "labelTapped")
tap.numberOfTapsRequired = 1
cell.label.addGestureRecognizer(tap)
cell.label.tag = indexPath.row

Then for action

func labelTapped(gesture: UITapGestureRecognizer) {
    let indexPath = NSIndexPath(forRow: gesture.view!.tag, inSection: 0)
    let cell = tableView.cellForRowAtIndexPath(indexPath) as UITableViewCell

    // Do whatever you want.
}
Leo Chan
  • 21
  • 1
  • 1
    The OP asked an Objective-C question, not Swift. – David Hoelzer May 09 '15 at 14:04
  • 1
    What you have will cause a run time error. `action: "labelTapped"` is saying that method expects to be called without any parameters but the method's signature suggests otherwise. – maml Jun 24 '15 at 00:42
  • Table view cells are **reused**; isn't there the risk of adding the gesture recognizer multiple times to the same cell? (same question applies to `addTarget:action:forControlEvents:` in buttons contained within cells). – Nicolas Miari Nov 25 '16 at 05:13