Replace regex matches in attributed string with image in Objective-C

旧巷老猫 提交于 2019-12-06 11:54:38

问题


My goal is to store the information for an attributed string in Parse.com. I decided to come up with an encoding for attributed text for my images that works by replacing any string {X} in braces with the corresponding image. For example:

Picture of 2 colorless mana: {X}

Should produce an attributed string where {X} is replaced by an image. This is what I've tried:

NSString *formattedText = @"This will cost {2}{PW}{PW} to cast.";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(?<=\\{)[^}]+(?=\\})" options:NSRegularExpressionAnchorsMatchLines
                                                                         error:nil];
NSArray *matches = [regex matchesInString:formattedText
                                  options:kNilOptions
                                    range:NSMakeRange(0, formattedText.length)];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:formattedText];
for (NSTextCheckingResult *result in matches)
{
    NSString *match = [formattedText substringWithRange:result.range];
    NSTextAttachment *imageAttachment = [NSTextAttachment new];
    imageAttachment.image = [UIImage imageNamed:[NSString stringWithFormat:@"Mana%@.png", match]];
    NSAttributedString *replacementForTemplate = [NSAttributedString attributedStringWithAttachment:imageAttachment];
    [attributedString replaceCharactersInRange:result.range
                          withAttributedString:replacementForTemplate];
}
[_textView setAttributedText:attributedString];

There are two problems with this approach currently:

  • The braces aren't replaced, only the text inside of them.
  • The range for each match is changing because the string itself is changing, and it gets more off with each replacement whose original text was of length > 1. Here's what it looks like:


回答1:


Two problems:

Braces aren't replaced. That's because you're using assertions, which aren't counted as part of the match. The match you're making with your pattern only contains the stuff inside the braces. Use this pattern instead:

\{([^}]+)\}

That's: match a brace, followed by one or more things that aren't closing braces in a capture group, followed by a closing brace. The whole match includes the braces now.

That introduces another problem, though -- you're using the enclosed bits to pick the replacement image. Small change to fix this: the internal capture group holds that information, now, rather than the whole group. The capture group's length tells you the range of the substring you need.

NSUInteger lengthOfManaName = [result rangeAtIndex:1].length;
NSString manaName = [match substringWithRange:(NSRange){1, lengthOfManaName}];
imageAttachment.image = [UIImage imageNamed:[NSString stringWithFormat:@"Mana%@.png", manaName]];

Second problem: length of string is changing. Just enumerate backwards:

for (NSTextCheckingResult *result in [matches reverseObjectEnumerator])
{
    //...
}

Changes to ranges towards the end of the string now won't affect earlier ranges.



来源:https://stackoverflow.com/questions/24874163/replace-regex-matches-in-attributed-string-with-image-in-objective-c

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