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

前端 未结 30 2608
半阙折子戏
半阙折子戏 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 02:52

    Create the class with the following .h and .m files. In the .m file there is the following function

     - (void)linkAtPoint:(CGPoint)location
    

    Inside this function we will check the ranges of substrings for which we need to give actions. Use your own logic to put your ranges.

    And following is the usage of the subclass

    TaggedLabel *label = [[TaggedLabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    [self.view addSubview:label];
    label.numberOfLines = 0;
    NSMutableAttributedString *attributtedString = [[NSMutableAttributedString alloc] initWithString : @"My name is @jjpp" attributes : @{ NSFontAttributeName : [UIFont systemFontOfSize:10],}];                                                                                                                                                                              
    //Do not forget to add the font attribute.. else it wont work.. it is very important
    [attributtedString addAttribute:NSForegroundColorAttributeName
                            value:[UIColor redColor]
                            range:NSMakeRange(11, 5)];//you can give this range inside the .m function mentioned above
    

    following is the .h file

    #import 
    
    @interface TaggedLabel : UILabel
    
    @property(nonatomic, strong)NSLayoutManager *layoutManager;
    @property(nonatomic, strong)NSTextContainer *textContainer;
    @property(nonatomic, strong)NSTextStorage *textStorage;
    @property(nonatomic, strong)NSArray *tagsArray;
    @property(readwrite, copy) tagTapped nameTagTapped;
    
    @end   
    

    following is the .m file

    #import "TaggedLabel.h"
    @implementation TaggedLabel
    
    - (id)initWithFrame:(CGRect)frame
    {
     self = [super initWithFrame:frame];
     if (self)
     {
      self.userInteractionEnabled = YES;
     }
    return self;
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder
    {
     self = [super initWithCoder:aDecoder];
    if (self)
    {
     self.userInteractionEnabled = YES;
    }
    return self;
    }
    
    - (void)setupTextSystem
    {
     _layoutManager = [[NSLayoutManager alloc] init];
     _textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
     _textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText];
     // Configure layoutManager and textStorage
     [_layoutManager addTextContainer:_textContainer];
     [_textStorage addLayoutManager:_layoutManager];
     // Configure textContainer
     _textContainer.lineFragmentPadding = 0.0;
     _textContainer.lineBreakMode = NSLineBreakByWordWrapping;
     _textContainer.maximumNumberOfLines = 0;
     self.userInteractionEnabled = YES;
     self.textContainer.size = self.bounds.size;
    }
    
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
     if (!_layoutManager)
     {
      [self setupTextSystem];
     }
     // Get the info for the touched link if there is one
     CGPoint touchLocation = [[touches anyObject] locationInView:self];
     [self linkAtPoint:touchLocation];
    }
    
    - (void)linkAtPoint:(CGPoint)location
    {
     // Do nothing if we have no text
     if (_textStorage.string.length == 0)
     {
      return;
     }
     // Work out the offset of the text in the view
     CGPoint textOffset = [self calcGlyphsPositionInView];
     // Get the touch location and use text offset to convert to text cotainer coords
     location.x -= textOffset.x;
     location.y -= textOffset.y;
     NSUInteger touchedChar = [_layoutManager glyphIndexForPoint:location inTextContainer:_textContainer];
     // If the touch is in white space after the last glyph on the line we don't
     // count it as a hit on the text
     NSRange lineRange;
     CGRect lineRect = [_layoutManager lineFragmentUsedRectForGlyphAtIndex:touchedChar effectiveRange:&lineRange];
     if (CGRectContainsPoint(lineRect, location) == NO)
     {
      return;
     }
     // Find the word that was touched and call the detection block
        NSRange range = NSMakeRange(11, 5);//for this example i'm hardcoding the range here. In a real scenario it should be iterated through an array for checking all the ranges
        if ((touchedChar >= range.location) && touchedChar < (range.location + range.length))
        {
         NSLog(@"range-->>%@",self.tagsArray[i][@"range"]);
        }
    }
    
    - (CGPoint)calcGlyphsPositionInView
    {
     CGPoint textOffset = CGPointZero;
     CGRect textBounds = [_layoutManager usedRectForTextContainer:_textContainer];
     textBounds.size.width = ceil(textBounds.size.width);
     textBounds.size.height = ceil(textBounds.size.height);
    
     if (textBounds.size.height < self.bounds.size.height)
     {
      CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0;
      textOffset.y = paddingHeight;
     }
    
     if (textBounds.size.width < self.bounds.size.width)
     {
      CGFloat paddingHeight = (self.bounds.size.width - textBounds.size.width) / 2.0;
      textOffset.x = paddingHeight;
     }
     return textOffset;
     }
    
    @end
    

提交回复
热议问题