diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3809.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3809.cs new file mode 100644 index 000000000..e92bcdfa4 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue3809.cs @@ -0,0 +1,129 @@ +using System.Linq; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; +using Xamarin.Forms.PlatformConfiguration.iOSSpecific; + + +#if UITEST +using Xamarin.UITest; +using NUnit.Framework; +using Xamarin.Forms.Core.UITests; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 3809, "SetUseSafeArea is wiping out Page Padding ")] + public class Issue3809 : TestMasterDetailPage + { + const string _setPagePadding = "Set Page Padding"; + const string _safeAreaText = "Safe Area Enabled: "; + const string _paddingLabel = "paddingLabel"; + const string _safeAreaAutomationId = "SafeAreaAutomation"; + + Label label = null; + protected override void Init() + { + label = new Label() + { + AutomationId = _paddingLabel + }; + + Master = new ContentPage() { Title = "Master" }; + Button button = null; + button = new Button() + { + Text = $"{_safeAreaText}{true}", + AutomationId = _safeAreaAutomationId, + Command = new Command(() => + { + bool safeArea = !Detail.On().UsingSafeArea(); + Detail.On().SetUseSafeArea(safeArea); + button.Text = $"{_safeAreaText}{safeArea}"; + Device.BeginInvokeOnMainThread(() => label.Text = $"{Detail.Padding.Left}, {Detail.Padding.Top}, {Detail.Padding.Right}, {Detail.Padding.Bottom}"); + }) + }; + + Detail = new ContentPage() + { + Title = "Details", + Content = new StackLayout() + { + Children = + { + new ListView(ListViewCachingStrategy.RecycleElement) + { + ItemsSource = Enumerable.Range(0,200).Select(x=> x.ToString()).ToList() + }, + label, + button, + new Button() + { + Text = _setPagePadding, + Command = new Command(() => + { + Detail.Padding = new Thickness(25, 25, 25, 25); + Device.BeginInvokeOnMainThread(() => label.Text = $"{Detail.Padding.Left}, {Detail.Padding.Top}, {Detail.Padding.Right}, {Detail.Padding.Bottom}"); + }) + } + } + } + }; + + Detail.Padding = new Thickness(25, 25, 25, 25); + Detail.On().SetUseSafeArea(true); + } + + protected override void OnAppearing() + { + base.OnAppearing(); + label.Text = $"{Detail.Padding.Left}, {Detail.Padding.Top}, {Detail.Padding.Right}, {Detail.Padding.Bottom}"; + } + +#if UITEST + [Test] + public void SafeAreaInsetsBreaksAndroidPadding() + { + // ensure initial paddings are honored + RunningApp.WaitForElement($"{_safeAreaText}{true}"); + var element = RunningApp.WaitForElement(_paddingLabel).First(); + + bool usesSafeAreaInsets = false; + if (element.Text != "25, 25, 25, 25") + usesSafeAreaInsets = true; + + Assert.AreNotEqual(element.Text, "0, 0, 0, 0"); + if (!usesSafeAreaInsets) + Assert.AreEqual(element.Text, "25, 25, 25, 25"); + + // disable Safe Area Insets + RunningApp.Tap(_safeAreaAutomationId); + RunningApp.WaitForElement($"{_safeAreaText}{false}"); + element = RunningApp.WaitForElement(_paddingLabel).First(); + + if (usesSafeAreaInsets) + Assert.AreEqual(element.Text, "0, 0, 0, 0"); + else + Assert.AreEqual(element.Text, "25, 25, 25, 25"); + + // enable Safe Area insets + RunningApp.Tap(_safeAreaAutomationId); + RunningApp.WaitForElement($"{_safeAreaText}{true}"); + element = RunningApp.WaitForElement(_paddingLabel).First(); + Assert.AreNotEqual(element.Text, "0, 0, 0, 0"); + + if (!usesSafeAreaInsets) + Assert.AreEqual(element.Text, "25, 25, 25, 25"); + + + // Set Padding and then disable safe area insets + RunningApp.Tap(_setPagePadding); + RunningApp.Tap(_safeAreaAutomationId); + RunningApp.WaitForElement($"{_safeAreaText}{false}"); + element = RunningApp.WaitForElement(_paddingLabel).First(); + Assert.AreEqual(element.Text, "25, 25, 25, 25"); + + } +#endif + } +} diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index a2874c6ba..07638c41a 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -9,6 +9,7 @@ Xamarin.Forms.Controls.Issues + diff --git a/Xamarin.Forms.Core/PlatformConfiguration/iOSSpecific/Page.cs b/Xamarin.Forms.Core/PlatformConfiguration/iOSSpecific/Page.cs index f8befc395..65179c621 100644 --- a/Xamarin.Forms.Core/PlatformConfiguration/iOSSpecific/Page.cs +++ b/Xamarin.Forms.Core/PlatformConfiguration/iOSSpecific/Page.cs @@ -58,18 +58,7 @@ return config; } - public static readonly BindableProperty UseSafeAreaProperty = BindableProperty.Create("UseSafeArea", typeof(bool), typeof(Page), false, propertyChanged: (bindable, oldValue, newValue) => - { - var page = bindable as Xamarin.Forms.Page; - if ((bool)oldValue && !(bool)newValue) - { - page.Padding = default(Thickness); - } - else - { - UpdatePadding(GetSafeAreaInsets(page), page); - } - }); + public static readonly BindableProperty UseSafeAreaProperty = BindableProperty.Create("UseSafeArea", typeof(bool), typeof(Page), false); public static bool GetUseSafeArea(BindableObject element) { @@ -115,17 +104,7 @@ return config; } - static readonly BindablePropertyKey SafeAreaInsetsPropertyKey = BindableProperty.CreateReadOnly(nameof(SafeAreaInsets), typeof(Thickness), typeof(Page), default(Thickness), propertyChanged: (bindable, oldValue, newValue) => - { - var page = bindable as Xamarin.Forms.Page; - UpdatePadding((Thickness)newValue, page); - }); - - static void UpdatePadding(Thickness thickness, FormsElement page) - { - if (page.On().UsingSafeArea()) - page.Padding = thickness; - } + static readonly BindablePropertyKey SafeAreaInsetsPropertyKey = BindableProperty.CreateReadOnly(nameof(SafeAreaInsets), typeof(Thickness), typeof(Page), default(Thickness)); public static readonly BindableProperty SafeAreaInsetsProperty = SafeAreaInsetsPropertyKey.BindableProperty; diff --git a/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs index 2a40142ea..29fc00e01 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs @@ -18,6 +18,9 @@ namespace Xamarin.Forms.Platform.iOS Page Page => Element as Page; + bool UsingSafeArea => (Forms.IsiOS11OrNewer) ? Page.On().UsingSafeArea() : false; + Thickness SafeAreaInsets => Page.On().SafeAreaInsets(); + public PageRenderer() { } @@ -70,13 +73,14 @@ namespace Xamarin.Forms.Platform.iOS if (page != null && Forms.IsiOS11OrNewer) { var insets = NativeView.SafeAreaInsets; - if(page.Parent is TabbedPage) + if (page.Parent is TabbedPage) { insets.Bottom = 0; } page.On().SetSafeAreaInsets(new Thickness(insets.Left, insets.Top, insets.Right, insets.Bottom)); - + } + base.ViewSafeAreaInsetsDidChange(); } @@ -195,6 +199,10 @@ namespace Xamarin.Forms.Platform.iOS UpdateTitle(); else if (e.PropertyName == PlatformConfiguration.iOSSpecific.Page.PrefersStatusBarHiddenProperty.PropertyName) UpdateStatusBarPrefersHidden(); + else if (Forms.IsiOS11OrNewer && e.PropertyName == PlatformConfiguration.iOSSpecific.Page.UseSafeAreaProperty.PropertyName) + UpdateUseSafeArea(); + else if (Forms.IsiOS11OrNewer && e.PropertyName == PlatformConfiguration.iOSSpecific.Page.SafeAreaInsetsProperty.PropertyName) + UpdateUseSafeArea(); } public override UIKit.UIStatusBarAnimation PreferredStatusBarUpdateAnimation @@ -215,6 +223,22 @@ namespace Xamarin.Forms.Platform.iOS } } + void UpdateUseSafeArea() + { + if (!Forms.IsiOS11OrNewer) return; + + if (!UsingSafeArea) + { + var safeAreaInsets = SafeAreaInsets; + if (safeAreaInsets == Page.Padding) + Page.Padding = default(Thickness); + } + else + { + Page.Padding = SafeAreaInsets; + } + } + void UpdateStatusBarPrefersHidden() { if (Element == null)