text-editing について
SwiftUIでのテキスト表示とリッチテキスト編集を扱うスキルです。Text、AttributedString、選択範囲の書式設定、Markdownの制約を解説します。
主な機能
- 01SwiftUIのテキストスタイル
- 02AttributedStringの書式設定
- 03選択範囲のリッチ編集
- 04カスタム書式制約
- 05インラインMarkdownの解説
活用シーン
- 01SwiftUIビューの読み取り専用テキストを装飾
- 02テキストエディタに書式設定コントロールを追加
- 03iOS 18 APIで選択テキストを整形
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ rshankras/ claude-code-apple-skills --skill text-editingインストール後、Claude Code で/text-editingを入力して読み込まれたか確認します。
詳細
スタイル付きテキストの編集
SwiftUI でスタイル付きテキストを表示し、リッチテキストエディタを構築するためのパターンを紹介します。Text のスタイリング、AttributedString、選択範囲に応じた書式設定を行う TextEditor、カスタム書式定義について説明します。
このスキルが有効になる場面
次のような場合にこのスキルを使用してください。
- スタイル付きまたは書式設定されたテキストを表示したい
- 太字・斜体・下線のコントロールを備えたリッチテキスト編集が必要
- AttributedString または TextEditor について質問する
- テキスト書式設定ツールバーを作成したい
- Text ビューでの Markdown のレンダリングについて質問する
- カスタムテキスト属性または書式設定の制約が必要
- 選択範囲に応じてテキストを書式設定したい
判断ツリー
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
API の利用可能状況
| API | 最小バージョン | 備考 |
|---|---|---|
Text with modifiers | iOS 13 | .font()、.bold()、.italic() |
TextEditor(text:) | iOS 14 | 通常の文字列バインディング |
.foregroundStyle() | iOS 15 | .foregroundColor() より推奨 |
AttributedString | iOS 15 | リッチテキストモデル |
.bold(condition) | iOS 16 | 条件付きの太字・斜体 |
.underline(pattern:) | iOS 16 | 破線、点線のパターン |
TextEditor(text:selection:) | iOS 18 | AttributedString と選択範囲 |
AttributedTextSelection | iOS 18 | 選択範囲に応じた書式設定 |
text.transformAttributes(in:) | iOS 18 | 選択範囲内の属性を変更 |
AttributedTextFormattingDefinition | iOS 18 | カスタム書式制約 |
@Environment(\.fontResolutionContext) | iOS 18 | フォントのプロパティを解決 |
よくある間違いトップ 5
| # | 間違い | 修正方法 |
|---|---|---|
| 1 | 新規プロジェクトで .foregroundColor() を使う | .foregroundStyle() を使います。グラデーションと ShapeStyle に対応しています |
| 2 | テキストに value: を指定せず .animation(.spring()) を使う | iOS 15 で非推奨になりました。必ず value: パラメーターを渡してください |
| 3 | Text で完全な Markdown が使えると思い込む | Text がサポートするのはインライン Markdown のみです。太字、斜体、[リンク]に対応します。リスト、表、コードブロック、画像には対応していません |
| 4 | body の評価ごとに AttributedString のインスタンスを新しく作成する | 複雑な AttributedString オブジェクトは @State にキャッシュするか、body の外の計算プロパティに保存してください |
| 5 | リッチテキストに TextEditor(text: $string) を使う | AttributedString のバインディングを使って TextEditor(text: $attributedString, selection: $selection) を使用してください(iOS 18 以降) |
テキストスタイリングのクイックリファレンス
// 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)
AttributedString
// 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)
使用可能な属性
| 属性 | 型 | 例 |
|---|---|---|
.font | Font | .headline |
.foregroundColor | Color | .red |
.backgroundColor | Color | .yellow |
.underlineStyle | UnderlineStyle | .single |
.underlineColor | Color | .blue |
.strikethroughStyle | UnderlineStyle | .single |
.strikethroughColor | Color | .red |
.inlinePresentationIntent | InlinePresentationIntent | .stronglyEmphasized(太字)、.emphasized(斜体) |
リッチテキスト編集(iOS 18 以降)
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
}
}
}
}
選択範囲の属性を読み取る
// Get current attributes at the selection/cursor
let attributes = selection.typingAttributes(in: text)
let currentColor = attributes.foregroundColor ?? .primary
カスタム書式定義(iOS 18 以降)
エディタで使用できる書式設定オプションを制限します。
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)
Text での Markdown
// Inline markdown support
Text("This is **bold** and *italic* text")
Text("Visit [Apple](https://www.apple.com)")
Text("Code: `inline code`")
Markdown の制限事項
Text がサポートするのはインライン Markdown のみです。
- ✅ 太字(
**text**)、斜体(*text*)、リンク、インラインコード - ❌ 改行、ブロック書式、リスト、表、コードブロック、画像、引用ブロック
レビューチェックリスト
テキスト表示
- 非推奨の
.foregroundColor()ではなく.foregroundStyle()を使用している - 切り捨てられるテキストには
.lineLimit()と.help()のツールチップ、または.textSelection(.enabled)を併用している - ダイナミックタイプに対応している — 固定サイズではなく、システムフォントスタイルを使用している
- 色がダークモードに適応する — システムカラーまたはセマンティックスタイルを使用している
AttributedString
- 複雑な AttributedString を body 内で再作成せず、@State にキャッシュしている
- 範囲検索に安全な
if let range = text.range(of:)パターンを使用している - 適切な属性を使用している(意味上の太字・斜体には
.inlinePresentationIntentを使用)
TextEditor
- リッチテキストでは
TextEditor(text: $attributedString, selection: $selection)を使用します(iOS 18 以降) - 書式設定ツールバーでは
text.transformAttributes(in: &selection)パターンを使用します - 切り替えロジックでは
@Environment(\.fontResolutionContext)を使用してフォントを解決します - 書式設定ボタンにアクセシビリティラベルを設定します
ローカライズ
- ユーザー向けの文字列には
LocalizedStringKeyを使用します - ローカライズされた文字列内の Markdown には
Text(LocalizedStringKey("**Bold** text"))を使用します
参考資料
コメント
Markdown 対応