Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8856959
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 14, 20262026-06-14T14:28:37+00:00 2026-06-14T14:28:37+00:00

EDIT: I’m thinking I should use a UILabel instead of a UITextView as I

  • 0

EDIT: I’m thinking I should use a UILabel instead of a UITextView as I don’t want the highlight to use the system wide blue with ‘copy/select all/define’ popover.

I’m trying to build a custom text view, and I’m looking for some help with the right approach. I’m an iOS n00b so mainly looking for ideas about how best to approach the problem.

I want to build a custom text view, with the following behaviour:

  1. View starts off small. If it receives a tap it grows (animated) to the size of the parent view as a modal window (top left picture, then top right picture)
  2. In this large state, if an individual word is tapped, the word highlights in some fashion, and a delegate method is called passing a string containing the that was tapped (bottom left picture)

New Text View
(source: telliott.net)

I suspect the complexity is going to be in the identifying the word that was clicked, so let’s start there. I can think of a couple of ways of trying this:

  1. Subclass UILabel. Add a touch gesture recognizer. When a touch is identified, get the (x.y) coordinates of the touch point somehow, look at the string the view is showing and figure out which word must have been pressed based on position. I can see this getting very complicated pretty quickly with word wrapping however. I’m not sure how to get the (x,y) coordinates of the touch (although I assume that’s pretty simple), or how to get device dependant text widths for each character etc. I’d rather not go down this route unless someone can convince me it’s not going to be horrible!
  2. Subclass UIView, and fake the sentence by adding a UILabel for each word. Measure the width of each UILabel and lay them out myself. This seems like a more sensible approach, although I worry that laying out the text in this way is also going to be harder than I think by the time I start trying.

Is there a more sensible way? Can you retrieve a word that was touched in a UILabel some other way?

If I go for option 2, then I think the animation from small -> large text might be complicated, as the words will wrap in interesting ways. So I was thinking of having another subview – this time a UITextView – to hold the sentence in the small state. Animate this to large, then hide it, and at the same time reveal my UIView with one-view-per-word.

Any thoughts appreciated. Thanks in advance 🙂

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-14T14:28:38+00:00Added an answer on June 14, 2026 at 2:28 pm

    Update

    This got a lot easier for iOS 7, thanks to the addition of NSLayoutManager in CoreText. If you’re dealing with a UITextView you can access the layout manager as a property of the view. In my case I wanted to stick with a UILabel, so you have to create a layout manager with the same size, i.e:

    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:labelText];
    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];
    CGRect bounds = label.bounds;
    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:bounds.size];
    [layoutManager addTextContainer:textContainer];
    

    Now you just need to find the index of the character that was clicked, which is simple!

    NSUInteger characterIndex = [layoutManager characterIndexForPoint:location
                                                      inTextContainer:textContainer
                             fractionOfDistanceBetweenInsertionPoints:NULL];
    

    Which makes it trivial to find the word itself:

    if (characterIndex < textStorage.length) {
      [labelText.string enumerateSubstringsInRange:NSMakeRange(0, textStorage.length)
                                           options:NSStringEnumerationByWords
                                        usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                                          if (NSLocationInRange(characterIndex, enclosingRange)) {
                                            // Do your thing with the word, at range 'enclosingRange'
                                            *stop = YES;
                                          }
                                        }];
    }
    

    Original Answer, which works for iOS < 7

    Thanks to @JP Hribovsek for some tips getting this working, I managed to solve this well enough for my purposes. It feels a little hacky, and likely wouldn’t work too well for large bodies of text, but for paragraphs at a time (which is what I need) it’s fine.

    I created a simple UILabel subclass that allows me to set the inset value:

    #import "WWLabel.h"
    
    #define WWLabelDefaultInset 5
    
    @implementation WWLabel
    
    @synthesize topInset, leftInset, bottomInset, rightInset;
    
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            self.topInset = WWLabelDefaultInset;
            self.bottomInset = WWLabelDefaultInset;
            self.rightInset = WWLabelDefaultInset;
            self.leftInset = WWLabelDefaultInset;
        }
        return self;
    }
    
    - (void)drawTextInRect:(CGRect)rect
    {
        UIEdgeInsets insets = {self.topInset, self.leftInset,
            self.bottomInset, self.rightInset};
    
        return [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
    }
    

    Then I created a UIView subclass that contained my custom label, and on tap constructed the size of the text for each word in the label, until the size exceeded that of the tap location – this is the word that was tapped. It’s not prefect, but works well enough for now.

    I then used a simple NSAttributedString to highlight the text:

    #import "WWPhoneticTextView.h"
    #import "WWLabel.h"
    
    #define WWPhoneticTextViewInset 5
    #define WWPhoneticTextViewDefaultColor [UIColor blackColor]
    #define WWPhoneticTextViewHighlightColor [UIColor yellowColor]
    
    #define UILabelMagicTopMargin 5
    #define UILabelMagicLeftMargin -5
    
    @implementation WWPhoneticTextView {
        WWLabel *label;
        NSMutableAttributedString *labelText;
        NSRange tappedRange;
    }
    
    // ... skipped init methods, very simple, just call through to configureView
    
    - (void)configureView
    {
        if(!label) {
            tappedRange.location = NSNotFound;
            tappedRange.length = 0;
    
            label = [[WWLabel alloc] initWithFrame:[self bounds]];
            [label setLineBreakMode:NSLineBreakByWordWrapping];
            [label setNumberOfLines:0];
            [label setBackgroundColor:[UIColor clearColor]];
            [label setTopInset:WWPhoneticTextViewInset];
            [label setLeftInset:WWPhoneticTextViewInset];
            [label setBottomInset:WWPhoneticTextViewInset];
            [label setRightInset:WWPhoneticTextViewInset];
    
            [self addSubview:label];
        }
    
    
        // Setup tap handling
        UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc]
                                                   initWithTarget:self action:@selector(handleSingleTap:)];
        singleFingerTap.numberOfTapsRequired = 1;
        [self addGestureRecognizer:singleFingerTap];
    }
    
    - (void)setText:(NSString *)text
    {
        labelText = [[NSMutableAttributedString alloc] initWithString:text];
        [label setAttributedText:labelText];
    }
    
    - (void)handleSingleTap:(UITapGestureRecognizer *)sender
    {
        if (sender.state == UIGestureRecognizerStateEnded)
        {
            // Get the location of the tap, and normalise for the text view (no margins)
            CGPoint tapPoint = [sender locationInView:sender.view];
            tapPoint.x = tapPoint.x - WWPhoneticTextViewInset - UILabelMagicLeftMargin;
            tapPoint.y = tapPoint.y - WWPhoneticTextViewInset - UILabelMagicTopMargin;
    
            // Iterate over each word, and check if the word contains the tap point in the correct line
            __block NSString *partialString = @"";
            __block NSString *lineString = @"";
            __block int currentLineHeight = label.font.pointSize;
            [label.text enumerateSubstringsInRange:NSMakeRange(0, [label.text length]) options:NSStringEnumerationByWords usingBlock:^(NSString* word, NSRange wordRange, NSRange enclosingRange, BOOL* stop){
    
                CGSize sizeForText = CGSizeMake(label.frame.size.width-2*WWPhoneticTextViewInset, label.frame.size.height-2*WWPhoneticTextViewInset);
                partialString = [NSString stringWithFormat:@"%@ %@", partialString, word];
    
                // Find the size of the partial string, and stop if we've hit the word
                CGSize partialStringSize  = [partialString sizeWithFont:label.font constrainedToSize:sizeForText lineBreakMode:label.lineBreakMode];
    
                if (partialStringSize.height > currentLineHeight) {
                    // Text wrapped to new line
                    currentLineHeight = partialStringSize.height;
                    lineString = @"";
                }
                lineString = [NSString stringWithFormat:@"%@ %@", lineString, word];
    
                CGSize lineStringSize  = [lineString sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode];
                lineStringSize.width = lineStringSize.width + WWPhoneticTextViewInset;
    
                if (tapPoint.x < lineStringSize.width && tapPoint.y > (partialStringSize.height-label.font.pointSize) && tapPoint.y < partialStringSize.height) {
                    NSLog(@"Tapped word %@", word);
                    if (tappedRange.location != NSNotFound) {
                        [labelText addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:tappedRange];
                    }
    
                    tappedRange = wordRange;
                    [labelText addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:tappedRange];
                    [label setAttributedText:labelText];
                    *stop = YES;
                }
            }];        
        }
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

EDIT: Simple version of the question: I want to create server variables in the
EDIT I don't know is it important, but destination triangle angles may be different
Edit : Though I've accepted David's answer, Jon's answer should be considered as well.
EDIT: Added debugging output with memory locations as suggested by PlasmaHH. I don't understand
Edit ok, great feedback here, got me pointed in the right direction. Use case
EDIT: Sorry, I should've searched more before I asked, because I've found a solution
EDIT I think pretty-mode is conceptually closer to what I want than the various
EDIT: I simplified the example to use text inputs. Same error. Form fails to
[Edit: don't try to understand the whole thing and don't waste your time to
Edit: Rewritting question I use the Project Management Library from http://dlhsoft.com/Home.aspx in my WPF

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.