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

Are there any analogues of [NSString stringWithFormat:] for NSAttributedString

通常我在界面生成器中构建应用程序界面。有时设计需要使用属性字符串(字体、颜色等)。如果字符串是静态的,配置起来很容易。
但是,如果字符串是动态的(带参数的格式),则无法在界面构建器中配置属性。需要写很多代码。
我正在为 NSAttributedString 寻找 [NSString stringWithFormat:] 的一些类似物。所以我将能够在界面生成器中设置字符串格式和必要的属性,然后在代码中提供必要的参数。

例如:
让我们考虑一下我需要这样格式的显示字符串:“%d + %d = %d "(所有数字均为粗体)。
我想在界面生成器中配置这种格式。在代码中,我想提供参数:1、1、2。应用程序应显示“1 + 1 = 2".

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

解决方案

NSAttributedString+VPAttributedFormat 类别提供了基于属性格式和应满足此格式的参数构建属性字符串的方法。
使用此类别最合适的情况是在界面构建器中配置了具有可变属性文本的文本控件。
您需要为属性文本设置正确的字符串格式并配置必要的属性。
那么你需要使用此类方法在代码中传递必要的参数。

  • 格式语法与[NSString stringWithFormat:]方法相同;
  • 可用于Objective C和Swift代码;
  • 需要 iOS 6.0 及更高版本;
  • 与 CocoaPods 集成;
  • 包含单元测试。

用法

1。导入框架头文件或模块

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

// By module
@import VPAttributedFormat;

// Swift
import VPAttributedFormat

2。在界面生成器中为文本控件设置正确的格式和属性

3。创建 IBOutlet 并 link 它带有文本控件

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

// Swift
@IBOutlet weak var textLabel: UILabel!

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

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

// Swift
let hot = "Hot"
let cold = "Cold"
var arguments: [CVarArgType] = [hot, cold]
textLabel.attributedText = withVaList(arguments) { pointer in
    NSAttributedString.vp_attributedStringWithAttributedFormat(textLabel.attributedText, arguments: pointer)
}

5.查看结果

例子

VPAttributedFormatExample 是一个示例项目。它提供了 Basic 和 Pro 格式示例。

这里是我写的一个类,把方法添加到NSAttributedString。但是,您必须将 NULL 作为最后一个参数传递给该函数,否则它将崩溃到检测大小的 va_list 限制。 [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

这是一个 Swift 4 扩展,基于 TheJeff 的回答(针对多个替换进行了更正)。它仅限于用 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)
    }
}

兼容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)

结果: