text-editing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseStyled Text Editing
样式文本编辑
Patterns for displaying styled text and building rich text editors in SwiftUI. Covers Text styling, AttributedString, TextEditor with selection-based formatting, and custom formatting definitions.
在SwiftUI中显示样式文本和构建富文本编辑器的方案。涵盖Text样式设置、AttributedString、带选中文本格式化功能的TextEditor以及自定义格式定义。
When This Skill Activates
适用场景
Use this skill when the user:
- Wants to display styled or formatted text
- Needs rich text editing with bold/italic/underline controls
- Asks about AttributedString or TextEditor
- Wants text formatting toolbars
- Asks about Markdown rendering in Text views
- Needs custom text attributes or formatting constraints
- Wants selection-based text formatting
当用户有以下需求时使用本技能:
- 需要显示带样式或格式的文本
- 需要带有粗体/斜体/下划线控件的富文本编辑功能
- 询问AttributedString或TextEditor相关内容
- 需要文本格式工具栏
- 询问Text视图中的Markdown渲染
- 需要自定义文本属性或格式约束
- 需要基于选中文本的格式化功能
Decision Tree
决策树
What text feature do you need?
|
+- Display styled read-only text
| +- Simple styling (one style) -> Text Styling section below
| +- Mixed styles in one string -> AttributedString section below
| +- Markdown content -> Markdown section below
|
+- Edit plain text
| +- TextEditor(text: $stringBinding)
|
+- Edit rich/styled text
| +- Basic rich editor -> Rich Text Editing section below
| +- With formatting toolbar -> Formatting Controls section below
| +- With custom constraints -> Custom Formatting section below你需要什么文本功能?
|
+- 显示只读样式文本
| +- 简单样式(单一风格)-> 下方的文本样式设置章节
| +- 单个字符串包含混合样式 -> 下方的AttributedString章节
| +- Markdown内容 -> 下方的Markdown章节
|
+- 编辑纯文本
| +- 使用TextEditor(text: $stringBinding)
|
+- 编辑富/样式文本
| +- 基础富文本编辑器 -> 下方的富文本编辑章节
| +- 带格式工具栏 -> 下方的格式控件章节
| +- 带自定义约束 -> 下方的自定义格式章节API Availability
API兼容性
| API | Minimum Version | Notes |
|---|---|---|
| iOS 13 | .font(), .bold(), .italic() |
| iOS 14 | Plain string binding |
| iOS 15 | Preferred over .foregroundColor() |
| iOS 15 | Rich text model |
| iOS 16 | Conditional bold/italic |
| iOS 16 | Dash, dot patterns |
| iOS 18 | AttributedString + selection |
| iOS 18 | Selection-based formatting |
| iOS 18 | Modify attributes in selection |
| iOS 18 | Custom formatting constraints |
| iOS 18 | Resolve font properties |
| API | 最低版本 | 说明 |
|---|---|---|
| iOS 13 | .font(), .bold(), .italic() |
| iOS 14 | 纯字符串绑定 |
| iOS 15 | 优先于.foregroundColor()使用 |
| iOS 15 | 富文本模型 |
| iOS 16 | 条件式粗体/斜体 |
| iOS 16 | 虚线、点线样式 |
| iOS 18 | AttributedString + 选中文本 |
| iOS 18 | 基于选中文本的格式化 |
| iOS 18 | 修改选中文本的属性 |
| iOS 18 | 自定义格式约束 |
| iOS 18 | 解析字体属性 |
Top 5 Mistakes
五大常见错误
| # | Mistake | Fix |
|---|---|---|
| 1 | Using | Use |
| 2 | | Deprecated in iOS 15 — always pass |
| 3 | Expecting full Markdown in | Text only supports inline Markdown: bold, italic, [links]. No lists, tables, code blocks, images |
| 4 | Creating new AttributedString instances on every body evaluation | Cache complex AttributedString objects in @State or computed properties outside body |
| 5 | Using | Use |
| 序号 | 错误 | 修复方案 |
|---|---|---|
| 1 | 在新项目中使用 | 使用 |
| 2 | 文本使用 | iOS 15中已弃用 — 务必传入 |
| 3 | 期望 | Text仅支持行内Markdown:粗体、斜体、[链接]。不支持列表、表格、代码块、图片 |
| 4 | 在每次body求值时创建新的AttributedString实例 | 将复杂的AttributedString对象缓存到@State或body外的计算属性中 |
| 5 | 使用 | 使用 |
Text Styling Quick Reference
文本样式速查
swift
// Font
Text("Hello").font(.headline)
Text("Custom").font(.system(size: 24, design: .rounded))
// Weight
Text("Bold").fontWeight(.bold)
// Color — prefer foregroundStyle over foregroundColor
Text("Styled").foregroundStyle(.red)
Text("Gradient").foregroundStyle(
.linearGradient(colors: [.yellow, .blue], startPoint: .top, endPoint: .bottom)
)
// Decorations
Text("Bold").bold()
Text("Conditional").bold(someCondition)
Text("Underline").underline(true, pattern: .dash, color: .blue)
Text("Strike").strikethrough(true, pattern: .dot, color: .red)
// Layout
Text("Centered").multilineTextAlignment(.center)
Text("Spaced").lineSpacing(10)
Text("Truncated").lineLimit(2).truncationMode(.tail)swift
// 字体
Text("Hello").font(.headline)
Text("Custom").font(.system(size: 24, design: .rounded))
// 字重
Text("Bold").fontWeight(.bold)
// 颜色 — 优先使用foregroundStyle而非foregroundColor
Text("Styled").foregroundStyle(.red)
Text("Gradient").foregroundStyle(
.linearGradient(colors: [.yellow, .blue], startPoint: .top, endPoint: .bottom)
)
// 装饰效果
Text("Bold").bold()
Text("Conditional").bold(someCondition)
Text("Underline").underline(true, pattern: .dash, color: .blue)
Text("Strike").strikethrough(true, pattern: .dot, color: .red)
// 布局
Text("Centered").multilineTextAlignment(.center)
Text("Spaced").lineSpacing(10)
Text("Truncated").lineLimit(2).truncationMode(.tail)AttributedString
AttributedString
swift
// Create and style ranges
var text = AttributedString("Red and Blue")
if let redRange = text.range(of: "Red") {
text[redRange].foregroundColor = .red
text[redRange].font = .headline
}
if let blueRange = text.range(of: "Blue") {
text[blueRange].foregroundColor = .blue
text[blueRange].underlineStyle = .single
}
// Display
Text(text)swift
// 创建并设置文本范围样式
var text = AttributedString("Red and Blue")
if let redRange = text.range(of: "Red") {
text[redRange].foregroundColor = .red
text[redRange].font = .headline
}
if let blueRange = text.range(of: "Blue") {
text[blueRange].foregroundColor = .blue
text[blueRange].underlineStyle = .single
}
// 显示文本
Text(text)Available Attributes
可用属性
| Attribute | Type | Example |
|---|---|---|
| Font | |
| Color | |
| Color | |
| UnderlineStyle | |
| Color | |
| UnderlineStyle | |
| Color | |
| InlinePresentationIntent | |
| 属性 | 类型 | 示例 |
|---|---|---|
| Font | |
| Color | |
| Color | |
| UnderlineStyle | |
| Color | |
| UnderlineStyle | |
| Color | |
| InlinePresentationIntent | |
Rich Text Editing (iOS 18+)
富文本编辑(iOS 18+)
swift
struct RichTextEditorView: View {
@State private var text = AttributedString("Select text to format")
@State private var selection = AttributedTextSelection()
@Environment(\.fontResolutionContext) private var fontResolutionContext
var body: some View {
VStack {
TextEditor(text: $text, selection: $selection)
.frame(height: 200)
HStack {
Button(action: toggleBold) {
Image(systemName: "bold")
}
Button(action: toggleItalic) {
Image(systemName: "italic")
}
Button(action: toggleUnderline) {
Image(systemName: "underline")
}
}
}
}
private func toggleBold() {
text.transformAttributes(in: &selection) {
let font = $0.font ?? .default
let resolved = font.resolve(in: fontResolutionContext)
$0.font = font.bold(!resolved.isBold)
}
}
private func toggleItalic() {
text.transformAttributes(in: &selection) {
let font = $0.font ?? .default
let resolved = font.resolve(in: fontResolutionContext)
$0.font = font.italic(!resolved.isItalic)
}
}
private func toggleUnderline() {
text.transformAttributes(in: &selection) {
if $0.underlineStyle != nil {
$0.underlineStyle = nil
} else {
$0.underlineStyle = .single
}
}
}
}swift
struct RichTextEditorView: View {
@State private var text = AttributedString("Select text to format")
@State private var selection = AttributedTextSelection()
@Environment(\.fontResolutionContext) private var fontResolutionContext
var body: some View {
VStack {
TextEditor(text: $text, selection: $selection)
.frame(height: 200)
HStack {
Button(action: toggleBold) {
Image(systemName: "bold")
}
Button(action: toggleItalic) {
Image(systemName: "italic")
}
Button(action: toggleUnderline) {
Image(systemName: "underline")
}
}
}
}
private func toggleBold() {
text.transformAttributes(in: &selection) {
let font = $0.font ?? .default
let resolved = font.resolve(in: fontResolutionContext)
$0.font = font.bold(!resolved.isBold)
}
}
private func toggleItalic() {
text.transformAttributes(in: &selection) {
let font = $0.font ?? .default
let resolved = font.resolve(in: fontResolutionContext)
$0.font = font.italic(!resolved.isItalic)
}
}
private func toggleUnderline() {
text.transformAttributes(in: &selection) {
if $0.underlineStyle != nil {
$0.underlineStyle = nil
} else {
$0.underlineStyle = .single
}
}
}
}Reading Selection Attributes
读取选中文本属性
swift
// Get current attributes at the selection/cursor
let attributes = selection.typingAttributes(in: text)
let currentColor = attributes.foregroundColor ?? .primary
// Set color on selection
text.transformAttributes(in: &selection) {
$0.foregroundColor = .blue
}swift
// 获取选中文本/光标位置的当前属性
let attributes = selection.typingAttributes(in: text)
let currentColor = attributes.foregroundColor ?? .primary
// 为选中文本设置颜色
text.transformAttributes(in: &selection) {
$0.foregroundColor = .blue
}Custom Formatting Definition (iOS 18+)
自定义格式定义(iOS 18+)
Constrain which formatting options are available in the editor:
swift
struct MyTextFormatting: AttributedTextFormattingDefinition {
typealias Scope = AttributeScopes.SwiftUIAttributes
static let fontWeight = ValueConstraint(
\.font,
constraint: { font in
guard let font = font else { return nil }
let weight = font.resolve().weight
return font.weight(weight == .bold ? .regular : .bold)
}
)
}
// Apply to TextEditor
TextEditor(text: $text, selection: $selection)
.textFormattingDefinition(MyTextFormatting.self)限制编辑器中可用的格式化选项:
swift
struct MyTextFormatting: AttributedTextFormattingDefinition {
typealias Scope = AttributeScopes.SwiftUIAttributes
static let fontWeight = ValueConstraint(
\.font,
constraint: { font in
guard let font = font else { return nil }
let weight = font.resolve().weight
return font.weight(weight == .bold ? .regular : .bold)
}
)
}
// 应用到TextEditor
TextEditor(text: $text, selection: $selection)
.textFormattingDefinition(MyTextFormatting.self)Markdown in Text
Text中的Markdown支持
swift
// Inline markdown support
Text("This is **bold** and *italic* text")
Text("Visit [Apple](https://www.apple.com)")
Text("Code: `inline code`")swift
// 支持行内Markdown
Text("This is **bold** and *italic* text")
Text("Visit [Apple](https://www.apple.com)")
Text("Code: `inline code`")Markdown Limitations
Markdown限制
Text supports only inline Markdown:
- ✅ Bold (), italic (
**text**), links, inline code*text* - ❌ Line breaks, block formatting, lists, tables, code blocks, images, block quotes
Text仅支持行内Markdown:
- ✅ 粗体 ()、斜体 (
**text**)、链接、行内代码*text* - ❌ 换行、块级格式、列表、表格、代码块、图片、块引用
Review Checklist
检查清单
Text Display
文本显示
- Using instead of deprecated
.foregroundStyle().foregroundColor() - paired with
.lineLimit()tooltip or.help()for truncated text.textSelection(.enabled) - Dynamic Type supported — using system font styles, not hardcoded sizes
- Colors adapt to dark mode — using system colors or semantic styles
- 使用替代已弃用的
.foregroundStyle().foregroundColor() - 为截断文本搭配和
.lineLimit()提示框或.help().textSelection(.enabled) - 支持动态类型 — 使用系统字体样式,而非硬编码字号
- 颜色适配深色模式 — 使用系统颜色或语义化样式
AttributedString
AttributedString
- Complex AttributedString cached in @State, not recreated in body
- Range lookups use safe pattern
if let range = text.range(of:) - Appropriate attributes used (for semantic bold/italic)
.inlinePresentationIntent
- 复杂的AttributedString缓存到@State中,不在body中重新创建
- 使用安全的模式查找文本范围
if let range = text.range(of:) - 使用合适的属性(语义化粗体/斜体使用)
.inlinePresentationIntent
TextEditor
TextEditor
- Rich text uses (iOS 18+)
TextEditor(text: $attributedString, selection: $selection) - Formatting toolbar uses pattern
text.transformAttributes(in: &selection) - Font resolution uses for toggle logic
@Environment(\.fontResolutionContext) - Accessibility labels provided for formatting buttons
- 富文本使用(iOS 18+)
TextEditor(text: $attributedString, selection: $selection) - 格式工具栏使用模式
text.transformAttributes(in: &selection) - 字体解析使用实现切换逻辑
@Environment(\.fontResolutionContext) - 为格式按钮提供无障碍标签
Localization
本地化
- User-facing strings use
LocalizedStringKey - Markdown in localized strings uses
Text(LocalizedStringKey("**Bold** text"))
- 用户可见字符串使用
LocalizedStringKey - 本地化字符串中的Markdown使用
Text(LocalizedStringKey("**Bold** text"))