SwiftUI の Button(role: .destructive) は List 内で SF Symbol を赤くしない
AI 主体で執筆設定画面で「削除」や「リセット」のような破壊的ボタンを作ったとき、テキストは赤いのにアイコンだけ青い——という経験はありませんか。symbolRenderingMode を変えても、シンボルを変えても直らず、最終的に原因と Apple の設計パターンに気づいた記録です。
現象:テキストは赤、アイコンは青
SwiftUI の List 内で Button(role: .destructive) と Label を組み合わせると、テキストには .destructive の赤色が適用されますが、SF Symbol のアイコンにはアクセントカラー(青)が使われたままになります。
Button(role: .destructive) {
// action
} label: {
Label("Drive から再取得",
systemImage: "exclamationmark.arrow.trianglehead.counterclockwise.rotate.90")
}
実行すると、こうなります:
試したこと(すべて効果なし)
1. symbolRenderingMode(.monochrome)
階層的なレンダリングが原因かと思い、モノクロを強制しましたが変わりません。モノクロにはなるものの、色はアクセントカラーのままです。
Label("Drive から再取得", systemImage: "...")
.symbolRenderingMode(.monochrome)
2. シンプルなシンボルに変更
arrow.counterclockwise のようなデフォルトでモノクロのシンボルに変えても同じです。シンボルの種類に関係なく、List 内の destructive ボタンではアイコンが青のままでした。
Label("Drive から再取得", systemImage: "arrow.counterclockwise")
結論: これはシンボルのレンダリングモードの問題ではなく、Button(role: .destructive) が List 内で SF Symbol の色を変更しないという SwiftUI の挙動そのものです。
対処法
方法 A:.foregroundStyle(.red) を明示する
Label に直接 .foregroundStyle(.red) を指定すれば、アイコンとテキストの両方が赤になります。
Button(role: .destructive) {
// action
} label: {
Label("Drive から再取得", systemImage: "arrow.counterclockwise")
.foregroundStyle(.red)
}
role: .destructive はアクセシビリティ(VoiceOver で破壊的操作と伝える)のために残しつつ、色は明示的に指定します。
注意点として、.foregroundStyle(.red) を指定すると .disabled(true) 時の自動グレーアウトが効かなくなる可能性があります。.opacity で明示的に制御しているなら問題ありません。
方法 B:アイコンを使わない
Apple 純正の設定アプリでは、破壊的操作のボタンはほぼテキストのみです。
- 「サインアウト」——アイコンなし、赤テキスト
- 「すべてのコンテンツと設定を消去」——アイコンなし、赤テキスト
- 「アカウントを削除」——アイコンなし、赤テキスト
Apple 自身がこの問題を回避しているとも言えますし、破壊的操作を視覚的に区別するための意図的なデザイン選択とも解釈できます。
どちらを選ぶべきか
画面内の他のボタンとの一貫性で判断するのが良いです。
- 他のボタンがすべてアイコン付きなら → 方法 A(
.foregroundStyle(.red))で揃える - 破壊的ボタンが独立したセクションにあるなら → 方法 B(テキストのみ)で Apple パターンに倣う
今回の 60d Memo では、設定画面の独立行ボタンがすべてアイコン付きだったため、方法 A を採用しました。1箇所だけアイコンがないと、かえって不自然だったためです。
Button(role: .destructive) {
showRebuildFromDriveConfirm = true
} label: {
// .destructive role does not tint SF Symbols in List rows
Label(String(localized: "Re-download from Drive"),
systemImage: "arrow.counterclockwise")
.foregroundStyle(.red)
}
.disabled(syncing)
.opacity(syncing ? 0.4 : 1)
まとめ
Button(role: .destructive)は List 内でテキストだけ赤くする。SF Symbol のアイコン色は変わらない。.symbolRenderingMode(.monochrome)でも解決しない。シンボルの種類にも依存しない。- 対処法は
.foregroundStyle(.red)の明示か、アイコンを外す(Apple パターン)の二択。 - 画面全体のデザインの一貫性で判断する。