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. 04协调 Blazor 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

详细介绍

安全区域与全面屏布局(.NET 10+)

.NET 10 推出了全新的跨平台安全区域 API,取代旧版仅适用于 iOS 的 UseSafeArea 和布局级 IgnoreSafeArea 属性。新的 SafeAreaEdges 属性和 SafeAreaRegions 标志枚举,可通过统一的 API 为 Android、iOS 和 Mac Catalyst 上的各个控件分别管理各边的安全区域。

这是 .NET 10 中新增的 API。 如果项目面向 .NET 9 或更早版本,这些 API 并不存在。请引导开发者改用旧版 ios:Page.UseSafeArea 和 Layout.IgnoreSafeArea 属性。

适用场景

  • 升级到 .NET 10 后,内容与状态栏、刘海、灵动岛或主屏幕指示条重叠
  • 实现全面屏/沉浸式布局(照片查看器、视频播放器、地图)
  • 为聊天或表单 UI 实现键盘避让
  • 从 ios:Page.UseSafeArea、Layout.IgnoreSafeArea 或 WindowSoftInputModeAdjust.Resize 迁移
  • 需要协调 CSS env(safe-area-inset-*) 的 Blazor Hybrid 应用
  • 混合布局:标题栏延伸至屏幕边缘,而主体遵循安全区域

不适用场景

  • 面向 .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全面屏布局。这是相较于 .NET 9 在 Android 上的破坏性变更。
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 区域)。

使用模式

全面屏沉浸式内容

在页面和布局上都设置为 None——布局默认值为 Container:

<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>
混合布局:全面屏标题栏 + 安全区域主体 + 键盘页脚
<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
  • 安全区域内边距涵盖:状态栏、导航栏、标签栏、刘海屏/灵动岛、主屏幕指示条
  • 键盘显示时,SoftInput 会包含键盘
  • 旋转设备或 UI 可见性变化时,内边距会自动更新
  • ScrollView 搭配 Default 时,会映射到 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 键盘避让使用包装 Grid,而不是 ScrollView 自身的 SafeAreaEdges
  • Blazor Hybrid:使用 XAML 或 CSS 安全区域,不要两者兼用
  • 在 Blazor 的 index.html <meta viewport> 标签中设置 viewport-fit=cover
  • 将旧版 UseSafeArea/IgnoreSafeArea 迁移到 SafeAreaEdges

评论

支持 Markdown