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;
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);
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);
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);
--- /dev/null
+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
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
{
//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<AppBarLayout>();
+ _globalLayoutListener = new GenericGlobalLayoutListener(() => UpdateNavBarHasShadow(Page));
+ _appBar.ViewTreeObserver.AddOnGlobalLayoutListener(_globalLayoutListener);
_toolbar.SetNavigationOnClickListener(this);
((IShellController)_shellContext.Shell).AddFlyoutBehaviorObserver(this);
}
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);
_drawerToggle?.Dispose();
}
+ _globalLayoutListener = null;
_backButtonBehavior = null;
SearchHandler = null;
_shellContext = null;
_searchView = null;
Page = null;
_toolbar = null;
+ _appBar = null;
_drawerLayout = null;
base.Dispose(disposing);
}
UpdateLeftBarButtonItem();
UpdateToolbarItems();
UpdateNavBarVisible(_toolbar, newPage);
+ UpdateNavBarHasShadow(newPage);
UpdateTitleView();
}
}
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);
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;
<Compile Include="Extensions\FlowDirectionExtensions.cs" />
<Compile Include="AppCompat\ImageButtonRenderer.cs" />
<Compile Include="FastRenderers\ImageElementManager.cs" />
+ <Compile Include="GenericGlobalLayoutListener.cs" />
<Compile Include="GestureManager.cs" />
<Compile Include="FastRenderers\LabelRenderer.cs" />
<Compile Include="FastRenderers\VisualElementRenderer.cs" />
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
-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
-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();
}
}
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)
}
#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
+}
if (_displayedPage != null)
{
_displayedPage.PropertyChanged += OnDisplayedPagePropertyChanged;
- if (!ShellSection.Stack.Contains(_displayedPage))
- UpdateNavigationBarHidden();
+ UpdateNavigationBarHidden();
+ UpdateNavigationBarHasShadow();
}
}
{
if (e.PropertyName == Shell.NavBarIsVisibleProperty.PropertyName)
UpdateNavigationBarHidden();
+ else if (e.PropertyName == Shell.NavBarHasShadowProperty.PropertyName)
+ UpdateNavigationBarHasShadow();
}
void PushPage(Page page, bool animated, TaskCompletionSource<bool> completionSource = null)
SetNavigationBarHidden(!Shell.GetNavBarIsVisible(_displayedPage), true);
}
+ void UpdateNavigationBarHasShadow()
+ {
+ _appearanceTracker.SetHasShadow(this, Shell.GetNavBarHasShadow(_displayedPage));
+ }
+
void UpdateShadowImages()
{
NavigationBar.SetValueForKey(NSObject.FromObject(true), new NSString("hidesShadow"));
<Compile Include="Renderers\ShellFlyoutRenderer.cs" />
<Compile Include="Renderers\ShellItemRenderer.cs" />
<Compile Include="Renderers\ShellItemTransition.cs" />
- <Compile Include="Renderers\ShellNavBarAppearanceTracker.cs" />
<Compile Include="Renderers\ShellPageRendererTracker.cs" />
<Compile Include="Renderers\ShellRenderer.cs" />
<Compile Include="Renderers\ShellScrollViewTracker.cs" />
<Compile Include="CollectionView\CarouselViewController.cs" />
<Compile Include="CollectionView\CarouselTemplatedCell.cs" />
<Compile Include="Renderers\RefreshViewRenderer.cs" />
+ <Compile Include="Renderers\ShellNavBarAppearanceTracker.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\StringResources.ar.resx" />