属性文本,使用 swift 将特定字体替换为另一种

Attributed text, replace a specific font by another using swift

我在 UITextView 中使用属性文本。 文字包含多种字体。

我正在寻找一种将特定字体替换为另一种字体的方法。

对此有什么swift方法吗? :)

有一种方法叫做attributesAtIndex(_:effectiveRange:)。此方法 returns 您可以从中获取字体的字典。

您需要遍历每个索引来存储字体。它会很慢,因为在文本文件中,字体可能会改变每个字符。所以我建议在主线程之外执行此操作。

我的代码会在Objective-C里,但是既然我们都用了CocoaTouch,应该是一样的逻辑。

我用的方法是enumerateAttribute:inRange:options:usingBlock:只找NSFontAttributeName.

还有一点没有讨论:如何识别字体是搜索到的字体。您是否在寻找 familyNamefontName(属性 of UIFont?即使在同一个系列中,字体看起来也可能有很大不同,您可能真的想搜索那个完全匹配相同的名称。 我曾经讨论过字体名称 here。您可能会发现它对您的情况很有趣。请注意,有一些方法(当时我不知道)可以获取字体的粗体名称(如果可用)(或斜体等)

Objective-C中的主要代码是这个:

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

可能change/adaptation根据您的需要: 更改字体,但不更改大小:

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

示例测试代码:

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);

有了@TigerCoding的解决方案,这里是可能的代码:

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

作为旁注: 一些优化测试(但需要一些研究)。 我从几个例子中认为,如果你对两个连续的范围应用相同的属性,NSAttributedString 将 "appends them" 合并为一个,以防你可能害怕连续应用相同的效果。 所以问题是如果你有 @{NSFontAttributeName:font1, NSForegroundColorAttributeName:color1} 范围 0,3 和 @{NSFontAttributeName:font1, NSForegroundColorAttributeName:color2} 范围 3,5 请问enumerateAttribute:inRange:options:usingBlock: return 你的范围是0,5?它会比枚举每个索引更快吗?