简体   繁体   中英

Attributed text, replace a specific font by another using swift

I'm using attributed text in a UITextView. The text contain many fonts.

I'm looking for a way to replace a particular font by another one.

Any swift approach for this? :)

There is a method called attributesAtIndex(_:effectiveRange:) . This method returns a dictionary that you can get the font from.

You will need to iterate over each index to store the fonts. It will be slow, because in text files the font could potentially change every character. So I would recommend doing this off the main thread.

My code will be in Objective-C, but since we use both CocoaTouch, it should be the same logic.

The method I use is enumerateAttribute:inRange:options:usingBlock: to look only for NSFontAttributeName .

There is another point that isn't discussed: How recognize that the font is the one searched. Are you looking for familyName , fontName (property of UIFont ? Even in the same family, font may look a lot different and you may want to search really for the one exactly matching the same name. I've discussed once about Font Names here . You may found it interesting in your case. Note that there are methods (that I didn't know at the time) that can get the Bold Name of the font if available (or italic, etc.)

The main code in Objective-C is this one:

[attrString enumerateAttribute:NSFontAttributeName
                           inRange:NSMakeRange(0, [attrString length])
                           options:0
                        usingBlock:^(id value, NSRange range, BOOL *stop) {
                            UIFont *currentFont = (UIFont *)value; //Font currently applied in this range
                            if ([self isFont:currentFont sameAs:searchedFont]) //This is where it can be tricky
                            {
                            [attrString addAttribute:NSFontAttributeName
                                               value:replacementFont
                                               range:range];
                            }
    }];

Possible change/adaptation according to your needs: Change the font, but not the size:

[attrString addAttribute:NSFontAttributeName value:[UIFont fontWithName:replaceFontName size:currentFont.pointSize]; range:range];

Sample test code:

UIFont *replacementFont = [UIFont boldSystemFontOfSize:12];
UIFont *searchedFont    = [UIFont fontWithName:@"Helvetica Neue" size:15];
UIFont *normalFont      = [UIFont italicSystemFontOfSize:14];

NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:@"Lorem ipsum dolor sit amet,"];

NSAttributedString *attrStr1 = [[NSAttributedString alloc] initWithString:@"consectetuer adipiscing elit." attributes:@{NSFontAttributeName:searchedFont, NSForegroundColorAttributeName:[UIColor redColor]}];

NSAttributedString *attrStr2 = [[NSAttributedString alloc] initWithString:@" Aenean commodo ligula eget dolor." attributes:@{NSFontAttributeName:normalFont}];

NSAttributedString *attrStr3 = [[NSAttributedString alloc] initWithString:@" Aenean massa." attributes:@{NSFontAttributeName:searchedFont}];

NSAttributedString *attrStr4 = [[NSAttributedString alloc] initWithString:@"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim."];

[attrString appendAttributedString:attrStr1];
[attrString appendAttributedString:attrStr2];
[attrString appendAttributedString:attrStr3];
[attrString appendAttributedString:attrStr4];

NSLog(@"AttrString: %@", attrString);

[attrString enumerateAttribute:NSFontAttributeName
                       inRange:NSMakeRange(0, [attrString length])
                       options:0
                    usingBlock:^(id value, NSRange range, BOOL *stop) {
                        UIFont *currentFont = (UIFont *)value;
                        if ([self isFont:currentFont sameAs:searchedFont])
                        {
            [attrString addAttribute:NSFontAttributeName
                               value:replacementFont
                               range:rangeEffect];
                        }
}];

NSLog(@"AttrString Changed: %@", attrString);

With the solution of @TigerCoding , here is the possible code:

NSInteger location = 0;
while (location < [attrString length])
{
    NSRange rangeEffect;
    NSDictionary *attributes = [attrString attributesAtIndex:location effectiveRange:&rangeEffect];
    if (attributes[NSFontAttributeName])
    {
        UIFont *font = attributes[NSFontAttributeName];
        if ([self isFont:font sameAs:searchedFont])
        {
            [attrString addAttribute:NSFontAttributeName value:replacementFont range:rangeEffect];
        }
    }
    location+=rangeEffect.length;
}

As a side note: A few optimization to test (but will need some research). I think from a few example that if you apply the same attributeS for two consecutive range, NSAttributedString will "appends them" into one, in case you may be afraid to apply the same effect consecutively. So the question is that if you have @{NSFontAttributeName:font1, NSForegroundColorAttributeName:color1} for range 0,3 and @{NSFontAttributeName:font1, NSForegroundColorAttributeName:color2} for range 3,5 Will enumerateAttribute:inRange:options:usingBlock: return you the range 0,5? Will it be faster than enumerating each indexes?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM