From: Shane Neuville Date: Wed, 20 Nov 2019 11:51:30 +0000 (-0700) Subject: Shell Navigation Bar Has Shadow property (#8408) X-Git-Tag: accepted/tizen/5.5/unified/20200421.150457~77^2~9 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f9e503ff966dbca4701fbef327d106efb4ad68f1;p=platform%2Fcore%2Fcsapi%2Fxsf.git Shell Navigation Bar Has Shadow property (#8408) * add shadow api for shell nav bar * - ios fixes * - ios fixes * - name fix android and simplify types on ios * - change to listener * - cleanup * - remove toolbar * - fix ios to propagate nav bar on page push * - fix android crash --- diff --git a/Xamarin.Forms.Controls/XamStore/Views/StorePages.cs b/Xamarin.Forms.Controls/XamStore/Views/StorePages.cs index f480b2b..3ae6457 100644 --- a/Xamarin.Forms.Controls/XamStore/Views/StorePages.cs +++ b/Xamarin.Forms.Controls/XamStore/Views/StorePages.cs @@ -302,11 +302,15 @@ namespace Xamarin.Forms.Controls.XamStore Content = new ScrollView { Content = grid }; - //var listView = new ListView(); - //listView.ItemsSource = Enumerable.Range(0, 1000).ToList(); - //Content = listView; - } + grid.Children.Add(MakeButton("Hide Nav Shadow", + () => Shell.SetNavBarHasShadow(this, false)), + 1, 21); + + grid.Children.Add(MakeButton("Show Nav Shadow", + () => Shell.SetNavBarHasShadow(this, true)), + 2, 21); + } Switch _navBarVisibleSwitch; Switch _tabBarVisibleSwitch; diff --git a/Xamarin.Forms.Core/Internals/PropertyPropagationExtensions.cs b/Xamarin.Forms.Core/Internals/PropertyPropagationExtensions.cs index 97f3753..5a7abec 100644 --- a/Xamarin.Forms.Core/Internals/PropertyPropagationExtensions.cs +++ b/Xamarin.Forms.Core/Internals/PropertyPropagationExtensions.cs @@ -18,6 +18,9 @@ namespace Xamarin.Forms.Internals if (propertyName == null || propertyName == Shell.NavBarIsVisibleProperty.PropertyName) BaseShellItem.PropagateFromParent(Shell.NavBarIsVisibleProperty, element); + if (propertyName == null || propertyName == Shell.NavBarHasShadowProperty.PropertyName) + BaseShellItem.PropagateFromParent(Shell.NavBarHasShadowProperty, element); + if (propertyName == null || propertyName == Shell.TabBarIsVisibleProperty.PropertyName) BaseShellItem.PropagateFromParent(Shell.TabBarIsVisibleProperty, element); diff --git a/Xamarin.Forms.Core/Shell/Shell.cs b/Xamarin.Forms.Core/Shell/Shell.cs index f362704..62b07b3 100644 --- a/Xamarin.Forms.Core/Shell/Shell.cs +++ b/Xamarin.Forms.Core/Shell/Shell.cs @@ -32,6 +32,10 @@ namespace Xamarin.Forms public static readonly BindableProperty NavBarIsVisibleProperty = BindableProperty.CreateAttached("NavBarIsVisible", typeof(bool), typeof(Shell), true); + public static readonly BindableProperty NavBarHasShadowProperty = + BindableProperty.CreateAttached("NavBarHasShadow", typeof(bool), typeof(Shell), default(bool), + defaultValueCreator: (b) => Device.RuntimePlatform == Device.Android); + public static readonly BindableProperty SearchHandlerProperty = BindableProperty.CreateAttached("SearchHandler", typeof(SearchHandler), typeof(Shell), null, BindingMode.OneTime, propertyChanged: OnSearchHandlerPropertyChanged); @@ -71,6 +75,9 @@ namespace Xamarin.Forms public static bool GetNavBarIsVisible(BindableObject obj) => (bool)obj.GetValue(NavBarIsVisibleProperty); public static void SetNavBarIsVisible(BindableObject obj, bool value) => obj.SetValue(NavBarIsVisibleProperty, value); + public static bool GetNavBarHasShadow(BindableObject obj) => (bool)obj.GetValue(NavBarHasShadowProperty); + public static void SetNavBarHasShadow(BindableObject obj, bool value) => obj.SetValue(NavBarHasShadowProperty, value); + public static SearchHandler GetSearchHandler(BindableObject obj) => (SearchHandler)obj.GetValue(SearchHandlerProperty); public static void SetSearchHandler(BindableObject obj, SearchHandler handler) => obj.SetValue(SearchHandlerProperty, handler); diff --git a/Xamarin.Forms.Platform.Android/GenericGlobalLayoutListener.cs b/Xamarin.Forms.Platform.Android/GenericGlobalLayoutListener.cs new file mode 100644 index 0000000..c525023 --- /dev/null +++ b/Xamarin.Forms.Platform.Android/GenericGlobalLayoutListener.cs @@ -0,0 +1,34 @@ +using System; +using Android.Views; +using Object = Java.Lang.Object; + +namespace Xamarin.Forms.Platform.Android +{ + internal class GenericGlobalLayoutListener : Object, ViewTreeObserver.IOnGlobalLayoutListener + { + Action _callback; + + public GenericGlobalLayoutListener(Action callback) + { + _callback = callback; + } + + public void OnGlobalLayout() + { + _callback?.Invoke(); + } + + protected override void Dispose(bool disposing) + { + Invalidate(); + base.Dispose(disposing); + } + + // I don't want our code to dispose of this class I'd rather just let the natural + // process manage the life cycle so we don't dispose of this too early + internal void Invalidate() + { + _callback = null; + } + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/Renderers/ShellToolbarTracker.cs b/Xamarin.Forms.Platform.Android/Renderers/ShellToolbarTracker.cs index f11e1b3..166d754 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/ShellToolbarTracker.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/ShellToolbarTracker.cs @@ -18,6 +18,7 @@ using R = Android.Resource; using Toolbar = Android.Support.V7.Widget.Toolbar; using ADrawableCompat = Android.Support.V4.Graphics.Drawable.DrawableCompat; using ATextView = global::Android.Widget.TextView; +using Android.Support.Design.Widget; namespace Xamarin.Forms.Platform.Android { @@ -50,13 +51,19 @@ namespace Xamarin.Forms.Platform.Android //assume the default Color _tintColor = Color.Default; Toolbar _toolbar; + AppBarLayout _appBar; + float _appBarElevation; + GenericGlobalLayoutListener _globalLayoutListener; public ShellToolbarTracker(IShellContext shellContext, Toolbar toolbar, DrawerLayout drawerLayout) { _shellContext = shellContext ?? throw new ArgumentNullException(nameof(shellContext)); _toolbar = toolbar ?? throw new ArgumentNullException(nameof(toolbar)); _drawerLayout = drawerLayout ?? throw new ArgumentNullException(nameof(drawerLayout)); + _appBar = _toolbar.Parent.GetParentOfType(); + _globalLayoutListener = new GenericGlobalLayoutListener(() => UpdateNavBarHasShadow(Page)); + _appBar.ViewTreeObserver.AddOnGlobalLayoutListener(_globalLayoutListener); _toolbar.SetNavigationOnClickListener(this); ((IShellController)_shellContext.Shell).AddFlyoutBehaviorObserver(this); } @@ -139,6 +146,11 @@ namespace Xamarin.Forms.Platform.Android if (disposing) { + if (_appBar.IsAlive() && _appBar.ViewTreeObserver.IsAlive()) + _appBar.ViewTreeObserver.RemoveOnGlobalLayoutListener(_globalLayoutListener); + + _globalLayoutListener.Invalidate(); + if (_backButtonBehavior != null) _backButtonBehavior.PropertyChanged -= OnBackButtonBehaviorChanged; ((IShellController)_shellContext?.Shell)?.RemoveFlyoutBehaviorObserver(this); @@ -156,6 +168,7 @@ namespace Xamarin.Forms.Platform.Android _drawerToggle?.Dispose(); } + _globalLayoutListener = null; _backButtonBehavior = null; SearchHandler = null; _shellContext = null; @@ -163,6 +176,7 @@ namespace Xamarin.Forms.Platform.Android _searchView = null; Page = null; _toolbar = null; + _appBar = null; _drawerLayout = null; base.Dispose(disposing); } @@ -202,6 +216,7 @@ namespace Xamarin.Forms.Platform.Android UpdateLeftBarButtonItem(); UpdateToolbarItems(); UpdateNavBarVisible(_toolbar, newPage); + UpdateNavBarHasShadow(newPage); UpdateTitleView(); } } @@ -215,6 +230,8 @@ namespace Xamarin.Forms.Platform.Android UpdateToolbarItems(); else if (e.PropertyName == Shell.NavBarIsVisibleProperty.PropertyName) UpdateNavBarVisible(_toolbar, Page); + else if (e.PropertyName == Shell.NavBarHasShadowProperty.PropertyName) + UpdateNavBarHasShadow(Page); else if (e.PropertyName == Shell.BackButtonBehaviorProperty.PropertyName) { var backButtonHandler = Shell.GetBackButtonBehavior(Page); @@ -411,6 +428,24 @@ namespace Xamarin.Forms.Platform.Android toolbar.Visibility = navBarVisible ? ViewStates.Visible : ViewStates.Gone; } + void UpdateNavBarHasShadow(Page page) + { + if (page == null || !_appBar.IsAlive()) + return; + + if (Shell.GetNavBarHasShadow(page)) + { + if (_appBarElevation > 0) + _appBar.SetElevation(_appBarElevation); + } + else + { + // 4 is the default + _appBarElevation = _appBar.Context.ToPixels(4); + _appBar.SetElevation(0f); + } + } + protected virtual void UpdatePageTitle(Toolbar toolbar, Page page) { _toolbar.Title = page.Title; diff --git a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj index afe184e..4614e93 100644 --- a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj +++ b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj @@ -146,6 +146,7 @@ + diff --git a/Xamarin.Forms.Platform.iOS/Renderers/IShellNavBarAppearanceTracker.cs b/Xamarin.Forms.Platform.iOS/Renderers/IShellNavBarAppearanceTracker.cs index 43d043a..800cc3c 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/IShellNavBarAppearanceTracker.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/IShellNavBarAppearanceTracker.cs @@ -9,5 +9,6 @@ namespace Xamarin.Forms.Platform.iOS void ResetAppearance(UINavigationController controller); void SetAppearance(UINavigationController controller, ShellAppearance appearance); void UpdateLayout(UINavigationController controller); + void SetHasShadow(UINavigationController controller, bool hasShadow); } } \ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/SafeShellNavBarAppearanceTracker.cs b/Xamarin.Forms.Platform.iOS/Renderers/SafeShellNavBarAppearanceTracker.cs index 92ba8d3..bd94ee7 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/SafeShellNavBarAppearanceTracker.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/SafeShellNavBarAppearanceTracker.cs @@ -1,64 +1,12 @@ -using UIKit; +using System; +using System.ComponentModel; +using CoreGraphics; +using UIKit; namespace Xamarin.Forms.Platform.iOS { - public class SafeShellNavBarAppearanceTracker : IShellNavBarAppearanceTracker + [EditorBrowsable(EditorBrowsableState.Never)] + public class SafeShellNavBarAppearanceTracker : ShellNavBarAppearanceTracker { - UIColor _defaultBarTint; - UIColor _defaultTint; - UIStringAttributes _defaultTitleAttributes; - - public void UpdateLayout(UINavigationController controller) - { - } - - public void ResetAppearance(UINavigationController controller) - { - if (_defaultTint != null) - { - var navBar = controller.NavigationBar; - navBar.TintColor = _defaultTint; - navBar.TitleTextAttributes = _defaultTitleAttributes; - } - } - - public void SetAppearance(UINavigationController controller, ShellAppearance appearance) - { - var background = appearance.BackgroundColor; - var foreground = appearance.ForegroundColor; - var titleColor = appearance.TitleColor; - - var navBar = controller.NavigationBar; - - if (_defaultTint == null) - { - _defaultBarTint = navBar.BarTintColor; - _defaultTint = navBar.TintColor; - _defaultTitleAttributes = navBar.TitleTextAttributes; - } - - if (!background.IsDefault) - navBar.BarTintColor = background.ToUIColor(); - if (!foreground.IsDefault) - navBar.TintColor = foreground.ToUIColor(); - if (!titleColor.IsDefault) - { - navBar.TitleTextAttributes = new UIStringAttributes - { - ForegroundColor = titleColor.ToUIColor() - }; - } - } - - #region IDisposable Support - protected virtual void Dispose(bool disposing) - { - } - - public void Dispose() - { - Dispose(true); - } - #endregion } } \ No newline at end of file diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ShellNavBarAppearanceTracker.cs b/Xamarin.Forms.Platform.iOS/Renderers/ShellNavBarAppearanceTracker.cs index aab03d3..af4c7b3 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/ShellNavBarAppearanceTracker.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/ShellNavBarAppearanceTracker.cs @@ -1,44 +1,29 @@ -using UIKit; +using System; +using System.ComponentModel; +using CoreGraphics; +using UIKit; namespace Xamarin.Forms.Platform.iOS { public class ShellNavBarAppearanceTracker : IShellNavBarAppearanceTracker { - UIView _blurView; - UIView _colorView; - UIImage _defaultBackgroundImage; + UIColor _defaultBarTint; UIColor _defaultTint; UIStringAttributes _defaultTitleAttributes; - bool _disposed = false; + float _shadowOpacity = float.MinValue; + CGColor _shadowColor; - public void UpdateLayout (UINavigationController controller) + public void UpdateLayout(UINavigationController controller) { - if (_blurView?.Superview == null) - return; - - var navBar = controller.NavigationBar; - navBar.SendSubviewToBack(_colorView); - navBar.SendSubviewToBack(_blurView); - - var frame = navBar.Frame; - frame.Height += frame.Y; - frame.Y = -frame.Y; - - _blurView.Frame = frame; - _colorView.Frame = frame; } public void ResetAppearance(UINavigationController controller) { - if (_blurView != null) + if (_defaultTint != null) { var navBar = controller.NavigationBar; - navBar.SetBackgroundImage(_defaultBackgroundImage, UIBarMetrics.Default); navBar.TintColor = _defaultTint; navBar.TitleTextAttributes = _defaultTitleAttributes; - - _blurView.RemoveFromSuperview(); - _colorView.RemoveFromSuperview(); } } @@ -50,42 +35,15 @@ namespace Xamarin.Forms.Platform.iOS var navBar = controller.NavigationBar; - if (_blurView == null) + if (_defaultTint == null) { - _defaultBackgroundImage = navBar.GetBackgroundImage(UIBarMetrics.Default); + _defaultBarTint = navBar.BarTintColor; _defaultTint = navBar.TintColor; _defaultTitleAttributes = navBar.TitleTextAttributes; - - var frame = navBar.Frame; - frame.Height += frame.Y; - frame.Y = -frame.Y; - - var effect = UIBlurEffect.FromStyle(UIBlurEffectStyle.Regular); - _blurView = new UIVisualEffectView(effect); - _blurView.UserInteractionEnabled = false; - _blurView.Frame = frame; - - _colorView = new UIView(frame); - _colorView.UserInteractionEnabled = false; - - if (Forms.IsiOS11OrNewer) - { - _blurView.Layer.ShadowColor = UIColor.Black.CGColor; - _blurView.Layer.ShadowOpacity = 1f; - _blurView.Layer.ShadowRadius = 3; - } } - navBar.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); - - navBar.InsertSubview(_colorView, 0); - navBar.InsertSubview(_blurView, 0); - if (!background.IsDefault) - { - _colorView.BackgroundColor = background.ToUIColor(); - } - + navBar.BarTintColor = background.ToUIColor(); if (!foreground.IsDefault) navBar.TintColor = foreground.ToUIColor(); if (!titleColor.IsDefault) @@ -98,37 +56,39 @@ namespace Xamarin.Forms.Platform.iOS } #region IDisposable Support - protected virtual void Dispose(bool disposing) { - if (!_disposed) - { - if (disposing) - { - if (_blurView != null) - { - _blurView.RemoveFromSuperview(); - _blurView.Dispose(); - } - - if (_colorView != null) - { - _colorView.RemoveFromSuperview(); - _colorView.Dispose(); - } - } - - _colorView = null; - _blurView = null; - - _disposed = true; - } } public void Dispose() { Dispose(true); } + + public virtual void SetHasShadow(UINavigationController controller, bool hasShadow) + { + var navigationBar = controller.NavigationBar; + if (_shadowOpacity == float.MinValue) + { + // Don't do anything if user hasn't changed the shadow to true + if (!hasShadow) + return; + + _shadowOpacity = navigationBar.Layer.ShadowOpacity; + _shadowColor = navigationBar.Layer.ShadowColor; + } + + if (hasShadow) + { + navigationBar.Layer.ShadowColor = UIColor.Black.CGColor; + navigationBar.Layer.ShadowOpacity = 1.0f; + } + else + { + navigationBar.Layer.ShadowColor = _shadowColor; + navigationBar.Layer.ShadowOpacity = _shadowOpacity; + } + } #endregion } -} \ No newline at end of file +} diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ShellSectionRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ShellSectionRenderer.cs index 9a949d5..a76b1a1 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/ShellSectionRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/ShellSectionRenderer.cs @@ -207,8 +207,8 @@ namespace Xamarin.Forms.Platform.iOS if (_displayedPage != null) { _displayedPage.PropertyChanged += OnDisplayedPagePropertyChanged; - if (!ShellSection.Stack.Contains(_displayedPage)) - UpdateNavigationBarHidden(); + UpdateNavigationBarHidden(); + UpdateNavigationBarHasShadow(); } } @@ -377,6 +377,8 @@ namespace Xamarin.Forms.Platform.iOS { if (e.PropertyName == Shell.NavBarIsVisibleProperty.PropertyName) UpdateNavigationBarHidden(); + else if (e.PropertyName == Shell.NavBarHasShadowProperty.PropertyName) + UpdateNavigationBarHasShadow(); } void PushPage(Page page, bool animated, TaskCompletionSource completionSource = null) @@ -431,6 +433,11 @@ namespace Xamarin.Forms.Platform.iOS SetNavigationBarHidden(!Shell.GetNavBarIsVisible(_displayedPage), true); } + void UpdateNavigationBarHasShadow() + { + _appearanceTracker.SetHasShadow(this, Shell.GetNavBarHasShadow(_displayedPage)); + } + void UpdateShadowImages() { NavigationBar.SetValueForKey(NSObject.FromObject(true), new NSString("hidesShadow")); diff --git a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj index 91f22fb..35d3dc3 100644 --- a/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj +++ b/Xamarin.Forms.Platform.iOS/Xamarin.Forms.Platform.iOS.csproj @@ -199,7 +199,6 @@ - @@ -243,6 +242,7 @@ +