You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Swift中NSAttributedStrings使用及动态修改属性字符串优化方案问询

当然有更优雅的解决方案!我之前做电商项目时也遇到过一模一样的需求——硬编码属性字典、每次手动拼接NSAttributedString不仅麻烦还容易出错。分享几个我实际项目中用过的高效方案:


方案1:iOS 15+ 用AttributedString原生插值(最简洁)

苹果在iOS 15推出的AttributedString(Swift)支持直接插值,还能把样式抽成可复用的AttributeContainer,完全不用硬编码整个字符串结构:

// 提前定义好各类样式,集中管理
let baseTextStyle: AttributeContainer = {
    var container = AttributeContainer()
    container.font = UIFont.systemFont(ofSize: 14)
    container.foregroundColor = UIColor.black
    return container
}()

let currencySymbolStyle: AttributeContainer = {
    var container = AttributeContainer()
    container.font = UIFont.systemFont(ofSize: 14)
    container.foregroundColor = UIColor.darkGray
    return container
}()

let priceValueStyle: AttributeContainer = {
    var container = AttributeContainer()
    container.font = UIFont.boldSystemFont(ofSize: 18)
    container.foregroundColor = UIColor.red
    return container
}()

// 动态更新价格的方法
func updatePrice(to amount: Double) {
    // 直接通过插值拼接带样式的字符串
    let fullAttributedString = AttributedString("现价:", attributes: baseTextStyle)
        + AttributedString("¥", attributes: currencySymbolStyle)
        + AttributedString(String(format: "%.2f", amount), attributes: priceValueStyle)
    
    // 赋值给标签
    priceLabel.attributedText = NSAttributedString(fullAttributedString)
}

这里只需要维护样式容器,更新时只传价格数值就行,完全不需要硬编码属性字典或者手动计算字符串范围。


方案2:兼容iOS 15以下的模板占位符替换

如果需要支持旧系统,可以用模板字符串+占位符替换的思路,提前定义带占位符的模板和对应样式,更新时只替换占位符内容:

// 提前定义模板和样式映射
private let priceTemplate = "现价:<currency><price>"
private let styleMap: [String: [NSAttributedString.Key: Any]] = [
    "default": [.font: UIFont.systemFont(ofSize: 14), .foregroundColor: UIColor.black],
    "currency": [.font: UIFont.systemFont(ofSize: 14), .foregroundColor: UIColor.darkGray],
    "price": [.font: UIFont.boldSystemFont(ofSize: 18), .foregroundColor: UIColor.red]
]

func updatePrice(to amount: Double) {
    // 初始化模板字符串,应用默认样式
    let mutableAttrString = NSMutableAttributedString(string: priceTemplate, attributes: styleMap["default"])
    
    // 替换货币符号占位符
    let currencyRange = mutableAttrString.mutableString.range(of: "<currency>")
    if currencyRange.location != NSNotFound {
        mutableAttrString.replaceCharacters(in: currencyRange, with: "¥")
        mutableAttrString.addAttributes(styleMap["currency"]!, range: NSRange(location: currencyRange.location, length: 1))
    }
    
    // 替换价格数值占位符
    let priceText = String(format: "%.2f", amount)
    let priceRange = mutableAttrString.mutableString.range(of: "<price>")
    if priceRange.location != NSNotFound {
        mutableAttrString.replaceCharacters(in: priceRange, with: priceText)
        mutableAttrString.addAttributes(styleMap["price"]!, range: NSRange(location: priceRange.location, length: priceText.count))
    }
    
    priceLabel.attributedText = mutableAttrString
}

这种方式不用每次重新构建整个NSAttributedString,只针对占位符做替换,样式集中管理,也避免了硬编码属性的问题。


方案3:封装自定义PriceLabel子类(高可维护性)

如果你的项目中有多处价格标签,推荐把样式和更新逻辑封装到自定义UILabel子类里,外部调用只需要传数值,完全不用关心内部的NSAttributedString构建:

import UIKit

class PriceLabel: UILabel {
    // 可以通过@IBInspectable让样式在Storyboard/XIB中可视化配置
    @IBInspectable var prefixText: String = "现价:"
    @IBInspectable var prefixFont: UIFont = .systemFont(ofSize: 14)
    @IBInspectable var prefixColor: UIColor = .black
    
    @IBInspectable var currencySymbol: String = "¥"
    @IBInspectable var currencyFont: UIFont = .systemFont(ofSize: 14)
    @IBInspectable var currencyColor: UIColor = .darkGray
    
    @IBInspectable var priceFont: UIFont = .boldSystemFont(ofSize: 18)
    @IBInspectable var priceColor: UIColor = .red
    
    // 外部调用的更新方法
    func setPrice(_ amount: Double) {
        let prefixAttr = NSAttributedString(string: prefixText, attributes: [
            .font: prefixFont,
            .foregroundColor: prefixColor
        ])
        let currencyAttr = NSAttributedString(string: currencySymbol, attributes: [
            .font: currencyFont,
            .foregroundColor: currencyColor
        ])
        let priceAttr = NSAttributedString(string: String(format: "%.2f", amount), attributes: [
            .font: priceFont,
            .foregroundColor: priceColor
        ])
        
        let fullAttr = NSMutableAttributedString()
        fullAttr.append(prefixAttr)
        fullAttr.append(currencyAttr)
        fullAttr.append(priceAttr)
        
        self.attributedText = fullAttr
    }
}

使用时只需要:

// 代码中创建或者从Storyboard拖出
let priceLabel = PriceLabel()
priceLabel.setPrice(199.99)

这种方案把所有和价格标签相关的逻辑都封装在子类里,后续修改样式或者格式只需要改这一个文件,可维护性拉满。


总结

  • 如果你只需要支持iOS 15+,优先用AttributedString插值,代码最简洁直观;
  • 需要兼容旧系统就用模板占位符替换;
  • 多处使用价格标签的话,自定义子类是最省心的选择。

内容的提问来源于stack exchange,提问作者Adrian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:13:18