106

Ok, so I have a UILabel created in interface builder that displays some some default text of "tap to begin".

When the user taps the UILabel I want it to trigger an IBAction method: -(IBAction)next; which updates the text on the label to say something new.
It would be really convenient if this allowed me to simply drag a connection from my method to my label and then select touch up inside, as with a button. but alas, no cigar.

so anyways, I guess my question is, am I going to have to subclass UILabel to get this to work? Or is there some way I can drag a button over the label, but make it 0% opaque. Or is there a simpler solution I'm missing?

TheNeil
  • 3,321
  • 2
  • 27
  • 52
wfbarksdale
  • 7,498
  • 15
  • 65
  • 88

4 Answers4

257

Check it out:

UILabel *label = ...
label.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture =
      [[UITapGestureRecognizer alloc] initWithTarget:self 
                                              action:@selector(labelTap)];
[label addGestureRecognizer:tapGesture];

The trick is to enable user interaction.

Alex Cio
  • 6,014
  • 5
  • 44
  • 74
Scott Persinger
  • 3,554
  • 2
  • 20
  • 12
  • 1
    What if I have multiple labels? how might I differentiate which one was tapped? – learner Jul 25 '14 at 22:47
  • 1
    @learner try the tag property. make sure to use labelTap: instead of labelTap. and use - (void) labelTap:(id)sender;. – thedjaney Jul 28 '14 at 09:43
  • 1
    @thedjaney The sender is the UITapGestureRecognizer, not the UILabel – h4labs Feb 10 '15 at 04:00
  • TapGestureRecognizer is not the same as touch up inside, and loses human interface conformance and accessibility. –  Jun 23 '16 at 01:09
  • 1
    @h4labs you can use UITapGestureRecognizer *tapGesture = sender; then get the label tag with tapGesture.view.tag – DJTano Jul 18 '16 at 17:23
70

UILabel inherits from UIView which inherits from UIResponder. All UIresponder objects can handle touch events. So in your class file which knows about your view (which contains the UIlabel) implement:

-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;

In interface builder set the UILabel's tag value. when touches occur in your touchesBegan method, check the tag value of the view to which the tag belongs:

UITouch *touch = [touches anyObject];

if(touch.view.tag == MY_TAG_VAL)
label.text = @"new text";

You connect your code in your class file with the UILabel object in interface builder by declaring your UILabel instance variable with the IBOutlet prefix:

IBOutlet UILabel *label;

Then in interface builder you can connect them up.

barfoon
  • 27,481
  • 26
  • 92
  • 138
Remover
  • 1,616
  • 1
  • 17
  • 27
  • 31
    One note, you have to make sure you check "User Interaction Enabled" in interface builder, or this won't work. – midas06 Jul 23 '10 at 04:18
  • I hit one problem with this method. It worked fine when the view controller was loaded directly, but when i created a nav controller, pushed the controller containing the label to the nav controller and then displaying it, the touches don't seem to get to the label anymore. Any Ideas? – midas06 Jul 23 '10 at 14:03
  • if it's the same view controller class that you're pushing it should work. sounds like there may be some other little problem. maybe start another question and post some code – Remover Jul 23 '10 at 15:04
  • 5
    A similar method that doesn't require tags is to simply check if touch.view is equal to the outlet you set for the label. I prefer this, as it means I don't have to keep track of the tags. – Defragged Oct 28 '11 at 14:13
  • great solution. I recommend that you don't set any tag to be 0 because clicking anywhere else has a tag value of 0. This is because your view will most likely have a tag value of 0. Otherwise, you can set the tag of your view to something like -1 and then you can use the tag value of 0 somewhere else i.e. a button – kevinl Aug 07 '13 at 20:17
31

You can use a UIButton, make it transparent, i.e. custom type without an image, and add a UILabel on it (centered). Then wire up the normal button events.

Eiko
  • 25,601
  • 15
  • 56
  • 71
  • I agree, the custom button type should give you a UILabel type user experience. – stitz Jul 03 '10 at 05:36
  • 2
    +1, nice and simple. I had tried this before by setting a normal button to opactiy 0 which did not work, but the tip to change the type to custom worked perfectly. – Brian Moeskau Aug 12 '10 at 06:22
  • The opacity will effect the button and all of its subviews, so opacity=0 will make the whole thing invisible. – Eiko Aug 14 '10 at 10:58
  • 10
    Yikes. This is a hack compared to the other solutions in this thread. Use a tap gesture recognizer on a label if you want to recognize a tap gesture on a label. (See how it reads like it was meant for this?) – James Boutcher Jan 23 '13 at 14:15
  • This is a far, far better solution because buttons handle touchupinside rather than just taps (begin). –  Jun 23 '16 at 01:13
  • @JamesBoutcher Look at the date of this question. UIGestureRecognizer was brand new (on the iPhone a few days). If you wanted to target anything older, it was not available. Today, I consider it a valid alternative. Just use the method that shows the intents best - if it really behaves like a button, use a button; if it's just some "text with a custom action not meant to be clicked regularly" take a gesture recognizer. – Eiko Jun 23 '16 at 07:05
22

Swift 3

You have an IBOutlet

@IBOutlet var label: UILabel!

In which you enable user interaction and add a gesture recognizer

label.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(userDidTapLabel(tapGestureRecognizer:)))
label.addGestureRecognizer(tapGesture)

And finally, handle the tap

func userDidTapLabel(tapGestureRecognizer: UITapGestureRecognizer) {
  // Your code goes here
}
César Cruz
  • 464
  • 4
  • 5