使用CALayer突出显示跨越多行的UITextView中的文本 [英] Using a CALayer to highlight text in a UITextView which spans multiple lines

查看:192
本文介绍了使用CALayer突出显示跨越多行的UITextView中的文本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是获取CGRect文字的延续在UITextView中,用于使用CALayer突出显示。我在为每个行片段中的范围获取正确的矩形时遇到问题。

This is a continuation of Getting CGRect for text in a UITextView for the purpose of highlighting with a CALayer. I'm having trouble with getting the correct rectangle for the ranges in each line fragment.

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坐标。有时候这种方法有效,有时却没有。

This my current attempt. I'm basically using enumerateLineFragmentsForGlyphRange:usingBlock: to cycle through each line. Then i'm using enumerateEnclosingRectsForGlyphRange:withinSelectedGlyphRange:usingBlock: to make sure the range of the text on the current line matches up with the line's range and has the same Y coordinate. Sometimes this works, and sometimes it doesn't.

似乎如果搜索文本靠近返回,它会突出显示高亮显示。 (y坐标)。

It seems that if the search text is near a return it draws the highlight way off. (the y coordinate).

在此屏幕截图中返回生成的字符范围应该突出显示。

In this screenshot "Returns the range of characters that generated" is supposed to be highlighted.

似乎文本不在返回或空格附近,这可以正常工作:

It seems if the text is not near a return or whitespace this works correctly:

我错过了什么吗?

更新:

我已经将问题缩小了一点,我相信enumerateLineFragmentsForGlyphRange:usingBlock :正在跳过有回报的行。

I've narrowed the problem down a bit, I believe enumerateLineFragmentsForGlyphRange: usingBlock: is skipping over lines which have returns.

推荐答案

我找到了解决方案:

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

Instead of using enumerateEnclosingRectsForGlyphRange: I use the NSString method enumerateSubstringsInRange:options:usingBlock:

I enumerat像往常一样,行片段,但我没有尝试使用封闭的rect枚举方法,而是在为图层构建矩形时枚举该行上的每个字符。

I enumerate the line fragments as usual, but instead of trying to use the enclosing rect enumeration method, I enumerate each character on the line while building the rect for the layer.

-(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
 }];

}

我还在工作在多个匹配项上,一旦我开始工作,我就会更新代码。

I'm still working on multiple matches, i'll update the code once I get that working.

这篇关于使用CALayer突出显示跨越多行的UITextView中的文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆