Getting the word touched in a UILabel/UITextView
Asked Answered
M

3

5

What I'd like to do is to create a text-container component that is able to indicate what is the nearest word when there's is a touch on it (i.e. the word "behind" the touched point).

First, I created a UILabel subclass and overrode the touchesEnded:withEvent: method to determine the touched CGPoint. I also wrote a method that compute the corresponding "frame" (CGRect) of each word of the text using sizeWithFont:forWidth:lineBreakMode:. With the touched CGPoint and this frame, I can determine which word is actually being touched. But the method that compute the frame only works with mono-line text.

So now I need to know which part of the text is on a given line (i.e. how the text has been splited), so that I can figure the correct left-margin and top-margin of each word.

Any idea how I can obtain this? Or maybe have you a more straightforward solution to achieve this? This post was unfortunately not very helpful......

Mossbunker answered 10/1, 2012 at 23:10 Comment(0)
J
9

(There is a link to a sample code project in your linked post that does contain some useful sample code, but I will outline the process for you here too.)

In short, you are going to need to use Core Text, which is Apple's advanced C-based text handling framework that backs all sophisticated text layout in iOS and OS X.

The full code is going to be somewhat involved, but key methods you are going to want to look at are:

CTFramesetterCreateWithAttributedString() - use this, in conjunction with an NSAttributedString that you will get from your label's text - to create the framesetter

CTFramesetterCreateFrame() - use this to get a CTFrameRef for your text from the above framesetter. You will need to create a CGPathRef using your label bounds to do this.

CTFrameGetLines(), CTFrameGetLineOrigins() - use these to get CTLineRefs corresponding to the typeset lines, and the coordinates of the line origins, respectively, then use CTLineGetStringIndexForPosition() to find the character index at a touch location.

You can then use this character index (in the line's reference frame) to work backward and find the actual character/word/etc within your full string.

Don't forget that matters are complicated by a couple issues:

  1. If you use UILabel's native drawing you will have to take care to perfectly match your typesetting metrics, which can be cumbersome since most of the objects (e.g. CTFontRef) are not toll-free bridged with their UIKit counterparts. Implementing your own drawing may, sometimes, be easier, which will guarantee metric matching.

  2. Core Text uses an inverted coordinate system with respect to the normal iOS drawing system. If you are getting wacky results, and especially if you do your own drawing, this is something to take a look at.

Not the easiest task in the world, but far from impossible. Good luck!

Josettejosey answered 10/1, 2012 at 23:38 Comment(1)
Actually, drawing the text myself is what I would like to avoid since it seems to be a bit of an overhead considering the initial need. I will consider the CoreText keys-functions you gave, thank you!Mossbunker
P
4

After searching a bit I have written a code for this. Code will work for UITextView and UILabel.
Just download VSWordDetector.zip then unzip it and drag & drop the folder VSWordDetector into your project.
There are two files VSWordDetector.h and VSWordDetector.m
#import VSWordDetector.h in your ViewController class.

@property (strong, nonatomic) VSWordDetector *wordDetector;

Note: Make its property don't take as locally.

Now its ready for you, Just add it on textViews or Labels

-(void)viewDidLoad
{
   self.wordDetector = [[VSWordDetector alloc] initWithDelegate:self];
   [self.wordDetector addOnView:textView];
   [self.wordDetector addOnView:label];
}

-(void)wordDetector:(VSWordDetector *)wordDetector detectWord:(NSString *)word
{
   NSLog(@"Detected Word: %@", word);
}

This delegate method will be call when you will tap on connected label or textView.

EDIT: Sample Code for VSWordDetector.

SUGGESTED EDIT: If you don't want to download the sample code here is the code for both file. Please see this gist file VSWordDetector.

Pleurodynia answered 5/2, 2014 at 12:51 Comment(9)
hi . I tried to use your code , but the tap is not detected. i followed the steps u have given , can you please tell me where im going wrong?Vania
@NijilNair Sorry but I can not say where you are doing wrong. So I made a sample please see Edit part of my answer.Pleurodynia
I am not a big fun of zipped code. Add the relevant parts of VSWordDetector here or paste the code on github.Vogt
@Vogt - The zipped code is separate code. I made sample which will help to freshers. And down vote only when if my code is not working and it has some problem.Pleurodynia
@Vogt - As I have already said the freshers are unable to get explanation in some cases. I don't enjoy making sample and provide to others but for this I had to made it to make people understood.Pleurodynia
@Vogt - I didn't paste the whole code because it is too long.Pleurodynia
I found it quite unfair that this answer had a score of -1 before I upvoted it. It answers the question perfectly and really works well. However, I agree with properly formatting Stackoverflow. So it'd be good if @Pleurodynia could reformat in the way that commenters above have suggested so this answer can get a score that it deserves.Sandstone
@ChrisHarrison - Thanks... Actually the reason for giving the sample link is the code is too long to paste here. If I do so it will make it long scroller for visitor. And there are many answers who just give the link of sample code for other's code even I gave link for my own sample code. Anyway I have given both .h and .m file as a text as you suggested.Pleurodynia
This is really helpful.. However, since this approach assumes the text is left aligned, if the text is centered in the label, it works wrong :(Resuscitator
M
2

Though this is an old question, but i thought this might be of some help.

Since learning core text was huge, i opted for GLTapDemo by German Laullon

With a few changes , this works great for detecting touched word on UILabel.

Muse answered 8/8, 2013 at 11:37 Comment(1)
I tried both the VSWordDetector solution and this one, and found this one to work more effectively for my use case.Claudineclaudio

© 2022 - 2024 — McMap. All rights reserved.