CGRect用于多行文本的选定UITextRange调整?

时间:2014-03-12 19:40:00

标签: ios objective-c uitextview nsattributedstring

我已使用this answer为特定范围的文字创建CGRect。

在这个UITextView我设置了attributedText(所以我有一堆带有不同字形大小的样式文本)。

这适用于左对齐的第一行文字,但在使用NSTextAlignmentJustifiedNSTextAlignmentCenter时会产生一些非常奇怪的结果。

当线条缠绕时,它也无法正确计算;如果有\n个换行符,则有时也无法计算。

我得到这样的东西(这是中心对齐的):

enter image description here

相反,我期待这个:

enter image description here

这一行有一个\n换行符 - 前两个代码位被成功突出显示,但最后一个more code for you to see并不是因为文本换行不计算在x,y计算中。

这是我的实施:

- (void)formatMarkdownCodeBlockWithAttributes:(NSDictionary *)attributesDict
                      withHighlightProperties:(NSDictionary *)highlightProperties
                               forFontSize:(CGFloat)pointSize
{
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"`.+?`" options:NO error:nil];
    NSArray *matchesArray = [regex matchesInString:[self.attributedString string] options:NO range:NSMakeRange(0, self.attributedString.length)];
    for (NSTextCheckingResult *match in matchesArray)
    {
        NSRange range = [match range];
        if (range.location != NSNotFound) {

            self.textView.attributedText = self.attributedString;

            CGRect codeRect = [self frameOfTextRange:range forString:[[self.attributedString string] substringWithRange:range] forFontSize:pointSize];
            UIView *highlightView = [[UIView alloc] initWithFrame:codeRect];
            highlightView.layer.cornerRadius = 4;
            highlightView.layer.borderWidth = 1;
            highlightView.backgroundColor = [highlightProperties valueForKey:@"backgroundColor"];
            highlightView.layer.borderColor = [[highlightProperties valueForKey:@"borderColor"] CGColor];
            [self.contentView insertSubview:highlightView atIndex:0];

            [self.attributedString addAttributes:attributesDict range:range];

            //strip first and last `
            [[self.attributedString mutableString] replaceOccurrencesOfString:@"(^`|`$)" withString:@" " options:NSRegularExpressionSearch range:range];
        }
    }
}

- (CGRect)frameOfTextRange:(NSRange)range forString:(NSString *)string forFontSize:(CGFloat)pointSize
{
    self.textView.selectedRange = range;
    UITextRange *textRange = [self.textView selectedTextRange];
    CGRect rect = [self.textView firstRectForRange:textRange];
    //These three lines are a workaround for getting the correct width of the string since I'm always using the monospaced Menlo font.
    rect.size.width = ((pointSize / 1.65) * string.length) - 4;
    rect.origin.x+=2;
    rect.origin.y+=2;
    return rect;
}

哦,如果你想要它,这是我正在玩的字符串:

*This* is **awesome** @mention `code` more \n `code and code` #hashtag [markdown](http://google.com) __and__ @mention2 {#FFFFFF|colored text} This**will also** work but ** will not ** **work** Also, some `more code for you to see`

注意:请不要建议我使用TTTAttributedLabelOHAttributedLabel

3 个答案:

答案 0 :(得分:4)

我认为你所有的问题都是因为说明顺序不正确。

你必须

  1. 设置文字对齐
  2. 查找所需的子字符串并向其添加特定属性
  3. 然后才会使用子视图突出显示字符串。
  4. 此外,在这种情况下,您不需要使用“一种解决方法来获取正确的字符串宽度,因为我总是使用等宽的Menlo字体”。

    我已经简化了一些代码,使其更容易理解。

    结果: enter image description here

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        NSDictionary *basicAttributes = @{ NSFontAttributeName : [UIFont boldSystemFontOfSize:18],
                                           NSForegroundColorAttributeName : [UIColor blackColor] };
        NSDictionary *attributes = @{ NSFontAttributeName : [UIFont systemFontOfSize:15],
                                      NSForegroundColorAttributeName : [UIColor darkGrayColor]};
    
    
        _textView.attributedText = [[NSAttributedString alloc] initWithString:
                                    @"*This* is **awesome** @mention `code` more \n `code and code` #hashtag [markdown](http://google.com) __and__ @mention2 {#FFFFFF|colored text} This**will also** work but ** will not ** **work** Also, some `more code for you to see`" attributes:attributes];
        _textView.textAlignment = NSTextAlignmentCenter;
    
        [self formatMarkdownCodeBlockWithAttributes:basicAttributes];
    }
    
    - (void)formatMarkdownCodeBlockWithAttributes:(NSDictionary *)attributesDict
    {
        NSMutableString *theString = [_textView.attributedText.string mutableCopy];
        NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"`.+?`" options:NO error:nil];
        NSArray *matchesArray = [regex matchesInString:theString options:NO range:NSMakeRange(0, theString.length)];
    
        NSMutableAttributedString *theAttributedString = [_textView.attributedText mutableCopy];
        for (NSTextCheckingResult *match in matchesArray)
        {
            NSRange range = [match range];
            if (range.location != NSNotFound) {
                [theAttributedString addAttributes:attributesDict range:range];
            }
        }
    
        _textView.attributedText = theAttributedString;
    
        for (NSTextCheckingResult *match in matchesArray)
        {
            NSRange range = [match range];
            if (range.location != NSNotFound) {
    
                CGRect codeRect = [self frameOfTextRange:range];
                UIView *highlightView = [[UIView alloc] initWithFrame:codeRect];
                highlightView.layer.cornerRadius = 4;
                highlightView.layer.borderWidth = 1;
                highlightView.backgroundColor = [UIColor yellowColor];
                highlightView.layer.borderColor = [[UIColor redColor] CGColor];
                [_textView insertSubview:highlightView atIndex:0];
            }
        }
    }
    
    - (CGRect)frameOfTextRange:(NSRange)range
    {
        self.textView.selectedRange = range;
        UITextRange *textRange = [self.textView selectedTextRange];
        CGRect rect = [self.textView firstRectForRange:textRange];
        return rect;
    }
    

答案 1 :(得分:1)

我只需做类似的事情。假设您使用的是iOS 7:

// Build the range that you want for your text
NSRange range = NSMakeRange(location, length);

// Get the substring of the attributed text at that range
NSAttributedString *substring = [textView.attributedText attributedSubstringFromRange:range];

// Find the frame that would enclose the substring of text.
CGRect frame = [substring boundingRectWithSize:maxSize
                                           options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                           context:nil];

这应该使用分配给属性字符串的NSTextAlignment。

答案 2 :(得分:1)

@Avt回答https://stackoverflow.com/a/22572201/3549781这个问题。我只是回答换行问题。即使您使用

,这个换行问题也会出现在iOS 7+上
[self.textView selectedTextRange] or [self.textView positionFromPosition: offset:]

我们必须在通过

调用firstRectForRange之前确保textView的布局
[self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer];

礼貌:https://stackoverflow.com/a/25983067/3549781

P.S:起初我把这个作为对这个问题的评论。由于大多数人不阅读评论,我将其作为答案添加。