I have a CarouselPage in my app where the items are visualized as ContentPages. I noticed that with this setup UseSafeArea doesn't work. Each ContentPage always covers the full display area on a device that should have insets. I tried the following to solve this:
First I tried to force the setting of safe area myself in OnAppearing(), with:
if (Device.RuntimePlatform == Device.iOS)
{
On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);
}
That doesn't work.
I then checked what the framework actually reports as insets with:
if (Device.RuntimePlatform == Device.iOS)
{
On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);
var safeInsets = On<Xamarin.Forms.PlatformConfiguration.iOS>().SafeAreaInsets();
this.Padding = safeInsets;
}
On devices that need insets (iPhone XR, iPad Pro, etc.) the insets are all 0.
I then tried to define UseSafeArea in the ContentPage's ItemTemplate, like so:
<CarouselPage.ItemTemplate>
<DataTemplate>
<ContentPage ios:UseSafeArea="True">
...
</ContentPage>
</DataTemplate>
</CarouselPage.ItemTemplate>
That didn't work either.
I finally managed to get it working by forcing the insets in code behind of my CarouselPage with an own implementation of a dependency service for safe area insets and setting the ContentPage's padding each time the current page in CarouselPage changes. It look as follows:
public partial class ArticleXFCarouselPage : CarouselPage
{
ArticleCarouselViewModel _carouselVM;
bool _isInitializing;
public ArticleXFCarouselPage (ArticleCarouselViewModel vm)
{
_carouselVM = vm;
this.BindingContext = vm;
_isInitializing = true;
ItemsSource = _carouselVM.Items;
CurrentPage = Children[_carouselVM.CurrentItemIdx];
InitializeComponent();
_isInitializing = false;
}
double _width;
double _height;
Thickness _contentPagePadding = new Thickness();
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
if (Equals(_width, width) &&
Equals(_height, height))
return;
var oldWidth = _width;
_width = width;
_height = height;
// Has the device been rotated ?
if (width != -1 && !Equals(width, oldWidth))
{
SetSafeArea(width < height ? PageOrientation.Vertical : PageOrientation.Horizontal);
}
}
void SetSafeArea(PageOrientation orientation)
{
if (Device.RuntimePlatform == Device.iOS)
{
_contentPagePadding = DependencyService.Get<IPlatformService>().GetSafeAreaInset();
if (orientation == PageOrientation.Vertical)
{
_contentPagePadding.Top = 0;
}
CurrentPage.Padding = _contentPagePadding;
}
}
protected override void OnCurrentPageChanged()
{
base.OnCurrentPageChanged();
if (!_isInitializing)
{
CurrentPage.Padding = _contentPagePadding;
// Tell viewmodel to load additional articles if any
Device.BeginInvokeOnMainThread(async () =>
{
var idx = Children.IndexOf(CurrentPage);
await _carouselVM.OnArticleBecomesVisibleAsync(idx);
});
}
}
}
The dependency service for iOS looks as follows:
public class PlatformService : IPlatformService
{
public PlatformService()
{ }
public Thickness GetSafeAreaInset()
{
var inset = new Thickness();
if (UIApplication.SharedApplication.KeyWindow != null)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
var safeAreaInsets = UIApplication.SharedApplication.KeyWindow.SafeAreaInsets;
inset.Top = safeAreaInsets.Top;
inset.Bottom = safeAreaInsets.Bottom;
inset.Left = safeAreaInsets.Left;
inset.Right = safeAreaInsets.Right;
}
}
return inset;
}
}
That of course leaves me with the question: Did I overlooked something? Is there indeed an error in CarouselPage and if so are there plans to solve this?