WWDC Quick Look 💓 By SwiftGGTeam
Tailor macOS windows with SwiftUI

Tailor macOS windows with SwiftUI

元の動画を見る

Highlight

macOS Sequoia の SwiftUI では、.toolbarBackgroundVisibility().windowMinimizeBehavior().defaultWindowPlacement() などのウィンドウカスタマイズ API が追加され、NSWindow ブリッジなしでタイトルバーの非表示、最小化ボタンの無効化、動的なウィンドウ位置の計算が可能になりました(02:02)。


主要内容

このセッションでは Destination Video サンプルアプリを使い、ウィンドウタイプごとに外観と動作をカスタマイズする方法を示します。開発者が直面する課題は、SwiftUI のデフォルトのウィンドウスタイルが十分に柔軟ではないことです。動画プレーヤーはコンテンツに応じてサイズを動的に計算する必要があり、About ウィンドウには最小化ボタンが不要で、メインウィンドウではコンテンツを上端まで伸ばしたい。これらの要件は以前は NSWindow 用の Objective-C ブリッジコードが必要でした。

Apple は macOS Sequoia で 3 種類の新 API を導入しました。ツールバーのカスタマイズでタイトルとツールバー背景を非表示にでき、ウィンドウ動作の制御で最小化ボタンと状態復元を無効にでき、ウィンドウ位置の制御でコンテンツサイズと画面サイズに基づいて理想的な位置を動的に計算できます。


詳細

ツールバーのカスタマイズ

Destination Video のメインウィンドウには、ウィンドウ上端まで伸ばす必要がある大きな画像があります。デフォルトでは、ツールバーとタイトル領域が上部のスペースを占めます(03:00)。

WindowGroup("Destination Video", id: "main") {
    MainView()
        .toolbar(removing: .title)
        .toolbarBackgroundVisibility(.hidden, for: .windowToolbar)
}
  • .toolbar(removing: .title) はウィンドウタイトルバーを非表示にします(タイトルはウィンドウメタデータに残り、メインメニューとアクセシビリティで引き続き使用されます)
  • .toolbarBackgroundVisibility(.hidden, for: .windowToolbar) はツールバー背景を削除し、コンテンツをウィンドウ上端まで伸ばします

ウィンドウ動作の制御

About ウィンドウは静的な情報を表示し、メインメニューからいつでもアクセスできるため、最小化ボタンや状態復元は不要です(05:12)。

Window("About Destination Video", id: "about") {
    AboutView()
        .toolbar(removing: .title)
        .toolbarBackgroundVisibility(.hidden, for: .windowToolbar)
        .containerBackground(.thickMaterial, for: .window)
        .windowMinimizeBehavior(.disabled)
        .restorationBehavior(.disabled)
}
  • .containerBackground(.thickMaterial, for: .window) はデフォルトの背景色をすりガラス素材に置き換えます
  • .windowMinimizeBehavior(.disabled) は黄色の最小化ボタンを無効にします
  • .restorationBehavior(.disabled) は状態復元をオフにします(アプリ終了後にこのウィンドウは自動的に再オープンされません)

ウィンドウ位置の制御

動画プレーヤーウィンドウは、動画サイズと画面サイズに基づいて位置とサイズを動的に計算する必要があります(07:02)。

Window("Video Player", id: "player") {
    PlayerView(video: video)
        .defaultWindowPlacement { content, context in
            let idealSize = content.sizeThatFits(.unspecified)
            let visibleRect = context.display.visibleRect

            let size = idealSize.clamped(to: visibleRect.size)
            return .default(.center, size: size)
        }
        .windowIdealPlacement { content, context in
            let visibleRect = context.display.visibleRect
            let zoomedSize = zoomToFit(content.sizeThatFits(.unspecified), in: visibleRect.size)
            return .default(.center, size: zoomedSize)
        }
}
  • .defaultWindowPlacement() は新しいウィンドウの初期位置とサイズを設定します
    • content.sizeThatFits() でコンテンツの理想サイズを計算
    • context.display.visibleRect で利用可能な画面領域を取得(メニューバーと Dock を自動的に差し引き)
    • .clamped(to:) でウィンドウが画面外に出ないようにします
  • .windowIdealPlacement() はユーザーが Zoom ボタンをクリックしたときのウィンドウサイズを制御します
    • zoomToFit() はアスペクト比を保ちながらウィンドウを最大化するカスタム関数

キーポイント:

  • 動画はネイティブサイズで表示され、画面を超える場合は自動的に縮小されます
  • ウィンドウはデフォルトで中央に配置されます
  • Zoom 動作はカスタマイズ可能で、システムのデフォルトロジックに制限されません

重要ポイント

1. ウィンドウタイプごとに独立した設定を行う

メインウィンドウ、About ウィンドウ、設定ウィンドウ、プレーヤーウィンドウでは、ツールバーの要件、最小化動作、状態復元の戦略が異なるべきです。各ウィンドウタイプに適切な modifier を追加する 10 分の投資で、ユーザー体験が目に見えて向上します。

2. ユーザーが最も感じる変更を優先する

.toolbarBackgroundVisibility(.hidden) はコンテンツレイアウトの自由度を高め、.defaultWindowPlacement() は初期ウィンドウサイズを修正します。この 2 つの変更は移行コストが最も低く、ユーザーへのインパクトが最も大きいです。状態復元と最小化動作の変更は、既存の操作習慣を変えるため、より慎重に行う必要があります。

3. 新 API を活用して言語間ブリッジを避ける

以前は NSWindow 用の Objective-C ブリッジコードが必要だった要件は、SwiftUI にネイティブ modifier があります。新規プロジェクトでは SwiftUI API を優先し、メンテナンスコストを下げましょう。


関連セッション

コメント

GitHub Issues · utterances