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
相关产品推荐
相关产品推荐

