关于 attributed-string
面向 Swift 开发者的 Foundation AttributedString 使用指南,涵盖文本样式、段落格式、选区编辑和 SwiftUI 集成。内容说明新旧 API 的用法及系统版本要求。
主要功能
- 01设置文本字体、颜色和下划线
- 02控制对齐、方向与行高
- 03替换和编辑文本选区
- 04处理不连续文本范围
- 05在 SwiftUI 中显示和编辑富文本
适用场景
- 01为文本片段应用样式
- 02调整段落对齐和行距
- 03在编辑器中替换所选文字
- 04构建 SwiftUI 富文本编辑界面
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rshankras/ claude-code-apple-skills --skill attributed-string安装后在 Claude Code 中输入/attributed-string确认已加载。
详细介绍
AttributedString 模式
Foundation AttributedString 的正确 API 结构和模式。涵盖创建样式文本、为范围应用属性、文本对齐、书写方向、行高控制、文本选择与编辑、不连续子字符串,以及 SwiftUI 集成。
此技能的触发时机
在用户遇到以下情况时使用此技能:
- 询问如何创建或操作 AttributedString
- 想要使用字体、颜色、下划线或其他属性来设置文本样式
- 提到文本对齐、书写方向或行高
- 询问如何使用 AttributedString 选择文本或编辑文本
- 想要使用 DiscontiguousAttributedSubstring 或 RangeSet
- 在 SwiftUI 中提到 TextEditor 与 AttributedString
- 询问 Swift 中的富文本格式
- 想要替换或修改 AttributedString 中的文本
- 提到 paragraphStyle、textSelectionAffinity 或 AttributedTextSelection
决策树
What do you need with AttributedString?
|
+-- Create or style text
| |
| +-- Simple inline attributes (font, color)
| | --> Creating and Styling section
| |
| +-- Paragraph-level formatting (alignment, line height)
| --> Text Alignment and Formatting section
|
+-- Control text layout
| |
| +-- Writing direction (LTR / RTL)
| | --> Writing Direction and Line Height section
| |
| +-- Line spacing / height
| --> Writing Direction and Line Height section
|
+-- Edit or select text programmatically
| |
| +-- Replace selection with characters or AttributedString
| | --> Text Selection and Editing section
| |
| +-- Work with multiple non-contiguous ranges
| --> DiscontiguousAttributedSubstring section
|
+-- Display in SwiftUI
--> SwiftUI Integration section
API 可用性
| API | 最低版本 | 说明 |
|---|---|---|
AttributedString | iOS 15 / macOS 12 | NSAttributedString 的 Swift 原生替代方案 |
AttributedString.paragraphStyle | iOS 15 / macOS 12 | 使用 NSMutableParagraphStyle |
AttributedString.writingDirection | iOS 26 / macOS 26 | 2025 年新增 |
AttributedString.LineHeight | iOS 26 / macOS 26 | .exact(points:)、.multiple(factor:)、.loose |
AttributedString.alignment | iOS 26 / macOS 26 | .left、.center、.right |
AttributedTextSelection | iOS 26 / macOS 26 | 以编程方式选择文本 |
replaceSelection(_:withCharacters:) | iOS 26 / macOS 26 | 将所选内容替换为纯文本字符 |
replaceSelection(_:with:) | iOS 26 / macOS 26 | 将所选内容替换为 AttributedString |
DiscontiguousAttributedSubstring | iOS 26 / macOS 26 | 非连续范围选择 |
AttributedString.utf8 | iOS 26 / macOS 26 | UTF-8 代码单元视图 |
使用 AttributedString 的 TextEditor(text:selection:) | iOS 26 / macOS 26 | SwiftUI 富文本编辑 |
.textSelectionAffinity(_:) | iOS 26 / macOS 26 | 控制行边界处的光标亲和性 |
最常见的 5 个错误
| # | 错误 | 修正方法 |
|---|---|---|
| 1 | 在新的 Swift 代码中使用 NSAttributedString | 使用 AttributedString(iOS 15+),获得类型安全的 Swift 原生属性 |
| 2 | 未检查范围是否存在就应用基于范围的属性 | 对 text.range(of:) 的结果始终进行安全解包,再用下标访问 |
| 3 | 忘记 AttributedString 是值类型 | 修改需要使用 var,而非 let;声明为 var 后再赋值属性 |
| 4 | 新的对齐 API 已可用,却仍在创建 NSMutableParagraphStyle | 在 iOS 26+ 上使用 text.alignment = .center,而非手动设置段落样式 |
| 5 | 使用 replaceSelection 时修改原字符串,而非所选内容 | 将选择作为 inout 传入,让 API 为你更新选择范围 |
创建和设置样式
基本初始化
// Plain text
let plain = AttributedString("Hello, world!")
// With attributes applied inline
var bold = AttributedString("Bold text")
bold.font = .boldSystemFont(ofSize: 16)
为范围应用属性
var text = AttributedString("Styled text")
text.foregroundColor = .red
text.backgroundColor = .yellow
text.font = .systemFont(ofSize: 14)
// Attribute on a specific range
if let range = text.range(of: "Styled") {
text[range].underlineStyle = .single
text[range].underlineColor = .blue
}
从子字符串创建
let source = AttributedString("Hello, world!")
if let range = source.range(of: "world") {
let substring = source[range]
let extracted = AttributedString(substring) // standalone copy
}
| 模式 | 结论 |
|---|---|
var text = AttributedString("..."),然后修改 | 正确 |
let text = AttributedString("..."),然后修改 | 无法编译——值类型需要使用 var |
对 text.range(of:)! 强制解包 | 不可靠——请使用 if let 或 guard let |
文本对齐和格式设置
旧版方法(iOS 15+)
var paragraph = AttributedString("Centered paragraph of text")
let style = NSMutableParagraphStyle()
style.alignment = .center
paragraph.paragraphStyle = style
新版方法(iOS 26+)
var paragraph = AttributedString("Centered paragraph of text")
paragraph.alignment = .center
可用的 TextAlignment 值:
| 值 | 说明 |
|---|---|
.left | 左对齐文本 |
.right | 右对齐文本 |
.center | 居中对齐文本 |
书写方向和行高
书写方向(iOS 26+)
var text = AttributedString("Hello عربي")
text.writingDirection = .rightToLeft
| 值 | 说明 |
|---|---|
.leftToRight | 标准的从左到右布局 |
.rightToLeft | 适用于阿拉伯语、希伯来语等语言的从右到左布局 |
行高(iOS 26+)
var multiline = AttributedString(
"This is a paragraph\nwith multiple lines\nof text."
)
// Exact point value
multiline.lineHeight = .exact(points: 32)
// Multiplier of the default line height
multiline.lineHeight = .multiple(factor: 2.5)
// System-defined loose spacing
multiline.lineHeight = .loose
| 模式 | 用例 |
|---|---|
.exact(points:) | 需要固定行高的像素级精准设计 |
.multiple(factor:) | 相对于字体大小按比例缩放 |
.loose | 由系统选择舒适的阅读间距 |
文本选择与编辑
替换选中文本(iOS 26+)
var text = AttributedString("Here is my dog")
var selection = AttributedTextSelection(range: text.range(of: "dog")!)
// Replace with plain characters
text.replaceSelection(&selection, withCharacters: "cat")
// Replace with an AttributedString
let replacement = AttributedString("horse")
text.replaceSelection(&selection, with: replacement)
要点:纯文本使用 withCharacters:,带样式的替换内容使用 with:。
不连续的 AttributedString 子串
同时选择和操作多个不连续的范围(iOS 26+)。
let text = AttributedString("Select multiple parts of this text")
if let range1 = text.range(of: "Select"),
let range2 = text.range(of: "text") {
let rangeSet = RangeSet([range1, range2])
var substring = text[rangeSet] // DiscontiguousAttributedSubstring
substring.backgroundColor = .yellow
// Flatten into a single contiguous AttributedString
let combined = AttributedString(substring)
}
UTF-8 视图
访问原始 UTF-8 代码单元(iOS 26+):
let text = AttributedString("Hello")
for codeUnit in text.utf8 {
print(codeUnit)
}
SwiftUI 集成
使用 AttributedString 和 Selection 的 TextEditor(iOS 26+)
struct SuggestionTextEditor: View {
@State var text: AttributedString = ""
@State var selection = AttributedTextSelection()
var body: some View {
VStack {
TextEditor(text: $text, selection: $selection)
SuggestionsView(
substrings: getSubstrings(
text: text,
indices: selection.indices(in: text)
)
)
}
}
}
文本选择亲和性
控制光标位于行边界时显示在哪一行:
TextEditor(text: $text, selection: $selection)
.textSelectionAffinity(.upstream)
.upstream 会将光标保留在上一行的末尾;.downstream 会将光标移至下一行的开头。
在 SwiftUI 中显示带样式的文本
// Simple display
Text(attributedString)
// With text selection enabled
Text(attributedString)
.textSelection(.enabled)
审核清单
正确性
- 新的 Swift 代码使用
AttributedString(而非NSAttributedString) - 使用
if let或guard let安全解包所有范围查找结果 - 应用属性前,将字符串声明为
var -
replaceSelection通过inout(&selection)传递 selection
平台与版本
- 新 API(alignment、writingDirection、lineHeight、selection)限定在 iOS 26+ / macOS 26+
- 面向 iOS 15-25 的目标使用旧版段落样式方案
- 支持较旧的部署目标时,使用可用性检查(
if #available)包裹较新的 API
SwiftUI
- 富文本编辑使用
TextEditor(text:selection:)重载(iOS 26+) - 在换行处的光标行为很重要时,应用
.textSelectionAffinity - 在显示用户内容的
Text视图上添加.textSelection(.enabled)
无障碍
- 前景色与背景色组合符合对比度要求
- 带样式的文本不只依靠颜色传达含义(添加下划线、粗体或图标)
- 为 RTL 语言内容正确设置书写方向
参考资料
评论
支持 Markdown