使用CALayer来突出显示跨越多行的UITextView中的文本

这是为了使用CALayer突出显示而在UITextView中获取CGRect文本的延续。 我无法获得每个线段中的范围正确的矩形。

NSString* searchString = @"Returns the range of characters that generated"; NSRange match = [[[self textView]text]rangeOfString:searchString]; NSRange matchingGlyphRange = [manager glyphRangeForCharacterRange:match actualCharacterRange:NULL]; [manager enumerateLineFragmentsForGlyphRange:matchingGlyphRange usingBlock: ^(CGRect lineRect, CGRect usedRect, NSTextContainer *textContainer, NSRange lineRange, BOOL *stop) { NSRange currentRange = NSIntersectionRange(lineRange, matchingGlyphRange); [manager enumerateEnclosingRectsForGlyphRange:currentRange withinSelectedGlyphRange:NSMakeRange(NSNotFound, 0) inTextContainer:textContainer usingBlock: ^(CGRect rect, BOOL* stop) { if (usedRect.origin.y == rect.origin.y && NSLocationInRange(currentRange.location, lineRange)) { CGRect theRect = [manager boundingRectForGlyphRange:currentRange inTextContainer:textContainer]; CALayer* roundRect = [CALayer layer]; [roundRect setFrame:theRect]; [roundRect setBounds:theRect]; [roundRect setCornerRadius:5.0f]; [roundRect setBackgroundColor:[[UIColor blueColor]CGColor]]; [roundRect setOpacity:0.2f]; [roundRect setBorderColor:[[UIColor blackColor]CGColor]]; [roundRect setBorderWidth:3.0f]; [roundRect setShadowColor:[[UIColor blackColor]CGColor]]; [roundRect setShadowOffset:CGSizeMake(20.0f, 20.0f)]; [roundRect setShadowOpacity:1.0f]; [roundRect setShadowRadius:10.0f]; [[[self textView]layer]addSublayer:roundRect]; *stop = YES; } }]; }]; 

这是我目前的尝试。 我基本上使用enumerateLineFragmentsForGlyphRange:usingBlock:来循环每一行。 然后我使用enumerateEnclosingRectsForGlyphRange:withinSelectedGlyphRange:usingBlock:确保当前行上的文本的范围与行的范围匹配,并具有相同的Y坐标。 有时候这是有效的,有时却不行。

看起来,如果search文本接近返回它将绘制突出显示的方式。 (y坐标)。

突出显示关闭。这似乎是关闭的Y坐标

在这个截图中“返回生成的字符范围”应该被突出显示。

看来,如果文本不接近返回或空白,这可以正常工作:

如果文本周围没有空白,则正确工作

我错过了什么吗?

更新:

我已经把问题缩小了一点,我相信enumerateLineFragmentsForGlyphRange:usingBlock:跳过有返回的行。

我find了解决办法:

而不是使用enumerateEnclosingRectsForGlyphRange:我使用NSString方法enumerateSubstringsInRange:options:usingBlock:

我像往常一样枚举行片段,而不是尝试使用封闭的rect枚举方法,而是在为图层构buildrect的同时枚举行上的每个字符。

 -(void)drawLayerForTextHighlightWithString:(NSString*)string { for (CALayer* eachLayer in [self highlightLayers]) { [eachLayer removeFromSuperlayer]; } NSLayoutManager* manager = [[self textView]layoutManager]; // Find the string NSRange match = [[[self textView]text]rangeOfString:string options: NSCaseInsensitiveSearch | NSDiacriticInsensitiveSearch | NSWidthInsensitiveSearch]; // Convert it to a glyph range NSRange matchingGlyphRange = [manager glyphRangeForCharacterRange:match actualCharacterRange:NULL]; // Enumerate each line in that glyph range (this will fire for each line that the match spans) [manager enumerateLineFragmentsForGlyphRange:matchingGlyphRange usingBlock: ^(CGRect lineRect, CGRect usedRect, NSTextContainer *textContainer, NSRange lineRange, BOOL *stop) { // currentRange uses NSIntersectionRange to return the range of the text that is on the current line NSRange currentRange = NSIntersectionRange(lineRange, matchingGlyphRange); // This rect will be built by enumerating each character in the line, and adding to it's width __block CGRect finalLineRect = CGRectZero; // Here we use enumerateSubstringsInRange:... to go through each glyph and build the final rect for the line [[[self textView]text]enumerateSubstringsInRange:currentRange options:NSStringEnumerationByComposedCharacterSequences usingBlock: ^(NSString* substring, NSRange substringRange, NSRange enclostingRange, BOOL* stop) { // The range of the single glyph being enumerated NSRange singleGlyphRange = [manager glyphRangeForCharacterRange:substringRange actualCharacterRange:NULL]; // get the rect for that glyph CGRect glyphRect = [manager boundingRectForGlyphRange:singleGlyphRange inTextContainer:textContainer]; // check to see if this is the first iteration, if not add the width to the final rect for the line if (CGRectEqualToRect(finalLineRect, CGRectZero)) { finalLineRect = glyphRect; } else { finalLineRect.size.width += glyphRect.size.width; } }]; // once we get the rect for the line, draw the layer UIEdgeInsets textContainerInset = [[self textView]textContainerInset]; finalLineRect.origin.x += textContainerInset.left; finalLineRect.origin.y += textContainerInset.top; CALayer* roundRect = [CALayer layer]; [roundRect setFrame:finalLineRect]; [roundRect setBounds:finalLineRect]; [roundRect setCornerRadius:5.0f]; [roundRect setBackgroundColor:[[UIColor blueColor]CGColor]]; [roundRect setOpacity:0.2f]; [roundRect setBorderColor:[[UIColor blackColor]CGColor]]; [roundRect setBorderWidth:3.0f]; [roundRect setShadowColor:[[UIColor blackColor]CGColor]]; [roundRect setShadowOffset:CGSizeMake(20.0f, 20.0f)]; [roundRect setShadowOpacity:1.0f]; [roundRect setShadowRadius:10.0f]; [[[self textView]layer]addSublayer:roundRect]; [[self highlightLayers]addObject:roundRect]; // continues for each line }]; 

}

我仍然在进行多场比赛,一旦得到这个工作,我会更新代码。