Xamarin.Forms
iOS
SafeArea
Xamarin.Forms iOS使用SafeArea
2018/12/31 15:44:02
0
804
Xamarin.Forms iOS使用SafeArea
簡介 |
Xamarin.Forms iOS使用SafeArea |
作者 |
張朝銘 |
主題: |
Xamarin.Forms iOS使用SafeArea |
作者: |
Steven |
版本/產出日期: |
V1.0/2018.12.16 |
前言
開發Xamarin.Fomrs畫面時,會遇到iOS與Android畫面起始位置不同的問題,
Android是從狀態列下方的最左上角開始擺放控制項的位置,
而iOS則是以整個銀幕的最左上角做為開始的位置。
因此當在設計Xamarin.Forms的畫面時,在iOS上就會遇到控制項與狀態列重疊
的問題。如下XAML畫面:
Android畫面

iOS畫面

為了避免此問題,最簡單的方式是使用OnPlatform針對不同的平台設置Padding,
iOS狀態列高度為20,因此將XAML調整成這樣:
再次執行後iOS畫面就可正常呈現:

但在iPhoneX問世後,這樣的設置已經不足應付畫面的變化,
同樣保留Padding Top 20的情況下控制項依然與狀態列發生重疊,
而且還會被那個瀏海擋住:

這是因為iPhoneX多出了劉海的空間,而且銀幕的邊緣是原角,
導致原本的20高度並不夠騰出空間來。
好在Xamarin.Forms在iOS11後新增了一個API,
該API的用意就是讓我們在開發時不需要在考慮要保留多少高度,
它會自動針對不同的裝置保留一定的高度,防止重疊。
該API可以透過XAML或者在程式碼中實作,
如下XMAL及程式碼:
XAML(最後兩行):
程式碼:
XAML或程式碼選擇其中一種實作即可,
最後在iPhoneX上也可以成功保留一定的空間防止重疊:

但該API僅只有支援iOS11之後,如果是iOS11之前,
還是需要自己去保留高20的高度空間,
如下圖同樣的程式碼在iOS9的情況:

而iOS則是以整個銀幕的最左上角做為開始的位置。
因此當在設計Xamarin.Forms的畫面時,在iOS上就會遇到控制項與狀態列重疊
的問題。如下XAML畫面:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.iOSSafeArea.MainPage" >
<StackLayout>
<Label Text="我是文字我是文字"
FontSize="20"
HorizontalOptions="Start" />
</StackLayout>
</ContentPage>
Android畫面

iOS畫面

為了避免此問題,最簡單的方式是使用OnPlatform針對不同的平台設置Padding,
iOS狀態列高度為20,因此將XAML調整成這樣:
<StackLayout>
<StackLayout.Padding>
<OnPlatform
x:TypeArguments="Thickness"
iOS="0,20,0,0"
Android="0,0,0,0"/>
</StackLayout.Padding>
<Label Text="我是文字我是文字"
FontSize="20"
HorizontalOptions="Start" />
</StackLayout>
再次執行後iOS畫面就可正常呈現:

但在iPhoneX問世後,這樣的設置已經不足應付畫面的變化,
同樣保留Padding Top 20的情況下控制項依然與狀態列發生重疊,
而且還會被那個瀏海擋住:

這是因為iPhoneX多出了劉海的空間,而且銀幕的邊緣是原角,
導致原本的20高度並不夠騰出空間來。
好在Xamarin.Forms在iOS11後新增了一個API,
該API的用意就是讓我們在開發時不需要在考慮要保留多少高度,
它會自動針對不同的裝置保留一定的高度,防止重疊。
該API可以透過XAML或者在程式碼中實作,
如下XMAL及程式碼:
XAML(最後兩行):
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.iOSSafeArea.MainPage"
xmlns:local="clr-namespace:Xamarin.Forms.iOSSafeArea"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.UseSafeArea="true" >
程式碼:
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Xamarin.Forms.iOSSafeArea
{
public partial class MainPage : ContentPage
{
public MainPage()
{
On<iOS>().SetUseSafeArea(true);
InitializeComponent();
}
}
}
XAML或程式碼選擇其中一種實作即可,
最後在iPhoneX上也可以成功保留一定的空間防止重疊:

但該API僅只有支援iOS11之後,如果是iOS11之前,
還是需要自己去保留高20的高度空間,
如下圖同樣的程式碼在iOS9的情況:

iPhoneX問世後都是從iOS11開始支援,所以這邊我們可以
只處理保留20高度的部分。
這裡我們可以透過實作Effect的方式。
首先在Xamarin.Forms專案中新增一個SafeAreaPaddingEffect類別,
並使其繼承RoutingEffect:
只處理保留20高度的部分。
這裡我們可以透過實作Effect的方式。
首先在Xamarin.Forms專案中新增一個SafeAreaPaddingEffect類別,
並使其繼承RoutingEffect:
public class SafeAreaPaddingEffect : RoutingEffect
{
public SafeAreaPaddingEffect() : base("TP.SafeAreaPaddingEffect") { }
}
這邊要注意base傳入的是 {GroupName}.{TypeName}。
接著在iOS專案中同樣新增一個SafeAreaPaddingEffect類別,
並讓它繼承PlatformEffect,並加上兩個特性
ResolutionGroupName與ExportEffect,
ResolutionGroupName傳入的就是GroupName。
該類別中我們判斷如果OS版本小於iOS11時,
就將Padding Top改為20。
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.iOSSafeArea.iOS;
using Xamarin.Forms.Platform.iOS;
[assembly: ResolutionGroupName("TP")]
[assembly: ExportEffect(typeof(SafeAreaPaddingEffect), nameof(SafeAreaPaddingEffect))]
namespace Xamarin.Forms.iOSSafeArea.iOS
{
public class SafeAreaPaddingEffect : PlatformEffect
{
Thickness padding;
protected override void OnAttached()
{
if (Element is Layout element)
{
if (!UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
element.Padding = new Thickness(padding.Left, padding.Top + 20, padding.Right, padding.Bottom);
}
}
protected override void OnDetached()
{
if (Element is Layout element)
element.Padding = padding;
}
}
}
最後在XAML上的StackLayout加上這個Effect。
<StackLayout>
<StackLayout.Effects>
<local:SafeAreaPaddingEffect />
</StackLayout.Effects>
<Label Text="我是文字我是文字"
FontSize="20"
HorizontalOptions="Start" />
</StackLayout>
然後再執行一次程式,在iOS9上也同樣保留了高度:
