【问题标题】:Are there any analogues of [NSString stringWithFormat:] for NSAttributedStringNSAttributedString 是否有 [NSString stringWithFormat:] 的类似物
【发布时间】:2015-07-20 19:33:28
【问题描述】:

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

例如:
假设我需要这样格式的显示字符串:“%d + %d = %d”(所有数字都是粗体)。 我想在界面生成器中配置这种格式。在代码中,我想提供参数:1、1、2。应用应显示“1 + 1 = 2”。

【问题讨论】:

    标签: ios objective-c swift nsattributedstring


    【解决方案1】:

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

    结果:

    【讨论】:

    • 稍微提醒一下,这不应该用于未经处理的参数(例如,如果fox == "%@",结果将是The quick brown dog jups over the lazy %@)。
    • results -> 由于未捕获的异常“NSRangeException”而终止应用程序,原因:“NSMutableRLEArray insertObject:range:: Out of bounds”
    • @UtkuDalmaz 您可能与格式字符串提供的参数数量不匹配。我可以通过添加包含更具体消息的 fatalError 行来改进我的答案。
    • 也可以通过添加if range.location != NSNotFound {来更安全
    • @Patrick 在添加 if range.location != NSNotFound 后可以正常工作吗?
    【解决方案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 并将其与文本控件链接

    // 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 格式的示例。

    【讨论】:

    • 很好,我就是需要这个!!
    【解决方案3】:

    这是我编写的一个类别,用于将方法添加到 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
    

    【讨论】:

    • 这只有在你不断改变循环内的 mutableString 时才会起作用。否则所有参数将进入第一个占位符,最后我们将最后一个参数放在第一个占位符的位置。
    【解决方案4】:

    这是一个基于 TheJeff 回答的 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)
        }
    }
    

    【讨论】:

    • 你能在没有 fatalError 的情况下改进它吗?
    • 好吧,如果你不匹配格式字符串提供的参数,这是一个编程错误。我认为编程错误是致命的,不应该发布。如果您愿意,您可以改为断言并返回原始字符串,但我将其留给读者来决定他们想要如何编程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-12
    • 1970-01-01
    相关资源
    最近更新 更多