8

I'm fairly new to this, so sorry if this is a dumb question. How do I get my Xamarin.Forms app to start below the status bar or the notch when applicable? I've tried using a NavigationPage, but then it started wear below the top of the screen. I've also seen a few other solutions, but I can't make it work. Can anyone help?

Thanks!

hvaughan3
  • 10,955
  • 5
  • 56
  • 76
Norman Percy
  • 858
  • 1
  • 8
  • 13

3 Answers3

18

use UseSafeArea

using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
using Xamarin.Forms;

namespace iPhoneX 
{
    public partial class ItemsPage : ContentPage
    {
        public ItemsPage()
        {
            InitializeComponent();

            On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);
        }
    }
}
Jason
  • 86,222
  • 15
  • 131
  • 146
  • Oh cool! Thanks! Just one follow up question: is it possible to make one thing on the screen expand all the way to the edges? Like expanding this blue bar? https://preview.ibb.co/hfDDSw/Screen_Shot_2017_12_12_at_11_24_56_AM.jpg – Norman Percy Dec 12 '17 at 19:26
  • Not if you are using safe areas. That is the whole point of them. – Steve Chadbourne Dec 12 '17 at 22:53
  • I don't know about Xamarin but using AutoLayout one should set the constraints from the parent View in order to go over the safe area. – Vahid Amiri Dec 13 '17 at 06:21
  • If you wish to make it in XAML add https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/ios/page-safe-area-layout – Abdullah Tahan Dec 30 '19 at 13:26
  • It's sad that I have to create a base content page, or else do this on every page. Works great, though. Is there any reason Xamarin cannot set this by default? – stepheaw Sep 22 '20 at 01:48
2

You'll need to consider the safe area but have the background colors expand to take the full screen. So you shouldn't use

On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);

this will box your page with large empty spaces on the bottom and top edges.

Instead, you should measure the safe areas and apply it as padding to your root view.

[assembly: ResolutionGroupName("Enterprise")]
[assembly: ExportEffect(typeof(SafeAreaPaddingEffect), nameof(SafeAreaPaddingEffect))]
namespace Enterprise.iOS.Effects
{
    class SafeAreaPaddingEffect : PlatformEffect
    {
        Thickness _padding;
        protected override void OnAttached()
        {
            if (Element is Layout element)
            {
                if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
                {
                    _padding = element.Padding;
                    var insets = UIApplication.SharedApplication.Windows[0].SafeAreaInsets; // Can't use KeyWindow this early
                    if (insets.Top > 0) // We have a notch
                    {
                        element.Padding = new Thickness(_padding.Left + insets.Left, _padding.Top + insets.Top, _padding.Right + insets.Right, _padding.Bottom);
                        return;
                    }
                }
                // Uses a default Padding of 20. Could use an property to modify if you wanted.
                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;
            }
        }
    }
}

then in xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"                 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="Enterprise.View.Features.Authentication.LoginView"                 
             xmlns:effect="clr-namespace:Enterprise.View.Effects">        
    <Grid>                      
        <Grid.RowDefinitions>               
            <RowDefinition Height="100"/>                
            <RowDefinition Height="*" />            
        </Grid.RowDefinitions>            
        <ContentView BackgroundColor="Green">                
            <ContentView.Effects>                    
                <effect:SafeAreaPaddingEffect />                
            </ContentView.Effects>                
            <Label Text="Hello, from XamarinHelp.com" />            
        </ContentView>                    
    </Grid>
</ContentPage>

ref: https://xamarinhelp.com/safeareainsets-xamarin-forms-ios/ Thank Adam, not me!

matfillion
  • 867
  • 6
  • 10
  • 2
    Unless I misunderstand the use case here, I think it's a lot simpler than this. iOS will set those top and bottom colors based on the background color that you set on the root `ContentPage`. Example here: https://stackoverflow.com/a/49821017/960691 – Le Mot Juiced Apr 13 '18 at 16:09
  • That works for the top of the page but won't work for the bottom of the page, you'll either have your content displayed under the bottom anchor or a blank space (of the color of your page background) – matfillion Apr 26 '18 at 14:47
  • Then we're having different experiences, because for me it sets the color of both top and bottom. – Le Mot Juiced Apr 26 '18 at 15:52
  • 1
    This is the one for me, I agree the background colour covers it, but I'm using an image for the background in the content rather than backgroundimage property. It also needs adjusting for landscape mode. – Paul Charlton May 31 '18 at 06:34
  • Life saver. Thanks to Adam. – Ian Warburton Aug 26 '19 at 19:23
1

is it possible to make one thing on the screen expand all the way to the edges?

(from this answer)

Stretching elements out of the bounds of the safe area is arguably a use case in the case you provided. The bar is a mere background element and not content, as the navigation bar is, which also stretches to fill the whole screen.

Having said that, you unfortunately don't get this for free, but have to implement this by yourself. Assume you have the following XAML

<ContentPage ...>
    <StackLayout>
        <ContentView BackgroundColor="LightSkyBlue" HorizontalOptions="Fill" x:Name="Header">
            <!-- Header -->
        </ContentView>
        <ContentView x:Name="Content">
            <!-- Content -->
        </ContentView>
    </StackLayout>
</ContentPage>

In your code-behind (I would not use it like this, but to make the point it suffices. For a real application I have written a utility class, which is attached to the view and manages the insets.) you can now check for the property SafeAreaInsets being changed

class SafeAreaPage : ContentPage
{
    // elided constructor

    protected override void OnPropertyChanged(string propertyName)
    {
        if(propertyName = "SafeAreaInsets")
        {
            var insets = On<Xamarin.Forms.PlatformConfiguration.iOS>.GetSafeAreaInsets();

            var headerInsets = insets; // Thickness is a value type
            headerInsets.Bottom = 0;

            var contentInsets = insets;
            contentInsets.Top = 0;

            Header.Padding = headerInsets;
            Content.Padding = contentInsets;
        }
    }
}

How you set the Paddings of your views depends on your layouts, but this way you have a bit more control on how the safe area insets are used, although it is a bit fiddly.

Paul Kertscher
  • 9,416
  • 5
  • 32
  • 57
  • 1
    This _almost_ works... however, it throws an error on `var insets = On.GetSafeAreaInsets();`: "Page.On()' is a method, which is not valid in the given context". How can I fix this? – Norman Percy Dec 16 '17 at 20:17
  • 1
    Also `if (propertyName = "SafeAreaInsets")` should be `if(propertyName == "SafeAreaInsets")`. – Norman Percy Dec 16 '17 at 20:19