CGRect for selected UITextRange adjustment for multiline text?

六月ゝ 毕业季﹏ 提交于 2019-12-03 02:17:34

I think all your problems are because of incorrect order of instructions.

You have to

  1. Set text aligment
  2. Find required substrings and add specific attributes to them
  3. And only then highlight strings with subviews.

Also you will not need to use "a workaround for getting the correct width of the string since I'm always using the monospaced Menlo font" in such a case.

I have simplified your code a little to make it more understandable.

Result:

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

I just had to do something similar to this. Assuming you are using 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];

This should use the NSTextAlignment assigned to the attributed string.

iCanCode

As @Avt answered https://stackoverflow.com/a/22572201/3549781 this question. I'm just answering for the newline problem. This newline problem occurs on iOS 7+ even if you use

[self.textView selectedTextRange] or [self.textView positionFromPosition: offset:]

We just have to ensure the layout of the textView before calling firstRectForRange by

[self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer];

Courtesy : https://stackoverflow.com/a/25983067/3549781

P.S : At first I added this as a comment to the question. As most people don't read comments I added this as an answer.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!