UITextView - Highlight text with NSBackgroundColor - exclude line breaks

蹲街弑〆低调 提交于 2019-12-03 16:42:33

My solution is not simple and I do not know if it solves all your problems, but I managed to achieve the effect you wanted.

I've tested it on UILabel, on different fonts and font sizes, and it worked well.

Here what i've done:

- (NSArray*)getRangesOfLinesForText:(NSString*)text font:(UIFont*)font containerWidth:(float)width {

    NSMutableArray *ranges = [[NSMutableArray alloc] init];

    NSInteger lastWhiteSpaceIndex = 0;
    NSInteger rangeStart = 0;
    NSMutableString *substring = [[NSMutableString alloc] init];
    for (int i = 0; i < [text length]; i++) {

        char c = [text characterAtIndex:i];
        [substring appendFormat:@"%c",c];

        CGRect substringRect = [substring boundingRectWithSize:CGSizeMake(width, font.capHeight)options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil];

        if([[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:c]) {
            lastWhiteSpaceIndex = i;
        }

        if(substringRect.size.width == 0) {
            NSRange range;
            if (lastWhiteSpaceIndex != i) {

                range = NSMakeRange(rangeStart, lastWhiteSpaceIndex-rangeStart);

                [ranges addObject:NSStringFromRange(range)];
                substring = [[NSMutableString alloc] init];
                i = lastWhiteSpaceIndex;
                rangeStart = lastWhiteSpaceIndex+1;
            }
        }
    }

    //Last Line
    NSRange range = NSMakeRange(rangeStart, [text length]-rangeStart);
    [ranges addObject:NSStringFromRange(range)];

    return ranges;
}

The method above splits the text string into separate lines. Unfortunately the method boundingRectWithSize:options:attributes:context: does not support line break by word wrap, so i had to detect it myself. I achieved that by checking if substringRect.size.width == 0. (It changes to zero, when substring becomes too long to fit the line width).

The method returns an array of ranges for every line. (Ranges are converted to NSStrings with NSStringFromRange).

Example of use:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectFromString(@"{{0,0},{300,400}}")];
    textLabel.numberOfLines = 0;
    [self.view addSubview:textLabel];


    NSString *text = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec luctus quis sapien a rutrum. Vivamus nec leo suscipit nibh rutrum dignissim at vel justo. Maecenas mi orci, ultrices non luctus nec, aliquet et nunc.";
    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:text];

    for (NSString* stringRange in [self getRangesOfLinesForText:text font:textLabel.font containerWidth:textLabel.frame.size.width]) {

        [string addAttribute:NSBackgroundColorAttributeName value:[UIColor greenColor] range:NSRangeFromString(stringRange)];

    }

    textLabel.attributedText = string;
}

See TextViewHighlighter

Basically, we need to detect the ranges of line break characters, and make attributes for them to NSBackgroundColorAttributeName: [UIColor clearColor]

NSMutableAttributedString *highlighedAttributedString = [[NSMutableAttributedString alloc] initWithString:self.text];
    [highlighedAttributedString addAttributes:@{NSForegroundColorAttributeName: self.textColor,
                                                NSBackgroundColorAttributeName: self.highlightedColor
                                                }
                                        range:NSMakeRange(0, self.text.length)];

    for (NSValue *rangeValue in rangeValues) {
        NSRange range = [rangeValue rangeValue];
        [highlighedAttributedString addAttributes:@{NSBackgroundColorAttributeName: [UIColor clearColor],
                                                    }
                                            range:range];
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!