Swift中如何为NSAttributedString实现replacingOccurrences并保留原有属性?
哈哈,这个需求我刚处理过!确实直接转成String用replacingOccurrences会丢光原有属性,得换个思路——直接在NSMutableAttributedString上操作,遍历匹配到的目标词汇范围,叠加新属性即可。
核心思路
- 将原
NSAttributedString转为可变的NSMutableAttributedString(方便修改属性) - 用正则表达式全局匹配所有目标词汇的位置
- 遍历每个匹配范围,给这些范围叠加新属性(保留原有属性,冲突时新属性覆盖旧属性)
完整Swift代码示例
func addAttributeToTargetWords(of target: String, in originalAttrStr: NSAttributedString, newAttributes: [NSAttributedString.Key: Any]) -> NSAttributedString { let mutableAttrStr = NSMutableAttributedString(attributedString: originalAttrStr) do { // 自动转义目标字符串中的正则特殊字符(比如.、*这类) let escapedTarget = NSRegularExpression.escapedPattern(for: target) // 可根据需求添加匹配选项,比如.caseInsensitive实现不区分大小写匹配 let regex = try NSRegularExpression(pattern: escapedTarget, options: [.caseInsensitive]) let fullRange = NSRange(location: 0, length: mutableAttrStr.length) // 反向遍历匹配结果(虽然这里只是修改属性不改变文本长度,正向也可以,但反向是处理文本修改的通用好习惯) let matches = regex.matches(in: mutableAttrStr.string, options: [], range: fullRange).reversed() for match in matches { let targetRange = match.range // 叠加新属性,原有属性会被保留,冲突属性会被新值覆盖 mutableAttrStr.addAttributes(newAttributes, range: targetRange) } } catch { print("正则创建失败:\(error.localizedDescription)") return originalAttrStr } return mutableAttrStr }
关键细节说明
- 正则转义:用
NSRegularExpression.escapedPattern(for:)处理目标字符串,避免目标里的特殊正则字符(比如"a.b")破坏匹配逻辑。 - 匹配选项:如果需要不区分大小写匹配,给正则添加
.caseInsensitive选项即可;其他需求比如多行匹配,也可以追加对应的选项。 - 保留原有属性:
addAttributes(_:range:)方法不会清空原有属性,只是把新属性叠加进去——如果原有范围有和新属性冲突的键(比如都设置了foregroundColor),新属性的值会覆盖旧的,这符合大部分场景的预期。 - 反向遍历:如果是修改文本内容(比如替换成其他字符串),正向遍历会导致后续范围偏移,但这里只是修改属性,文本长度不变,其实正向也没问题,但反向遍历是通用的安全写法,养成习惯准没错。
实际使用例子
// 构造一个带原有属性的富文本 let originalAttrStr = NSMutableAttributedString(string: "Hi there! Hello world, hello Swift!") // 给前5个字符设置蓝色+18号字体 originalAttrStr.addAttributes([ .foregroundColor: UIColor.blue, .font: UIFont.systemFont(ofSize: 18) ], range: NSRange(location: 0, length: 5)) // 给后面的普通文本设置黑色+14号字体 originalAttrStr.addAttributes([ .foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 14) ], range: NSRange(location: 5, length: originalAttrStr.length - 5)) // 给所有"hello"添加红色属性 let redAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.red] let resultAttrStr = addAttributeToTargetWords(of: "hello", in: originalAttrStr, newAttributes: redAttributes) // 结果:"Hello"和"hello"都会变成红色,同时保留原来的字体大小属性
这样操作下来,完全不需要把富文本转成普通字符串,原有属性丝毫不丢,还能精准给指定词汇加上新属性~
内容的提问来源于stack exchange,提问作者user9494956
相关产品推荐
相关产品推荐

