Reputation: 727
This question might have asked few times, but I'm still struggling to find best solution. addAttributes with NSMutableAttributedString won't work as I only can give a link to a part of the string.
I have a label with string "Already sign up? Login here". I can get NSRange of text "Login" but how can I give UITapGesture to it? I don't want to use 3rd party library here. Please suggest me some ideas. Thanks
Upvotes: 0
Views: 1189
Reputation: 11233
You can't add tap gesture to a specific part of UILabel or if you tried to do so, you will stuck in finding frames and part of label that might help you at this point but it will be more problematic if it is inside some other view like UITableViewCell etc. It may require some calculation for point conversion with respect to parent/child view.
To keep it simple, my suggestion would be to create a UIView
with a UILabel
and UIButton
embedded in it and button is set next to UILabel.
An example:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Create label
UILabel *lbl = [[UILabel alloc] init];
[lbl setText:@"Already Sign up ? "];
[lbl sizeToFit];
// Create button
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setTitle:@"Login Here" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(didTapLink:) forControlEvents:UIControlEventTouchUpInside];
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btn sizeToFit];
[btn setFrame:CGRectMake(lbl.frame.size.width, lbl.frame.origin.y, btn.frame.size.width, lbl.frame.size.height)];
// Create container that embeds the label and button
UIView *customLabel = [[UIView alloc] initWithFrame:CGRectMake(0, 50, lbl.frame.size.width + btn.frame.size.width, lbl.frame.size.height)];
[customLabel addSubview:lbl];
[customLabel addSubview:btn];
// Add this custom label to UI
[self.view addSubview:customLabel];
}
-(void)didTapLink:(id)sender
{
NSLog(@"Button tapped");
}
Screenshot:
Hope that helps!
Upvotes: 0
Reputation: 10102
Gesture recognizers can't be added to specific parts of a UI component. However, inside tap handler action, you might want to check
CGPoint tapLocation = [tapRecognizer locationInView:tapRecognizer.view];
You can check if the point lies in your targetRect
if(CGRectContainsPoint(myTargetRect, tapLocation))
{
// Do your action here
}
Hope it helps.
Here's the Objective-C version for getRect() from @Code 's answer as you needed.
-(CGRect)getRectForString:(NSAttributedString*)str withRange:(NSRange)textRange usingMaxWidth:(CGFloat)maxWidth
{
// Create a layoutManager
NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init];
// Create a textContainer with maxWidth size and add it to layoutManager
NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(maxWidth, CGFLOAT_MAX)];
[layoutManager addTextContainer:textContainer];
textContainer.lineFragmentPadding = 0;
// Create a textStorage with 'str' variable and add the layoutManager to this textStorage
NSTextStorage* textStorage = [[NSTextStorage alloc] initWithAttributedString:str];
[textStorage addLayoutManager:layoutManager];
// Query the actualGlyphRange from layoutManager for specified textRange
NSRange actualGlyphRange;
[layoutManager characterRangeForGlyphRange:textRange actualGlyphRange:&actualGlyphRange];
// Find out the boundingRect for actualGlyphRange
return [layoutManager boundingRectForGlyphRange:actualGlyphRange inTextContainer:textContainer];
}
Upvotes: 0
Reputation: 1523
You cannot add a gesture recognizer to a "part of a UILabel". For your case, I would just use two different UILabels: one that is not-tappable that says "Already signed up?", and one that is tappable that says "Sign in here". Honestly, I'd put the tappable part into a UIButton to make it clear. One more option would be to put an empty UIView as a subview of the UILabel, making it the size of the text you want to tap. Then add a gesture recognizer to THAT subview.
(In my opinion, one of the biggest traps developers get into when working with iOS is trying to force it to act differently than designed; like mimicking a web link or something. It always works best, and with a minimum of code, when written to take advantage of its design patterns)
All of the tap location nonsense can be ignored, because that is the point of a recognizer. The recognizer handles all of that for you, and will call your target with the action you set when the UIView is tapped.
So it would basically be:
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:someObject action:@selector(someMethod:)];
[loginLabel addGestureRecognizer:tapRecognizer];
And that's it. The rest will be handled for you by the system. Your target object will be called when the label is tapped. At least, it seems simple from the problem as described in the original post. If there are other complications, please include those points in your question.
Upvotes: 1