Add a tap gesture to a part of a UILabel
Asked Answered
C

5

6

I have a NSAttributedString like so:

NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:@"testing it out @clickhere"];
NSInteger length = str.length;
[str addAttribute:NSForegroundColorAttributeName value:[UIColor bestTextColor] range:NSMakeRange(0,length)];

The NSMutableAttributedString gets set to a UILabel like so:

label.attributedText = str;

How do I make a tap gesture (or something clickable) to another viewcontroller for the words '@clickhere in the string above?

Thanks!

Cryptic answered 19/1, 2015 at 5:57 Comment(1)
K
5

I think, the best way is adding the UIGestureRecognizer to your UILabel and validate the frame that you would like.

UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[_yourLabel addGestureRecognizer:singleTap];

- (void)handleTap:(UITapGestureRecognizer *)tapRecognizer
{
    CGPoint touchPoint = [tapRecognizer locationInView: _yourLabel];

    //Modify the validFrame that you would like to enable the touch
    //or get the frame from _yourLabel using the NSMutableAttributedString, if possible
    CGRect validFrame = CGRectMake(0, 0, 300, 44);

    if(YES == CGRectContainsPoint(validFrame, touchPoint)
     {
        //Handle here.
     }
}
Kingpin answered 19/1, 2015 at 8:28 Comment(0)
R
1

Simply first add a gesture to your label

[label setUserInteractionEnabled:YES];
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[label addGestureRecognizer:gesture];

control your gesture area in the below method

- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
    static CGRect touchableRect = CGRectMake(100.0f, 0.0f, 100.0f, 50.0f); // Give your rect as you need.
    CGPoint touchPoint = [gestureRecognizer locationInView:self.view];
    if (CGRectContainsPoint(touchableRect, touchPoint))
    {
       //User has tap on where you want. Do your other stuff here
    }
}
Radicand answered 19/1, 2015 at 6:38 Comment(0)
B
0
  UITapGestureRecognizer *Tap = [[UITapGestureRecognizer alloc]  initWithTarget:self action:@selector(tapDetected)];
Tap.numberOfTapsRequired = 1;
// label Name Is your Label Name
[labelName addGestureRecognizer:Tap];
-(void)tapDetected
  {
  //your code
   }
Bush answered 19/1, 2015 at 12:46 Comment(0)
O
0

I'd just like to add on Ramshad's answer, about how to deal with the valid frame.

For this you might want to consider using UITextView instead of UILabel, which doesn't give you access to how it manages layout the text. By disabling editing, selection and scrolling, UITextView behaves roughly the same as a UILabel, except some padding you have to remove.

For convenience, you might want to add a little category to UITextView, in which you write a method to test if a point touches any of the characters in range.

- (BOOL)point:(CGPoint)point touchesSomeCharacterInRange:(NSRange)range
{
    NSRange glyphRange = [self.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL];

    BOOL touches = NO;
    for (NSUInteger index = glyphRange.location; index < glyphRange.location + glyphRange.length; index++) {
        CGRect rectForGlyphInContainer = [self.layoutManager boundingRectForGlyphRange:NSMakeRange(index, 1) inTextContainer:self.textContainer];
        CGRect rectForGlyphInTextView = CGRectOffset(rectForGlyphInContainer, self.textContainerInset.left, self.textContainerInset.top);

        if (CGRectContainsPoint(rectForGlyphInTextView, point)) {
            touches = YES;
            break;
        }
    }

    return touches;
}

This would also work for a fragment of text containing multiple words spreading across multiple lines due to word wrap. It would also work on localized texts as we deal with glyphs that are printed.

Osborn answered 6/11, 2015 at 9:50 Comment(0)
K
0

The idea is the same as the accepted answer. Here is the way in Swift.

  • Suppose you have set up your label ready.

    youLabel.text = "please tab me!"

  • Add tapGestuerRecognizer to your label

    let tap = UITapGestureRecognizer(target: self, action:    #selector(tapAction))
    yourLabel.addGestureRecognizer(tap)
    
  • Add the String extension method to String to calculate the string rect.

    extension String {
        func rect(font: UIFont) -> CGRect {
            let label = UILabel(frame: .zero)
            label.font = font
            label.text = self
            label.sizeToFit()
            return label.frame
        }
    }
    
  • Calculate the available tap rect of the tap gesture.

    let pleaseStringRect = "please".rect(font: yourFont)
    let tapStringRect = "tap".rect(font: yourFont)
    let tapAreaRect = CGRect(x: pleaseStringRect.width, y:   tapStringRect.origin.y, width: tapStringRect.width, height: tapStringRect.height"
    
  • In the action do what you want

     @objc private func tapAction(tap: UITapGestureRecognizer) {
       let position = tap.location(in: content)
       guard reporterFirstNameRect.contains(position) else {
           return
       }
       // Do the tap action stuff
    }
    

    That's it, happy coding.

Kalindi answered 6/3, 2019 at 7:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.