Level: ★☆☆ Tag: “UILabel” “TTTAttributedLabel basic use” “TTTAttributedLabel implementation” author: WYW


Introduction: The author recently needs to implement the function of adding click events to links in UILabel. After checking with SO.com, TTTAttributedLabel has a better encapsulation degree. Sort out the basic use of TTTAttributedLabel, and part of the implementation.

Basic use of TTTAttributedLabel

theTTTAttributedLabel.hTTTAttributedLabel.mPut it in the project

Comply with theTTTAttributedLabelDelegateagreement

/ / abide by TTTAttributedLabelDelegate @ interface ViewController () < TTTAttributedLabelDelegate >Copy the code

Create TTTAttributedLabel instance and the corresponding configuration

Create an instance of TTTAttributedLabel and add the appropriate configuration.

- (void)setupTTTAttributedLabel {
    
    TTTAttributedLabel *attriLabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectZero];
    attriLabel.font = [UIFont systemFontOfSize:32.0];
    attriLabel.numberOfLines = 0;
    // Automatically detect links when the label text is subsequently changed
    attriLabel.enabledTextCheckingTypes = NSTextCheckingTypeLink;
    // Delegate methods are called when the user taps on a link (see `TTTAttributedLabelDelegate` protocol)
    attriLabel.delegate = self;
    // Repository URL will be automatically detected and linked
    attriLabel.text = @"Fork me on GitHub! (https://github.com/mattt/TTTAttributedLabel/)";
    NSRange range = [attriLabel.text rangeOfString:@"me"];
    // Embedding a custom link in a substring
    [attriLabel addLinkToURL:[NSURL URLWithString:@"http://github.com/mattt/"] withRange:range];
    [self.view addSubview:attriLabel];
    attriLabel.frame = CGRectMake(20.0.100.0.300.0.200.0);
}
Copy the code

Implement TTTAttributedLabelDelegate proxy method

View the currently clicked link in the following proxy method.

/ /! - (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url {NSLog(@" URL information: %@", url); }Copy the code

TTTAttributedLabel is partially implemented

Set attrilabel. text = @”Fork me on GitHub! (https://github.com/mattt/TTTAttributedLabel/)”; We looked at the implementation flow of TTTAttributedLabel.

Set TTTAttributedLabel to be available for user interaction.

self.userInteractionEnabled = YES;
Copy the code

The TTTAttributedLabel link specifies the default link style

TTTAttributedLabel specifies the default link style for links to be blue and underlined.

The relevant codes are:

NSMutableDictionary *mutableLinkAttributes = [NSMutableDictionary dictionary];
[mutableLinkAttributes setObject:[NSNumber numberWithBool:YES] forKey:(NSString *)kCTUnderlineStyleAttributeName];
if ([NSMutableParagraphStyle class]) {
    [mutableLinkAttributes setObject:[UIColor blueColor] forKey:(NSString *)kCTForegroundColorAttributeName];
} else {
    [mutableLinkAttributes setObject:(__bridge id) [[UIColor blueColor] CGColor] forKey:(NSString *)kCTForegroundColorAttributeName];
}
self.linkAttributes = [NSDictionary dictionaryWithDictionary:mutableLinkAttributes];
Copy the code

useNSDataDetectordetectionlabel.textThe links in the

NSArray *results = [dataDetector matchesInString:[(NSAttributedString *)text string] options:0 range:NSMakeRange(0, [(NSAttributedString *)text length])];
Copy the code

The results array of type NSArray

* will contain the link information for the label text.

Such as

<__NSArrayM 0x2830a6310>(
<NSLinkCheckingResult: 0x283ed27c0>{20, 44}{https://github.com/mattt/TTTAttributedLabel/}
)
Copy the code
// The URL in label.text
(lldb) po ((NSLinkCheckingResult *)[results lastObject]).URL
https://github.com/mattt/TTTAttributedLabel/
Copy the code
// range of urls in label.text
(lldb) po ((NSLinkCheckingResult *)[results lastObject]).range
location=20, length=44
Copy the code

Autodetect link

When we set attrilabel. text = @”Fork me on GitHub! (https://github.com/mattt/TTTAttributedLabel/)”; When will find https://github.com/mattt/TTTAttributedLabel/ to link form automatically. Automatically detect the label. The text of the text in the url information is, in turn, in – (NSArray *) addLinksWithTextCheckingResults (NSArray *) results Attributes :(NSDictionary *)attributes and – (void)addLinks:(NSArray *)links implementation.

  • First through the in – (NSArray *) addLinksWithTextCheckingResults: (NSArray *) results the attributes (NSDictionary *) attributes Method encapsulates link text attribute information medium TTTAttributedLabelLink.

  • – (void)addLinks:(NSArray *)links (TTTAttributedLabelLink) Set label.attributedText to automatically detect the URL in label.text.

In addition, we add addLinkToURl

- (void)addLinks:(NSArray *)links {
    ...
    self.attributedText = mutableAttributedString; . }Copy the code

Adds a link to the specifiedrange

[attriLabel addLinkToURL:[NSURL URLWithString:@”http://github.com/mattt/”] withRange:range]; As an example. You can see that the internal implementation of TTTAttributedLabel is

[self addLinkWithTextCheckingResult:[NSTextCheckingResult linkCheckingResultWithRange:range URL:url]]; until

[self addLinkWithTextCheckingResult:result attributes:self.linkAttributes]; This is the same as the autodetect link above.

Click the link text of Label and find the correspondingurl

So this is basically a ‘touchesBegan’ method for finding the location of the click, ‘touchesMoved’ method for comparing the position of the touch on the Label to see if the current position is the same as the ‘touchesBegan’ method, Finally, in ‘touchesEnded,’ you pass links you click as blocks and proxies.

Here’s a quick note: I looked at the ‘touchesBegan’ method to find links to click on.

  • Gets the current clicked point
[touch locationInView:self]
Copy the code
  • in- (TTTAttributedLabelLink *)linkAtPoint:(CGPoint)point Method to get the current dot link

– (CFIndex)characterIndexAtPoint:(CGPoint)p Then use – (TTTAttributedLabelLink *)linkAtCharacterIndex:(CFIndex)idx to find the TTTAttributedLabelLink instance of the index of the corresponding character (which contains the link information of the current click point)

See TTTAttributedLabel for details

[self linkAtPoint:[touch locationInView:self]];
TTTAttributedLabelLink *result = [self linkAtCharacterIndex:[self characterIndexAtPoint:point]];
Copy the code

Gets the index of the character at the current click position

The difficulty I found was to get the index of the characters at the current click position. Relevant content is as follows:

Convert the coordinate of the current point to the text coordinate of the corresponding UILabel to the point coordinate of the UILabel’s own coordinate system;

p = CGPointMake(p.x - textRect.origin.x, p.y - textRect.origin.y);
Copy the code

Another adjustment is to convert the upper-left corner of iOS to the lower-left corner of THE CT coordinate system.

p = CGPointMake(p.x, textRect.size.height - p.y);
Copy the code
  • Create the frame required by the CT coordinate system based on the current label relative to its frame frame and the attribute string
    CGMutablePathRef path = CGPathCreateMutable(a);CGPathAddRect(path, NULL, textRect);
    CTFrameRef frame = CTFramesetterCreateFrame([self framesetter], CFRangeMake(0, (CFIndex) [self.attributedText length]), path, NULL);
Copy the code
  • Determines the number of lines currently occupied by the UILabel display in the CT coordinate systemCFArrayGetCount(lines)
NSInteger numberOfLines = self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines);
Copy the code
  • Traverse each line of text in CT coordinates, find the line where the current click point is, calculate the coordinates of the click point relative to the current line, and calculate the current index.
CGPoint relativePoint = CGPointMake(p.x - lineOrigin.x, p.y - lineOrigin.y);
idx = CTLineGetStringIndexForPosition(line, relativePoint);
Copy the code

Descent includes ascent (the recommended distance from the peak to the baseline) and descent (the recommended distance from the peak to the baseline). If you’re interested, check out CoreText for an in-depth understanding of the CoreText typography engine

	CFIndex idx = NSNotFound;	

    CGPoint lineOrigins[numberOfLines];
    CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins);

    for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) {
        CGPoint lineOrigin = lineOrigins[lineIndex];
        CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex);

        // Get bounding information of line
        CGFloat ascent = 0.0f, descent = 0.0f, leading = 0.0f;
        CGFloat width = (CGFloat)CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
        CGFloat yMin = (CGFloat)floor(lineOrigin.y - descent);
        CGFloat yMax = (CGFloat)ceil(lineOrigin.y + ascent);

        // Apply penOffset using flushFactor for horizontal alignment to set lineOrigin since this is the horizontal offset from  drawFramesetter
        CGFloat flushFactor = TTTFlushFactorForTextAlignment(self.textAlignment);
        CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, flushFactor, textRect.size.width);
        lineOrigin.x = penOffset;

        // Check if we've already passed the line
        if (p.y > yMax) {
            break;
        }
        // Check if the point is within this line vertically
        if (p.y >= yMin) {
            // Check if the point is within this line horizontally
            if (p.x >= lineOrigin.x && p.x <= lineOrigin.x + width) {
                // Convert CT coordinates to line-relative coordinates
                CGPoint relativePoint = CGPointMake(p.x - lineOrigin.x, p.y - lineOrigin.y);
                idx = CTLineGetStringIndexForPosition(line, relativePoint);
                break; }}}Copy the code

This article explains the basic use and partial implementation of TTTAttributedLabel. Interested readers should download TTTAttributedLabel for details.

Refer to study website

  • TTTAttributedLabel
  • In-depth understanding of Core Text layout engines

Recommended articles:

Use SwiftUI to add animation to view with SwiftUI to write a simple page iOS control log switch iOS App can be removed a framework of two ways to customize WKWebView display content (a) Swift 5.1 (7) – closure Swift 5.1 (6) – Function Swift 5.1 (5) – Control flow SceneDelegate iOS App startup optimization in Xcode11 new project (2) — Use “Time Profiler” tool to monitor the startup Time of App iOS App startup optimization (a) – to understand the App startup process qiwu Weekly