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?