mcpField
M
maui-safe-area

maui-safe-area

github.com/dotnetデザイン・UI5.8k3.3k 回インストール1 日前に更新
リポジトリ

maui-safe-area について

.NET MAUI 開発者向けに、.NET 10 以降の `SafeAreaEdges` API を使ったセーフエリアとエッジツーエッジレイアウトを解説します。辺ごとの制御、キーボード領域への対応、旧 API からの移行を扱います。

主な機能

  1. 01辺ごとのセーフエリア設定
  2. 02エッジツーエッジレイアウトの構築
  3. 03キーボード領域への対応
  4. 04Blazor Hybrid の CSS インセット連携
  5. 05旧セーフエリア API からの移行

活用シーン

  1. 01フォームをシステムバーやキーボードから保護
  2. 02写真や動画の没入型画面の構築
  3. 03キーボードに対応するチャットレイアウトの作成
  4. 04.NET 10 のセーフエリア処理への更新

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dotnet/skills --skill maui-safe-area

インストール後、Claude Code で/maui-safe-areaを入力して読み込まれたか確認します。

同梱ファイルreferences/safe-area-api.md

詳細

Safe Area とエッジツーエッジレイアウト(.NET 10 以降)

.NET 10 では、従来の iOS 専用 UseSafeArea とレイアウトレベルの IgnoreSafeArea プロパティに代わる、まったく新しいクロスプラットフォームの Safe Area API が導入されています。新しい SafeAreaEdges プロパティと SafeAreaRegions フラグ列挙型を使うと、単一の API から Android、iOS、Mac Catalyst 上で、コントロールごとに各辺の Safe Area を管理できます。

これは .NET 10 で新たに導入された API です。 プロジェクトのターゲットが .NET 9 以前の場合、これらの API は存在しません。その場合は、従来の ios:Page.UseSafeArea プロパティと Layout.IgnoreSafeArea プロパティを使用するよう開発者に案内してください。

使用する場面

  • .NET 10 へのアップグレード後、コンテンツがステータスバー、ノッチ、Dynamic Island、ホームインジケーターに重なる
  • エッジツーエッジ/イマーシブなレイアウト(フォトビューアー、動画プレーヤー、マップ)を実装する
  • チャットやフォームの UI でキーボードを避ける
  • ios:Page.UseSafeArea、Layout.IgnoreSafeArea、または WindowSoftInputModeAdjust.Resize から移行する
  • CSS env(safe-area-inset-*) との連携が必要な Blazor Hybrid アプリ
  • エッジツーエッジのヘッダーと Safe Area を尊重する本文を組み合わせたレイアウト

使用しない場面

  • .NET 9 以前をターゲットにするプロジェクト — 従来の iOS 専用 API を使用してください
  • システムバーやキーボードに関係しない一般的なページレイアウトに関する質問 — 標準のレイアウトガイダンスを使用してください
  • アプリのライフサイクルやナビゲーション構造 — maui-app-lifecycle または Shell のガイダンスを使用してください
  • テーマ設定やビジュアルスタイル — maui-theming スキルを使用してください

入力

  • ターゲットフレームワーク:新しい API を使用するには net10.0-* 以降である必要があります
  • ターゲットプラットフォーム:Android、iOS、Mac Catalyst(Windows にはシステムバーのインセットはありません)
  • UI の方式:XAML/C#、Blazor Hybrid、または MauiReactor

SafeAreaRegions 列挙型

[Flags]
public enum SafeAreaRegions
{
    None      = 0,       // Edge-to-edge — no safe area padding
    SoftInput = 1 << 0,  // Pad to avoid the on-screen keyboard
    Container = 1 << 1,  // Stay inside status bar, notch, home indicator
    Default   = -1,      // Use the platform default for the control type
    All       = 1 << 15  // Respect all safe area insets (most restrictive)
}

SoftInput と Container は組み合わせ可能なフラグです。 SafeAreaRegions.Container | SafeAreaRegions.SoftInput = システムバー と キーボードを尊重します。

SafeAreaEdges 構造体

public readonly struct SafeAreaEdges
{
    public SafeAreaRegions Left { get; }
    public SafeAreaRegions Top { get; }
    public SafeAreaRegions Right { get; }
    public SafeAreaRegions Bottom { get; }

    // Uniform — same value for all four edges
    public SafeAreaEdges(SafeAreaRegions uniformValue)

    // Horizontal / Vertical
    public SafeAreaEdges(SafeAreaRegions horizontal, SafeAreaRegions vertical)

    // Per-edge
    public SafeAreaEdges(SafeAreaRegions left, SafeAreaRegions top,
                         SafeAreaRegions right, SafeAreaRegions bottom)
}

静的プリセット:SafeAreaEdges.None、SafeAreaEdges.All、SafeAreaEdges.Default。

XAML 型コンバーター

Thickness と同様のカンマ区切り構文に従います。


SafeAreaEdges="Container"


SafeAreaEdges="Container, SoftInput"


SafeAreaEdges="Container, Container, Container, SoftInput"

コントロールの既定値

コントロール既定値備考
ContentPageNoneエッジツーエッジです。Android では .NET 9 からの破壊的変更です。
Layout(Grid、StackLayout など)Containerバー/ノッチを尊重し、キーボードの下に表示されます
ScrollViewDefaultiOS ではコンテンツのインセットが自動設定されます。効果があるのは Container と None のみです。
ContentViewNone親の動作を継承します
BorderNone親の動作を継承します

.NET 9 からの破壊的変更

ContentPage の既定値が None に変更

.NET 9 では、Android の ContentPage は Container のように動作していました。.NET 10 では、すべてのプラットフォームで既定値が None です。アップグレード後に Android のコンテンツがステータスバーの背後に表示される場合:


<ContentPage>


<ContentPage SafeAreaEdges="Container">
WindowSoftInputModeAdjust.Resize の置き換え

WindowSoftInputModeAdjust.Resize は引き続き存在し、コンパイルもできます(削除も非推奨化もされていません)が、Android 専用です。クロスプラットフォームでキーボードを避けるには、ContentPage に SafeAreaEdges="All"(または SoftInput リージョン)を設定することをおすすめします。

使用パターン

エッジツーエッジのイマーシブコンテンツ

レイアウトの既定値は Container なので、ページとレイアウトの両方に None を設定します。

<ContentPage SafeAreaEdges="None">
    <Grid SafeAreaEdges="None">
        <Image Source="background.jpg" Aspect="AspectFill" />
        <VerticalStackLayout Padding="20" VerticalOptions="End">
            <Label Text="Overlay text" TextColor="White" FontSize="24" />
        </VerticalStackLayout>
    </Grid>
</ContentPage>
フォームと重要なコンテンツ
<ContentPage SafeAreaEdges="All">
    <VerticalStackLayout Padding="20">
        <Label Text="Safe content" FontSize="18" />
        <Entry Placeholder="Enter text" />
        <Button Text="Submit" />
    </VerticalStackLayout>
</ContentPage>
キーボードに対応するチャットレイアウト
<ContentPage>
    <Grid RowDefinitions="*,Auto"
          SafeAreaEdges="Container, Container, Container, SoftInput">
        <ScrollView Grid.Row="0">
            <VerticalStackLayout Padding="20" Spacing="10">
                <Label Text="Messages" FontSize="24" />
            </VerticalStackLayout>
        </ScrollView>
        <Border Grid.Row="1" BackgroundColor="LightGray" Padding="20">
            <Grid ColumnDefinitions="*,Auto" Spacing="10">
                <Entry Placeholder="Type a message..." />
                <Button Grid.Column="1" Text="Send" />
            </Grid>
        </Border>
    </Grid>
</ContentPage>
混在レイアウト:エッジツーエッジのヘッダー+Safe Area 対応の本文+キーボードフッター
<ContentPage SafeAreaEdges="None">
    <Grid RowDefinitions="Auto,*,Auto">
        <Grid BackgroundColor="{StaticResource Primary}">
            <Label Text="App Header" TextColor="White" Margin="20,40,20,20" />
        </Grid>
        <ScrollView Grid.Row="1" SafeAreaEdges="Container">
            
            <VerticalStackLayout Padding="20">
                <Label Text="Main content" />
            </VerticalStackLayout>
        </ScrollView>
        <Grid Grid.Row="2" SafeAreaEdges="SoftInput"
              BackgroundColor="LightGray" Padding="20">
            <Entry Placeholder="Type a message..." />
        </Grid>
    </Grid>
</ContentPage>
プログラムによる設定(C#)
var page = new ContentPage
{
    SafeAreaEdges = SafeAreaEdges.All
};

var grid = new Grid
{
    SafeAreaEdges = new SafeAreaEdges(
        left: SafeAreaRegions.Container,
        top: SafeAreaRegions.Container,
        right: SafeAreaRegions.Container,
        bottom: SafeAreaRegions.SoftInput)
};

選択の指針

シナリオSafeAreaEdges の値
フォーム、重要な入力欄All
フォトビューアー、動画プレーヤー、ゲームNone(ページとレイアウトの両方)
固定ヘッダー/フッター付きのスクロール可能なコンテンツContainer
下部入力バーがあるチャット/メッセージング辺ごとに設定:Container, Container, Container, SoftInput
Blazor Hybrid アプリページには None を設定し、インセットには CSS env() を使用

Blazor Hybrid の統合

Blazor Hybrid アプリでは、二重のパディングを避けるために、CSS でセーフエリアを処理します。

  1. ページを画面端まで表示します(.NET 10 のデフォルト):
<ContentPage SafeAreaEdges="None">
    <BlazorWebView HostPage="wwwroot/index.html">
        <BlazorWebView.RootComponents>
            <RootComponent Selector="#app" ComponentType="{x:Type local:Routes}" />
        </BlazorWebView.RootComponents>
    </BlazorWebView>
</ContentPage>
  1. index.html に viewport-fit=cover を追加します:
<meta name="viewport" content="width=device-width, initial-scale=1.0,
      maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
  1. CSS の env() 関数を使用します:
body {
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
}

利用可能な CSS 環境変数:env(safe-area-inset-top)、env(safe-area-inset-bottom)、env(safe-area-inset-left)、env(safe-area-inset-right)。

レガシー API からの移行

レガシー(.NET 9 以前)新規(.NET 10 以降)
ios:Page.UseSafeArea="True"SafeAreaEdges="Container"
Layout.IgnoreSafeArea="True"SafeAreaEdges="None"
WindowSoftInputModeAdjust.ResizeContentPage で SafeAreaEdges="All"

レガシーの ios:Page.UseSafeArea および Layout.IgnoreSafeArea プロパティは引き続きコンパイルできますが、非推奨としてマークされています。IgnoreSafeArea="True" は内部的に SafeAreaRegions.None にマッピングされます。WindowSoftInputModeAdjust.Resize は非推奨ではありません。引き続きサポートされていますが、Android 専用です。


<ContentPage xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls"
             ios:Page.UseSafeArea="True">


<ContentPage SafeAreaEdges="Container">

プラットフォーム固有の動作

iOS と Mac Catalyst
  • セーフエリアのインセットには、ステータスバー、ナビゲーションバー、タブバー、ノッチ/Dynamic Island、ホームインジケーターが含まれます
  • SoftInput には、表示中のキーボードが含まれます
  • インセットは、画面の回転や UI の表示状態の変更に応じて自動的に更新されます
  • Default を設定した ScrollView は、UIScrollViewContentInsetAdjustmentBehavior.Automatic にマッピングされます

ナビゲーションバーの背後にコンテンツを表示するための透明なナビゲーションバー:

<Shell Shell.BackgroundColor="#80000000" Shell.NavBarHasShadow="False" />
Android
  • セーフエリアのインセットには、システムバー(ステータスバー/ナビゲーションバー)とディスプレイの切り欠きが含まれます
  • SoftInput には、ソフトキーボードが含まれます
  • MAUI は内部的に WindowInsetsCompat および WindowInsetsAnimationCompat を使用します
  • 動作は Android のバージョンや OEM のエッジツーエッジ設定によって異なります

よくある落とし穴

  1. レイアウトにも None を設定し忘れる。 ContentPage SafeAreaEdges="None" にするとページは画面端まで表示されますが、子レイアウトのデフォルトは Container のため、内側にパディングが残ります。完全なイマーシブ表示にするには、ページとレイアウトの両方に None を設定します。

  2. ScrollView に直接 SoftInput を使用する。 ScrollView は独自にコンテンツのインセットを管理するため、SoftInput を無視します。ScrollView を Grid または StackLayout でラップし、そちらに SoftInput を適用します。

  3. Default と None を混同する。 Default は「このコントロールの種類に対するプラットフォームのデフォルト」を意味します。iOS の ScrollView では、自動コンテンツインセットが有効になります。None は「セーフエリアのパディングを一切適用しない」という意味です。

  4. Blazor Hybrid で二重にパディングする。 ページに SafeAreaEdges="Container" を設定し、さらに CSS の env(safe-area-inset-*) を使用すると、インセットが二重に適用されます。どちらか一方を選択してください。Blazor では CSS の使用を推奨します。

  5. Blazor で viewport-fit=cover を設定し忘れる。 この meta タグがないと、iOS では CSS の env(safe-area-inset-*) の値が常にゼロになります。

  6. Android で .NET 9 の動作が続くと思い込む。 .NET 10 にアップグレードすると、Android の ContentPage のデフォルトは None になります(以前は実質的に Container でした)。以前の動作に戻すには、SafeAreaEdges="Container" を追加します。

  7. 新しいコードでレガシーの ios:Page.UseSafeArea を使用する。 古い API は iOS 専用で、非推奨です。クロスプラットフォームのセーフエリア管理には、常に SafeAreaEdges を使用してください。

チェックリスト

  • Android のアップグレード:コンテンツがステータスバーの下に表示される場合は、SafeAreaEdges="Container" を追加
  • エッジツーエッジ表示:ページとレイアウトの両方に None を設定
  • ScrollView のキーボード回避には、ScrollView 自体の SafeAreaEdges ではなくラッパーの Grid を使用
  • Blazor Hybrid:XAML と CSS のどちらか一方でセーフエリアを設定し、両方は使用しない
  • Blazor の index.html の <meta viewport> タグに viewport-fit=cover を設定
  • レガシーの UseSafeArea/IgnoreSafeArea を SafeAreaEdges に移行

コメント