简体   繁体   English

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

[英]Attributed text, replace a specific font by another using swift

I'm using attributed text in a UITextView. 我在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:) . 有一个称为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. 我的代码将在Objective-C中进行,但是由于我们同时使用了CocoaTouch,因此逻辑应该相同。

The method I use is enumerateAttribute:inRange:options:usingBlock: to look only for NSFontAttributeName . 我使用的方法是enumerateAttribute:inRange:options:usingBlock:仅查找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.) 您是否要查找familyNamefontNameUIFont属性?即使在同一个家族中,字体看起来也可能有很大不同,并且您可能想真正地搜索完全匹配相同名称的字体。我在这里曾经讨论过Font Names。您可能会发现这种情况很有趣,请注意,有些方法(当时我还不知道)可以获取字体的粗体(如果可用)(或斜体等)。

The main code in Objective-C is this one: 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];
                            }
    }];

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: 使用@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;
}

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. 我想举几个例子,如果您对两个连续的范围应用相同的attributeS,则NSAttributedString会将它们“附加”为一个,以防您可能害怕连续应用相同的效果。 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? 所以问题是,如果您有@{NSFontAttributeName:font1, NSForegroundColorAttributeName:color1}的范围为0,3和@{NSFontAttributeName:font1, NSForegroundColorAttributeName:color2} @{NSFontAttributeName:font1, NSForegroundColorAttributeName:color1}的范围为3,5将会enumerateAttribute:inRange:options:usingBlock:返回您0.5的范围? Will it be faster than enumerating each indexes? 它会比枚举每个索引更快吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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