Xamarin.Forms iOS SafeArea

Xamarin.Forms iOS使用SafeArea

張阿鬼 2018/12/31 15:44:02
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畫面:
<?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:
    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上也同樣保留了高度:


範例連結

 

 

 

張阿鬼