Ios NSAttributedString是否有[NSString stringWithFormat:]的类似物

Ios NSAttributedString是否有[NSString stringWithFormat:]的类似物,ios,objective-c,swift,nsattributedstring,Ios,Objective C,Swift,Nsattributedstring,通常我在interface builder中构建应用程序界面。有时设计需要使用属性字符串(字体、颜色等)。如果字符串是静态的,则很容易配置。 但如果字符串是动态的(带参数的格式),则无法在interface builder中配置属性。它需要编写大量代码。 我正在为NSAttributedString寻找一些类似的[nsstringwithformat:。因此,我将能够在interface builder中设置字符串格式和必要的属性,然后在代码中提供必要的参数 例如: 我们需要用这样的格式显示字符

通常我在interface builder中构建应用程序界面。有时设计需要使用属性字符串(字体、颜色等)。如果字符串是静态的,则很容易配置。
但如果字符串是动态的(带参数的格式),则无法在interface builder中配置属性。它需要编写大量代码。
我正在为
NSAttributedString
寻找一些类似的
[nsstringwithformat:
。因此,我将能够在interface builder中设置字符串格式和必要的属性,然后在代码中提供必要的参数

例如:
我们需要用这样的格式显示字符串:“<强> %d<强> >强> %d<强> >强> %d”(所有数字都是粗体)。
我想在interface builder中配置此格式。在代码中,我想提供参数:1、1、2。应用程序应该显示“1+1=2

我一直在寻找这个问题的好的现有解决方案,但没有成功。
因此我能够自己实现它。
这就是为什么我要自我回答这个问题,与社区分享知识

解决方案 category提供了基于属性格式和应满足此格式的参数构建属性字符串的方法。
使用此类别最合适的情况是在interface builder中配置了可变属性文本的文本控件。
您需要为属性文本设置正确的字符串格式,并配置必要的属性。
然后,您需要使用此类方法在代码中传递必要的参数

  • 格式语法与
    [NSString stringWithFormat:
    方法中的相同
  • 可用于目标C和Swift代码
  • 需要iOS 6.0及更高版本
  • 与椰荚整合
  • 包括单元测试
用法 1。导入框架标题或模块

// Objective C
// By header
#import <VPAttributedFormat/VPAttributedFormat.h>

// By module
@import VPAttributedFormat;
2。在interface builder中为文本控件设置正确的格式和属性

3。创建IBOutlet并将其与文本控件链接

// Objective C
@property (nonatomic, weak) IBOutlet UILabel *textLabel;

4。用必要的参数填充格式

// Objective C
NSString *hot = @"Hot";
NSString *cold = @"Cold";
  
self.textLabel.attributedText = [NSAttributedString vp_attributedStringWithAttributedFormat:self.textLabel.attributedText,
                                 hot,
                                 cold];

5。查看结果

例子 这是一个示例项目。它提供了基本和Pro格式的示例。

我编写了一个类别,将该方法添加到NSAttributedString中。但是,您必须将NULL作为最后一个参数传递给函数,否则它将崩溃到va_列表对检测大小的限制。[attributedString stringWithFormat:attrFormat,attrArg1,attrArg2,NULL]

@implementation NSAttributedString(stringWithFormat)

+(NSAttributedString*)stringWithFormat:(NSAttributedString*)format, ...{
    va_list args;
    va_start(args, format);

    NSMutableAttributedString *mutableAttributedString = (NSMutableAttributedString*)[format mutableCopy];
    NSString *mutableString = [mutableAttributedString string];

    while (true) {
        NSAttributedString *arg = va_arg(args, NSAttributedString*);
        if (!arg) {
            break;
        }
        NSRange rangeOfStringToBeReplaced = [mutableString rangeOfString:@"%@"];
        [mutableAttributedString replaceCharactersInRange:rangeOfStringToBeReplaced withAttributedString:arg];
    }

    va_end(args);

    return mutableAttributedString;
}
@end

这里是一个基于Jeff答案的Swift 4扩展(针对多次替换进行了更正)。仅限于用NSAttributedString替换占位符:

public extension NSAttributedString {
    convenience init(format: NSAttributedString, args: NSAttributedString...) {
        let mutableNSAttributedString = NSMutableAttributedString(attributedString: format)

        var nsRange = NSString(string: mutableNSAttributedString.string).range(of: "%@")
        var param = 0
        while nsRange.location != NSNotFound {
            guard args.count > 0, param < args.count else {
                fatalError("Not enough arguments provided for \(format)")
            }

            mutableNSAttributedString.replaceCharacters(in: nsRange, with: args[param])
            param += 1
            nsRange = NSString(string: mutableNSAttributedString.string).range(of: "%@")
        }

        self.init(attributedString: mutableNSAttributedString)
    }
}
公共扩展NSAttributedString{
便利初始化(格式:NSAttributedString,参数:NSAttributedString…){
让mutableNSAttributedString=NSMutableAttributedString(attributedString:format)
var nsRange=NSString(字符串:mutableNSAttributedString.string)。范围(共:“%@”)
变量参数=0
而nsRange.location!=NSNotFound{
保护参数args.count>0,参数
与Swift 4.2兼容

public extension NSAttributedString {
    convenience init(format: NSAttributedString, args: NSAttributedString...) {
        let mutableNSAttributedString = NSMutableAttributedString(attributedString: format)

        args.forEach { (attributedString) in
            let range = NSString(string: mutableNSAttributedString.string).range(of: "%@")
            mutableNSAttributedString.replaceCharacters(in: range, with: attributedString)
        }
        self.init(attributedString: mutableNSAttributedString)
    }
}
用法:

let content = NSAttributedString(string: "The quick brown %@ jumps over the lazy %@")
let fox = NSAttributedString(string: "fox", attributes: [.font: Fonts.CalibreReact.boldItalic.font(size: 40)])
let dog = NSAttributedString(string: "dog", attributes: [.font: Fonts.CalibreReact.lightItalic.font(size: 11)])
attributedLabel.attributedText = NSAttributedString(format: content, args: fox, dog)
结果:


非常好,我只是需要这个!!只有在循环中不断更改mutableString时,这才有效。否则,所有参数都将进入第一个占位符,最后我们将最后一个参数放在第一个占位符的位置。您能否在没有fatalError的情况下对此进行改进?如果您没有匹配格式字符串提供的参数,这将是一个编程错误。我认为编程错误是致命的,永远不应该上船。如果你想,你可以断言,只返回原始字符串,但我把它留给读者来决定他们想要如何编程。只是提醒一下,这不应该用于未初始化的参数(例如,如果
fox==“%@”
,结果将是
快速的棕色狗伸出懒惰的%@
).results->由于未捕获的异常“NSRangeException”而终止应用程序,原因:“NSMutableRLEArray insertObject:range::Out bounds”@UtkuDalmaz您可能与格式字符串提供的参数数量不匹配。我可以通过添加带有更具体消息的fatalError行来改进我的答案。如果range.location!=NSNotFound{@Patrick在添加if range.location!=NSNotFound之后,这工作正常吗?
public extension NSAttributedString {
    convenience init(format: NSAttributedString, args: NSAttributedString...) {
        let mutableNSAttributedString = NSMutableAttributedString(attributedString: format)

        var nsRange = NSString(string: mutableNSAttributedString.string).range(of: "%@")
        var param = 0
        while nsRange.location != NSNotFound {
            guard args.count > 0, param < args.count else {
                fatalError("Not enough arguments provided for \(format)")
            }

            mutableNSAttributedString.replaceCharacters(in: nsRange, with: args[param])
            param += 1
            nsRange = NSString(string: mutableNSAttributedString.string).range(of: "%@")
        }

        self.init(attributedString: mutableNSAttributedString)
    }
}
public extension NSAttributedString {
    convenience init(format: NSAttributedString, args: NSAttributedString...) {
        let mutableNSAttributedString = NSMutableAttributedString(attributedString: format)

        args.forEach { (attributedString) in
            let range = NSString(string: mutableNSAttributedString.string).range(of: "%@")
            mutableNSAttributedString.replaceCharacters(in: range, with: attributedString)
        }
        self.init(attributedString: mutableNSAttributedString)
    }
}
let content = NSAttributedString(string: "The quick brown %@ jumps over the lazy %@")
let fox = NSAttributedString(string: "fox", attributes: [.font: Fonts.CalibreReact.boldItalic.font(size: 40)])
let dog = NSAttributedString(string: "dog", attributes: [.font: Fonts.CalibreReact.lightItalic.font(size: 11)])
attributedLabel.attributedText = NSAttributedString(format: content, args: fox, dog)