Create tap-able “links” in the NSAttributedString of a UILabel?

前端 未结 30 2642
半阙折子戏
半阙折子戏 2020-11-22 02:07

I have been searching this for hours but I\'ve failed. I probably don\'t even know what I should be looking for.

Many applications have text and in this text are web

30条回答
  •  花落未央
    2020-11-22 03:07

    (My answer builds on @NAlexN's excellent answer. I won't duplicate his detailed explanation of each step here.)

    I found it most convenient and straightforward to add support for tap-able UILabel text as a category to UITapGestureRecognizer. (You don't have to use UITextView's data detectors, as some answers suggest.)

    Add the following method to your UITapGestureRecognizer category:

    /**
     Returns YES if the tap gesture was within the specified range of the attributed text of the label.
     */
    - (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange {
        NSParameterAssert(label != nil);
    
        CGSize labelSize = label.bounds.size;
        // create instances of NSLayoutManager, NSTextContainer and NSTextStorage
        NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
        NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
        NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText];
    
        // configure layoutManager and textStorage
        [layoutManager addTextContainer:textContainer];
        [textStorage addLayoutManager:layoutManager];
    
        // configure textContainer for the label
        textContainer.lineFragmentPadding = 0.0;
        textContainer.lineBreakMode = label.lineBreakMode;
        textContainer.maximumNumberOfLines = label.numberOfLines;
        textContainer.size = labelSize;
    
        // find the tapped character location and compare it to the specified range
        CGPoint locationOfTouchInLabel = [self locationInView:label];
        CGRect textBoundingBox = [layoutManager usedRectForTextContainer:textContainer];
        CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
                                                  (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
        CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
                                                             locationOfTouchInLabel.y - textContainerOffset.y);
        NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer
                                                                inTextContainer:textContainer
                                       fractionOfDistanceBetweenInsertionPoints:nil];
        if (NSLocationInRange(indexOfCharacter, targetRange)) {
            return YES;
        } else {
            return NO;
        }
    }
    

    Example Code

    // (in your view controller)    
    // create your label, gesture recognizer, attributed text, and get the range of the "link" in your label
    myLabel.userInteractionEnabled = YES;
    [myLabel addGestureRecognizer:
       [[UITapGestureRecognizer alloc] initWithTarget:self 
                                               action:@selector(handleTapOnLabel:)]]; 
    
    // create your attributed text and keep an ivar of your "link" text range
    NSAttributedString *plainText;
    NSAttributedString *linkText;
    plainText = [[NSMutableAttributedString alloc] initWithString:@"Add label links with UITapGestureRecognizer"
                                                       attributes:nil];
    linkText = [[NSMutableAttributedString alloc] initWithString:@" Learn more..."
                                                      attributes:@{
                                                          NSForegroundColorAttributeName:[UIColor blueColor]
                                                      }];
    NSMutableAttributedString *attrText = [[NSMutableAttributedString alloc] init];
    [attrText appendAttributedString:plainText];
    [attrText appendAttributedString:linkText];
    
    // ivar -- keep track of the target range so you can compare in the callback
    targetRange = NSMakeRange(plainText.length, linkText.length);
    

    Gesture Callback

    // handle the gesture recognizer callback and call the category method
    - (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture {
        BOOL didTapLink = [tapGesture didTapAttributedTextInLabel:myLabel
                                                inRange:targetRange];
        NSLog(@"didTapLink: %d", didTapLink);
    
    }
    

提交回复
热议问题