ハイライト
iOS 17 と macOS Sonoma では StandBy、デスクトップ Widget、iPad のロック画面など新しい表示場所が追加されました。WidgetKit には
containerBackground、contentMargins、widgetAccentable、containerBackgroundRemovableなどの API が加わり、Widget が各環境の背景・余白・描画スタイルに自動適応できるようになりました。
主要内容
課題: Widget はかつてホーム画面だけだった
iOS 14 で WidgetKit が登場したとき、Widget の表示場所はホーム画面だけでした。開発者は 1 種類の背景スタイル、1 セットの余白ルール、1 つの色の描画方式だけを考えればよかったのです。
状況は変わりました。iOS 17 では StandBy モードが追加され、iPhone を横向きに充電すると Widget がダークな全画面 UI に表示されます。macOS Sonoma では Widget をデスクトップに直接置けます。iPad のロック画面でも Widget が使えるようになりました。
新しい表示場所ごとに見た目の要求が異なります。StandBy ではシステムが Widget をインテリジェントに着色します。デスクトップ Widget では背景を外して壁紙に溶け込ませる必要があるかもしれません。ホーム画面 Widget は従来どおり鮮やかな背景を保つべきです。1 つのコードベースですべての環境に対応するのが、新たな課題になりました。
Apple のアプローチ: 環境を意識した Widget API
WidgetKit には 3 つの中核機能が追加されました。
- コンテナ背景(
containerBackground): Widget の背景がどうあるべきかを宣言し、表示場所に応じてシステムが調整します。 - コンテンツ余白の無効化(
contentMarginsDisabled): 端から端まで描画が必要な場合(StandBy など)、開発者が自分でパディングを制御します。 - アクセント色の描画(
widgetAccentable): StandBy などのダークなスマート着色モードで、システムが着色すべきテキストと要素を指定します。
さらに containerBackgroundRemovable があり、デスクトップ Widget の背景をシステムが除去できるため、壁紙に透明に溶け込む表示が可能になります。
共通の考え方は、開発者が意図を宣言し、システムが実行環境に応じて具体的な描画を決める、というものです。
詳細
デフォルト余白を無効化する(02:08)
iOS 17 では、Widget にはデフォルトでシステムが追加するコンテンツ余白があります。レイアウトを完全に制御したい Widget — 単色やグラデーションで Widget 全体を埋めるようなもの — では、このデフォルト余白が不自然な空白を残します。
contentMarginsDisabled() でデフォルト余白をオフにし、widgetContentMargins 環境値でパディングを手動制御します。
struct SafeAreasWidgetView: View {
@Environment(\.widgetContentMargins) var margins
var body: some View {
ZStack {
Color.blue
Group {
Color.lightBlue
Text("Hello, world!")
}
.padding(margins)
}
}
}
struct SafeAreasWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(...) {_ in
SafeAreasWidgetView()
}
.contentMarginsDisabled()
}
}
キーポイント:
WidgetConfigurationでcontentMarginsDisabled()を呼び、システム余白を無効にします。@Environment(\.widgetContentMargins)でシステムが適用するはずだった余白値を読み取り、必要な場所に padding を追加できます。- 全面背景の Widget に向いたパターンです。内側コンテンツに手動で余白を付け、外側の背景は Widget 全体を覆います。
コンテナ背景: 1 つのコードですべての場所に対応(03:19)
containerBackground(for:alignment:content:) は iOS 17 / macOS Sonoma で最も重要な WidgetKit の新 API です。View に直接背景を設定する従来のやり方に代わります。
struct EmojiRangerWidgetEntryView: View {
var entry: Provider.Entry
@Environment(\.widgetFamily) var family
var body: some View {
switch family {
case .systemSmall:
ZStack {
AvatarView(entry.hero)
.widgetURL(entry.hero.url)
.foregroundColor(.white)
}
.containerBackground(for: .widget) {
Color.gameBackground
}
}
// additional cases
}
}
キーポイント:
containerBackground(for: .widget)は「この部分が Widget の背景である」とシステムに伝えます。表示環境に応じて描画が変わります。- StandBy では背景が除去または薄くされることがあります。デスクトップではユーザーが背景を外して壁紙に溶け込ませることもできます。
for: .widgetを付けない通常の背景(ZStack内のColor.blueなど)は、システムのスマート調整の対象になりません。@Environment(\.widgetFamily)で small / medium / large / accessory 系のサイズを区別し、1 つの Widget エントリでレイアウトを分岐できます。
アクセント色: StandBy での自動着色(03:48)
StandBy モードでは、システムが Widget をスマートに着色し、カラフルなコンテンツをダーク環境に合ったモノクロやデュオトーンに変換します。すべての要素を着色すべきではありません。widgetAccentable() でシステムが着色すべきテキストを指定します。
var body: some View {
switch family {
case .accessoryRectangular:
HStack(alignment: .center, spacing: 0) {
VStack(alignment: .leading) {
Text(entry.hero.name)
.font(.headline)
.widgetAccentable()
Text("Level \(entry.hero.level)")
Text(entry.hero.fullHealthDate, style: .timer)
}.frame(maxWidth: .infinity, alignment: .leading)
Avatar(hero: entry.hero, includeBackground: false)
}
.containerBackground(for: .widget) {
Color.gameBackground
}
// additional cases
}
キーポイント:
widgetAccentable()でテキストをマークすると、StandBy の着色モードでシステムが優先的に着色します。- マークされていないテキストは着色時に控えめに表示され、視覚的な階層が生まれます。
accessoryRectangularはロック画面と StandBy で使われるコンパクトな矩形サイズです。- この modifier はシステムがスマート着色を行うときだけ効きます。通常のホーム画面 Widget には影響しません。
除去可能な背景: macOS デスクトップに溶け込む(04:22)
macOS Sonoma では Widget をデスクトップに置けます。ユーザーは Widget の背景を除去して、デスクトップの一部のように見せることもできます。写真 Widget では背景を外すと、写真が壁紙の上に浮いているように見えます。
struct PhotoWidget: Widget {
public var body: some WidgetConfiguration {
StaticConfiguration(...) { entry in
PhotoWidgetView(entry: entry)
}
.containerBackgroundRemovable(false)
}
}
キーポイント:
containerBackgroundRemovable(false)は「この Widget の背景は除去できない」と宣言します。写真 Widget は通常falseにします。写真自体がコンテンツなので、背景を外すと Widget が消えたのと同じになります。true(デフォルト)の場合、macOS デスクトップでユーザーが Widget 背景を除去できます。- この API が目に見える効果を持つのは macOS Sonoma だけですが、すべてのプラットフォームで同じコードを書けます。システムがプラットフォーム差を処理します。
重要ポイント
これらの新 API を使って取り組める方向の例です。
-
StandBy 専用時計 Widget を作る:
widgetAccentable()で StandBy の着色モードでも時刻数字を目立たせ、装飾要素は薄くする。containerBackground(for: .widget)でダーク背景を用意し、StandBy の暗い環境でネイティブに見せる。入口はStaticConfiguration+accessoryRectangularレイアウト。 -
既存 Widget のデスクトップ体験をアップグレードする: ホーム画面 Widget があるなら、2 行追加するだけで macOS Sonoma デスクトップでもよく見える。背景色をすべて
.containerBackground(for: .widget)に移し、重要なテキストに.widgetAccentable()を付ける。プラットフォーム分岐は不要。 -
デスクトップ向け写真 Widget を作る:
containerBackgroundRemovable(false)で写真 Widget の背景除去を防ぐ。ユーザーが Widget をデスクトップにドラッグしても、写真が壁紙にシームレスに溶け込む。PhotoPickerと組み合わせて表示アルバムを選ばせる。 -
「余白なし」天気 Widget を作る:
contentMarginsDisabled()+widgetContentMarginsで余白を手動制御し、グラデーション背景が Widget 全体を覆う効果を実現する。没入感のある天気表示 — 空のグラデーションが端から端まで — に向いています。 -
既存 Widget に accessory 系サイズを追加する:
systemSmall/Medium/Largeだけなら、accessoryRectangularとaccessoryCircularの case を追加して iPhone のロック画面と StandBy に出せる。データモデルは再利用し、レイアウトだけ変える。
関連セッション
- What’s new in SwiftUI — Widget 関連の新 modifier とレイアウト API を含む SwiftUI の最新アップデート
- Explore enhancements to App Intents — Widget にインタラクティブ性をもたらす App Intents の最新強化
- Build programmatic UI with Xcode Previews — 異なるサイズと環境で Widget を Xcode Previews でプレビュー
- Design for spatial computing — 各プラットフォームと表示環境で一貫した UI を設計する方法(Widget の適応を含む)
コメント
GitHub Issues · utterances