用另一个 NSAttributedString 替换 NSAttributedString 的子字符串

Posted

技术标签:

【中文标题】用另一个 NSAttributedString 替换 NSAttributedString 的子字符串【英文标题】:Replace substring of NSAttributedString with another NSAttributedString 【发布时间】:2012-01-04 02:22:12 【问题描述】:

我想用另一个NSAttributedString 替换NSAttributedString 的子字符串(例如@"replace")。

我正在为NSAttributedString 寻找与NSStringstringByReplacingOccurrencesOfString:withString: 等效的方法。

【问题讨论】:

要替换给定范围内的文本(不是每次出现),请参见下面@Darius 给出的答案 【参考方案1】:

    将属性字符串转换为NSMutableAttributedString 的实例。

    可变属性字符串具有mutableString 属性。根据文档:

    “接收者跟踪此字符串的更改并保持其属性映射是最新的。”

    因此,您可以使用生成的可变字符串以replaceOccurrencesOfString:withString:options:range: 执行替换。

【讨论】:

这个私有API作为可变字符串属性不是只读的吗? @MichalShatz 否。只读属性意味着您不能为其分配不同的对象(即,您不能调用setMutableString:)。但是在适当的位置修改可变字符串对象是完全可以的。这就是该属性存在的全部原因。 这真的是一个答案吗?我遇到了一个转换问题,第二个参数(withString)是一个属性字符串 方法“replaceOccurrencesOfString:withString:options:range:”适用于 NSMutableString,不适用于 NSAttributedString 或 NSMutableAttributedString。如果你转换为 NSMutableString 在那里进行替换,你将在转换后失去它的属性。问题是关于如何获得 NSAttributedString 作为结果,而不仅仅是字符串,所以答案完全无关紧要,但它得到了如此多的支持。 这很好用,例如替换换行符:[[result mutableString] replaceOccurrencesOfString:@"\n" withString:@" " options:NSCaseInsensitiveSearch range:NSMakeRange(0, result.length)];【参考方案2】:

以下是如何更改 NSMutableAttributedString 的字符串,同时保留其属性:

斯威夫特:

// first we create a mutable copy of attributed text 
let originalAttributedText = nameLabel.attributedText?.mutableCopy() as! NSMutableAttributedString

// then we replace text so easily
let newAttributedText = originalAttributedText.mutableString.setString("new text to replace")

目标-C:

NSMutableAttributedString *newAttrStr = [attribtedTxt.mutableString setString:@"new string"];

【讨论】:

您的示例可能甚至无法编译。存储在 NSMutableAttributedString 中的 C 字符串?替换无效,因为您在没有参考的情况下在副本 (mutableString) 上进行了替换?【参考方案3】:
NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithString:@"I am a boy."];
[result addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, [result length])];

NSMutableAttributedString *replace = [[NSMutableAttributedString alloc] initWithString:@"a"];
[replace addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [replace length])];

[result replaceCharactersInRange:NSMakeRange(5, [replace length]) withAttributedString:replace];

【讨论】:

【参考方案4】:

我必须在<b> 标签中加粗文本,我做了什么:

- (NSAttributedString *)boldString:(NSString *)string 
    UIFont *boldFont = [UIFont boldSystemFontOfSize:14];
    NSMutableAttributedString *attributedDescription = [[NSMutableAttributedString alloc] initWithString:string];

    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@".*?<b>(.*?)<\\/b>.*?" options:NSRegularExpressionCaseInsensitive error:NULL];
    NSArray *myArray = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)] ;
    for (NSTextCheckingResult *match in myArray) 
        NSRange matchRange = [match rangeAtIndex:1];
        [attributedDescription addAttribute:NSFontAttributeName value:boldFont range:matchRange];
    
    while ([attributedDescription.string containsString:@"<b>"] || [attributedDescription.string containsString:@"</b>"]) 
        NSRange rangeOfTag = [attributedDescription.string rangeOfString:@"<b>"];
        [attributedDescription replaceCharactersInRange:rangeOfTag withString:@""];
        rangeOfTag = [attributedDescription.string rangeOfString:@"</b>"];
        [attributedDescription replaceCharactersInRange:rangeOfTag withString:@""];
    
    return attributedDescription;

【讨论】:

谢谢你,骗子!我一直在努力为我的项目实现这样的功能,但使用不同的标签(例如 )。经过几个小时的工作,我决定尝试一下,但做了一些修改,比如自定义字体。谢谢!【参考方案5】:

就我而言,以下方式是唯一的(在 ios9 上测试):

NSAttributedString *attributedString = ...;
NSAttributedString *anotherAttributedString = ...; //the string which will replace

while ([attributedString.mutableString containsString:@"replace"]) 
        NSRange range = [attributedString.mutableString rangeOfString:@"replace"];
        [attributedString replaceCharactersInRange:range  withAttributedString:anotherAttributedString];
    

当然,如果能找到另一种更好的方法就好了。

【讨论】:

这更适合替换给定范围内的字符串,而不是出现,因为字符串可能包含重复的子字符串。【参考方案6】:

我发现所有其他答案都不起作用。以下是我如何在类别扩展中替换 NSAttributed 字符串的内容:

func stringWithString(stringToReplace:String, replacedWithString newStringPart:String) -> NSMutableAttributedString

    let mutableAttributedString = mutableCopy() as! NSMutableAttributedString
    let mutableString = mutableAttributedString.mutableString

    while mutableString.containsString(stringToReplace) 
        let rangeOfStringToBeReplaced = mutableString.rangeOfString(stringToReplace)
        mutableAttributedString.replaceCharactersInRange(rangeOfStringToBeReplaced, withString: newStringPart)
    
    return mutableAttributedString

【讨论】:

【参考方案7】:

在 Swift 4 和 iOS 11 中,您可以使用以下两种方法之一来解决您的问题。


#1。使用NSMutableAttributedStringreplaceCharacters(in:with:)方法

NSMutableAttributedString 有一个名为replaceCharacters(in:with:) 的方法。 replaceCharacters(in:with:) 有以下声明:

用给定属性字符串的字符和属性替换给定范围内的字符和属性。

func replaceCharacters(in range: NSRange, with attrString: NSAttributedString)

下面的 Playground 代码展示了如何使用 replaceCharacters(in:with:) 来将 NSMutableAttributedString 实例的子字符串替换为新的 NSMutableAttributedString 实例:

import UIKit

// Set initial attributed string
let initialString = "This is the initial string"
let attributes = [NSAttributedStringKey.foregroundColor : UIColor.red]
let mutableAttributedString = NSMutableAttributedString(string: initialString, attributes: attributes)

// Set new attributed string
let newString = "new"
let newAttributes = [NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue]
let newAttributedString = NSMutableAttributedString(string: newString, attributes: newAttributes)

// Get range of text to replace
guard let range = mutableAttributedString.string.range(of: "initial") else  exit(0) 
let nsRange = NSRange(range, in: mutableAttributedString.string)

// Replace content in range with the new content
mutableAttributedString.replaceCharacters(in: nsRange, with: newAttributedString)

#2。使用NSMutableStringreplaceOccurrences(of:with:options:range:)方法

NSMutableString 有一个名为replaceOccurrences(of:with:options:range:) 的方法。 replaceOccurrences(of:with:options:range:) 有以下声明:

用另一个给定字符串替换给定范围内所有出现的给定字符串,返回替换次数。

func replaceOccurrences(of target: String, with replacement: String, options: NSString.CompareOptions = [], range searchRange: NSRange) -> Int

下面的 Playground 代码展示了如何使用 replaceOccurrences(of:with:options:range:) 来将 NSMutableAttributedString 实例的子字符串替换为新的 NSMutableAttributedString 实例:

import UIKit

// Set initial attributed string
let initialString = "This is the initial string"
let attributes = [NSAttributedStringKey.foregroundColor : UIColor.red]
let mutableAttributedString = NSMutableAttributedString(string: initialString, attributes: attributes)

// Set new string
let newString = "new"

// Replace replaceable content in mutableAttributedString with new content
let totalRange = NSRange(location: 0, length: mutableAttributedString.string.count)
_ = mutableAttributedString.mutableString.replaceOccurrences(of: "initial", with: newString, options: [], range: totalRange)

// Get range of text that requires new attributes
guard let range = mutableAttributedString.string.range(of: newString) else  exit(0) 
let nsRange = NSRange(range, in: mutableAttributedString.string)

// Apply new attributes to the text matching the range
let newAttributes = [NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue]
mutableAttributedString.setAttributes(newAttributes, range: nsRange)

【讨论】:

【参考方案8】:

我有一个特定的要求并固定如下。这可能会对某人有所帮助。

要求: 在storyboard中,直接将富文本添加到UITextView的属性中,其中包含一个单词“App Version: 1.0”。现在我必须通过从 info plist 中读取来动态化版本号。

解决方案: 从情节提要中删除版本号 1.0,只保留“App Version:”并添加以下代码。

NSAttributedString *attribute = self.firsttextView.attributedText;
NSMutableAttributedString *mutableAttri = [[NSMutableAttributedString alloc] initWithAttributedString:attribute];
NSString *appVersionText = @"App Version:";
if ([[mutableAttri mutableString] containsString:appVersionText]) 
    NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
    NSString* version = [infoDict objectForKey:@"CFBundleShortVersionString"];
    NSString *newappversion = [NSString stringWithFormat:@"%@ %@",appVersionText,version] ;
    [[mutableAttri mutableString] replaceOccurrencesOfString:appVersionText withString:newappversion options:NSCaseInsensitiveSearch range:NSMakeRange(0, mutableAttri.length)];
    self.firsttextView.attributedText = mutableAttri;

完成!!更新/修改了属性文本。

【讨论】:

【参考方案9】:

斯威夫特 4: 更新了sunkas Swift 4 的优秀解决方案并包含在“扩展”中。只需将它剪辑到您的 ViewController(在类之外)并使用它。

extension NSAttributedString 
    func stringWithString(stringToReplace: String, replacedWithString newStringPart: String) -> NSMutableAttributedString
    
        let mutableAttributedString = mutableCopy() as! NSMutableAttributedString
        let mutableString = mutableAttributedString.mutableString
        while mutableString.contains(stringToReplace) 
            let rangeOfStringToBeReplaced = mutableString.range(of: stringToReplace)
            mutableAttributedString.replaceCharacters(in: rangeOfStringToBeReplaced, with: newStringPart)
        
        return mutableAttributedString
    

【讨论】:

“只需将它剪辑到你的 ViewController 中(在类之外)并使用它。” - 那么这是放置扩展的最佳位置吗? 我没有这么说,我只是说它会那样工作,而且确实如此。你把所有的扩展放在哪里? 嗯,你要小心你说的话,这里有很多开发者学习坏习惯。如果您有 NSAttributedString 的扩展名,则创建一个名为“NSAttributedString+Extensions.swift”的文件并将其放入名为“Extensions”的文件夹中。

以上是关于用另一个 NSAttributedString 替换 NSAttributedString 的子字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何从 NSAttributedString 获取图像数据

你如何使用NSAttributedString?

NSAttributedString

NSAttributedString使用方法整理

仅更改 NSAttributedString 的字体大小

你如何使用 NSAttributedString?