File: iOS\Renderers\NavigationRenderer.cs
Web Access
Project: src\src\Compatibility\Core\src\Compatibility.csproj (Microsoft.Maui.Controls.Compatibility)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using CoreGraphics;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Graphics;
using ObjCRuntime;
using UIKit;
using static Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.NavigationPage;
using static Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page;
using PageUIStatusBarAnimation = Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.UIStatusBarAnimation;
using PointF = CoreGraphics.CGPoint;
using RectangleF = CoreGraphics.CGRect;
using SizeF = CoreGraphics.CGSize;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS
{
	[Obsolete("Use Microsoft.Maui.Controls.Handlers.Compatibility.NavigationRenderer instead")]
	public class NavigationRenderer : UINavigationController, IVisualElementRenderer, IEffectControlProvider
	{
		internal const string UpdateToolbarButtons = "Xamarin.UpdateToolbarButtons";
		bool _appeared;
		bool _ignorePopCall;
		bool _loaded;
		FlyoutPage _parentFlyoutPage;
		Size _queuedSize;
		UIViewController[] _removeControllers;
		UIToolbar _secondaryToolbar;
		VisualElementTracker _tracker;
		nfloat _navigationBottom = 0;
		bool _hasNavigationBar;
		UIImage _defaultNavBarShadowImage;
		UIImage _defaultNavBarBackImage;
		bool _disposed;
 
		[Preserve(Conditional = true)]
		public NavigationRenderer() : base(typeof(FormsNavigationBar), null)
		{
			MessagingCenter.Subscribe<IVisualElementRenderer>(this, UpdateToolbarButtons, sender =>
			{
				if (!ViewControllers.Any())
					return;
				var parentingViewController = GetParentingViewController();
				parentingViewController?.UpdateLeftBarButtonItem();
			});
		}
 
		Page Current { get; set; }
 
		IPageController PageController => Element as IPageController;
 
		NavigationPage NavPage => Element as NavigationPage;
		INavigationPageController NavPageController => NavPage;
 
		public VisualElement Element { get; private set; }
 
		public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
 
		public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
		{
			return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
		}
 
		public UIView NativeView
		{
			get { return View; }
		}
 
		public void SetElement(VisualElement element)
		{
			var oldElement = Element;
			Element = element;
			OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
 
			if (element != null)
				element.SendViewInitialized(NativeView);
 
			EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);
		}
 
		public void SetElementSize(Size size)
		{
			if (_loaded)
				Element.Layout(new Rect(Element.X, Element.Y, size.Width, size.Height));
			else
				_queuedSize = size;
		}
 
		public UIViewController ViewController
		{
			get { return this; }
		}
 
		//TODO: this was deprecated in iOS8.0 and is not called in 9.0+
		[UnsupportedOSPlatform("ios8.0")]
		[UnsupportedOSPlatform("tvos")]
		public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
		{
			base.DidRotate(fromInterfaceOrientation);
 
			View.SetNeedsLayout();
 
			var parentingViewController = GetParentingViewController();
			parentingViewController?.UpdateLeftBarButtonItem();
		}
 
		public Task<bool> PopToRootAsync(Page page, bool animated = true)
		{
			return OnPopToRoot(page, animated);
		}
 
		public override UIViewController[] PopToRootViewController(bool animated)
		{
			if (!_ignorePopCall && ViewControllers.Length > 1)
				RemoveViewControllers(animated);
 
			return base.PopToRootViewController(animated);
		}
 
		public Task<bool> PopViewAsync(Page page, bool animated = true)
		{
			return OnPopViewAsync(page, animated);
		}
 
		public override UIViewController PopViewController(bool animated)
		{
			RemoveViewControllers(animated);
			return base.PopViewController(animated);
		}
 
		public Task<bool> PushPageAsync(Page page, bool animated = true)
		{
			return OnPushAsync(page, animated);
		}
 
		public override void ViewDidAppear(bool animated)
		{
			if (!_appeared)
			{
				_appeared = true;
				PageController?.SendAppearing();
			}
 
			base.ViewDidAppear(animated);
 
			View.SetNeedsLayout();
		}
 
		public override void ViewWillAppear(bool animated)
		{
			base.ViewWillAppear(animated);
 
			SetStatusBarStyle();
		}
 
		public override void ViewDidDisappear(bool animated)
		{
			base.ViewDidDisappear(animated);
 
			if (!_appeared || Element == null)
				return;
 
			_appeared = false;
			PageController.SendDisappearing();
		}
 
		public override void ViewDidLayoutSubviews()
		{
			base.ViewDidLayoutSubviews();
			if (Current == null)
				return;
			UpdateToolBarVisible();
 
			var navBarFrameBottom = Math.Min(NavigationBar.Frame.Bottom, 140);
			_navigationBottom = (nfloat)navBarFrameBottom;
			var toolbar = _secondaryToolbar;
 
			//save the state of the Current page we are calculating, this will fire before Current is updated
			_hasNavigationBar = NavigationPage.GetHasNavigationBar(Current);
 
			// Use 0 if the NavBar is hidden or will be hidden
			var toolbarY = NavigationBarHidden || NavigationBar.Translucent || !_hasNavigationBar ? 0 : navBarFrameBottom;
			toolbar.Frame = new RectangleF(0, toolbarY, View.Frame.Width, toolbar.Frame.Height);
 
			double trueBottom = toolbar.Hidden ? toolbarY : toolbar.Frame.Bottom;
			var modelSize = _queuedSize.IsZero ? Element.Bounds.Size : _queuedSize;
			PageController.ContainerArea =
				new Rect(0, toolbar.Hidden ? 0 : toolbar.Frame.Height, modelSize.Width, modelSize.Height - trueBottom);
 
			if (!_queuedSize.IsZero)
			{
				Element.Layout(new Rect(Element.X, Element.Y, _queuedSize.Width, _queuedSize.Height));
				_queuedSize = Size.Zero;
			}
 
			_loaded = true;
 
			foreach (var view in View.Subviews)
			{
				if (view == NavigationBar || view == _secondaryToolbar)
					continue;
				view.Frame = View.Bounds;
			}
		}
 
		public override void ViewDidLoad()
		{
			base.ViewDidLoad();
 
			UpdateTranslucent();
 
			_secondaryToolbar = new SecondaryToolbar { Frame = new RectangleF(0, 0, 320, 44) };
			View.Add(_secondaryToolbar);
			_secondaryToolbar.Hidden = true;
 
			FindParentFlyoutPage();
 
			var navPage = NavPage;
			INavigationPageController navPageController = NavPage;
			if (navPage.CurrentPage == null)
			{
				throw new InvalidOperationException(
					"NavigationPage must have a root Page before being used. Either call PushAsync with a valid Page, or pass a Page to the constructor before usage.");
			}
 
			navPageController.PushRequested += OnPushRequested;
			navPageController.PopRequested += OnPopRequested;
			navPageController.PopToRootRequested += OnPopToRootRequested;
			navPageController.RemovePageRequested += OnRemovedPageRequested;
			navPageController.InsertPageBeforeRequested += OnInsertPageBeforeRequested;
 
			UpdateBarBackground();
			UpdateBarTextColor();
			UpdateHideNavigationBarSeparator();
			UpdateUseLargeTitles();
 
			if (OperatingSystem.IsIOSVersionAtLeast(11))
				SetNeedsUpdateOfHomeIndicatorAutoHidden();
 
			// If there is already stuff on the stack we need to push it
			NavPageController.Pages.ForEach(async p => await PushPageAsync(p, false));
 
			_tracker = new VisualElementTracker(this);
 
			Element.PropertyChanged += HandlePropertyChanged;
 
			UpdateToolBarVisible();
			UpdateBackgroundColor();
			Current = navPage.CurrentPage;
		}
 
		protected override void Dispose(bool disposing)
		{
			if (_disposed)
			{
				return;
			}
 
			_disposed = true;
 
			if (disposing)
			{
				MessagingCenter.Unsubscribe<IVisualElementRenderer>(this, UpdateToolbarButtons);
 
				foreach (var childViewController in ViewControllers)
					childViewController.Dispose();
 
				if (_tracker != null)
					_tracker.Dispose();
 
				_secondaryToolbar.RemoveFromSuperview();
				_secondaryToolbar.Dispose();
				_secondaryToolbar = null;
 
				_parentFlyoutPage = null;
				Current = null; // unhooks events
 
				var navPage = NavPage;
				INavigationPageController navPageController = NavPage;
				navPage.PropertyChanged -= HandlePropertyChanged;
 
				navPageController.PushRequested -= OnPushRequested;
				navPageController.PopRequested -= OnPopRequested;
				navPageController.PopToRootRequested -= OnPopToRootRequested;
				navPageController.RemovePageRequested -= OnRemovedPageRequested;
				navPageController.InsertPageBeforeRequested -= OnInsertPageBeforeRequested;
			}
 
			base.Dispose(disposing);
 
			if (disposing && _appeared)
			{
				PageController.SendDisappearing();
 
				_appeared = false;
			}
		}
 
		protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
		{
			ElementChanged?.Invoke(this, e);
		}
 
		protected virtual async Task<bool> OnPopToRoot(Page page, bool animated)
		{
			_ignorePopCall = true;
			var renderer = Platform.GetRenderer(page);
			if (renderer == null || renderer.ViewController == null)
				return false;
 
			var task = GetAppearedOrDisappearedTask(page);
 
			PopToRootViewController(animated);
 
			_ignorePopCall = false;
			var success = !await task;
 
			UpdateToolBarVisible();
			return success;
		}
 
		protected virtual async Task<bool> OnPopViewAsync(Page page, bool animated)
		{
			if (_ignorePopCall)
				return true;
 
			var renderer = Platform.GetRenderer(page);
			if (renderer == null || renderer.ViewController == null)
				return false;
 
			if (page != ((ParentingViewController)TopViewController).Child)
				throw new NotSupportedException("Popped page does not appear on top of current navigation stack, please file a bug.");
 
			var task = GetAppearedOrDisappearedTask(page);
 
			UIViewController poppedViewController;
			_ignorePopCall = true;
			poppedViewController = base.PopViewController(animated);
 
			var actuallyRemoved = poppedViewController == null ? true : !await task;
			_ignorePopCall = false;
 
			poppedViewController?.Dispose();
 
			UpdateToolBarVisible();
			return actuallyRemoved;
		}
 
		protected virtual async Task<bool> OnPushAsync(Page page, bool animated)
		{
			if (page is FlyoutPage)
				System.Diagnostics.Trace.WriteLine($"Pushing a {nameof(FlyoutPage)} onto a {nameof(NavigationPage)} is not a supported UI pattern on iOS. " +
					"Please see https://developer.apple.com/documentation/uikit/uisplitviewcontroller for more details.");
 
			var pack = CreateViewControllerForPage(page);
			var task = GetAppearedOrDisappearedTask(page);
 
			PushViewController(pack, animated);
 
			var shown = await task;
			UpdateToolBarVisible();
			return shown;
		}
 
		public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection)
		{
#pragma warning disable CA1422 // Validate platform compatibility
			base.TraitCollectionDidChange(previousTraitCollection);
#pragma warning restore CA1422 // Validate platform compatibility			// Make sure the control adheres to changes in UI theme
			if (Forms.IsiOS13OrNewer && previousTraitCollection?.UserInterfaceStyle != TraitCollection.UserInterfaceStyle)
				UpdateBackgroundColor();
		}
 
 
		ParentingViewController CreateViewControllerForPage(Page page)
		{
			if (Platform.GetRenderer(page) == null)
				Platform.SetRenderer(page, Platform.CreateRenderer(page));
 
			// must pack into container so padding can work
			// otherwise the view controller is forced to 0,0
			var pack = new ParentingViewController(this) { Child = page };
 
			pack.UpdateTitleArea(page);
 
			var pageRenderer = Platform.GetRenderer(page);
			pack.View.AddSubview(pageRenderer.ViewController.View);
			pack.AddChildViewController(pageRenderer.ViewController);
			pageRenderer.ViewController.DidMoveToParentViewController(pack);
 
			return pack;
		}
 
		ParentingViewController GetParentingViewController()
		{
			if (!ViewControllers.Any())
				return null;
 
			return ViewControllers.Last() as ParentingViewController;
		}
 
		void FindParentFlyoutPage()
		{
			var page = Element as Page;
			var parentPages = page.GetParentPages();
			var flyoutDetail = parentPages.OfType<FlyoutPage>().FirstOrDefault();
 
			if (flyoutDetail != null && parentPages.Append((Page)Element).Contains(flyoutDetail.Detail))
				_parentFlyoutPage = flyoutDetail;
		}
 
		Task<bool> GetAppearedOrDisappearedTask(Page page)
		{
			var tcs = new TaskCompletionSource<bool>();
 
			var parentViewController = Platform.GetRenderer(page).ViewController.ParentViewController as ParentingViewController;
			if (parentViewController == null)
				throw new NotSupportedException("ParentingViewController parent could not be found. Please file a bug.");
 
			EventHandler appearing = null, disappearing = null;
			appearing = (s, e) =>
			{
				parentViewController.Appearing -= appearing;
				parentViewController.Disappearing -= disappearing;
 
				Device.BeginInvokeOnMainThread(() => { tcs.SetResult(true); });
			};
 
			disappearing = (s, e) =>
			{
				parentViewController.Appearing -= appearing;
				parentViewController.Disappearing -= disappearing;
 
				Device.BeginInvokeOnMainThread(() => { tcs.SetResult(false); });
			};
 
			parentViewController.Appearing += appearing;
			parentViewController.Disappearing += disappearing;
 
			return tcs.Task;
		}
 
		void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName ||
				e.PropertyName == NavigationPage.BarBackgroundProperty.PropertyName)
			{
				UpdateBarBackground();
			}
			else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName
				  || e.PropertyName == StatusBarTextColorModeProperty.PropertyName)
			{
				UpdateBarTextColor();
				SetStatusBarStyle();
			}
			else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
			{
				UpdateBackgroundColor();
			}
			else if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
			{
				Current = NavPage?.CurrentPage;
				ValidateNavbarExists(Current);
			}
			else if (e.PropertyName == IsNavigationBarTranslucentProperty.PropertyName)
			{
				UpdateTranslucent();
			}
			else if (e.PropertyName == PreferredStatusBarUpdateAnimationProperty.PropertyName)
			{
				UpdateCurrentPagePreferredStatusBarUpdateAnimation();
			}
			else if (e.PropertyName == PrefersLargeTitlesProperty.PropertyName)
			{
				UpdateUseLargeTitles();
			}
			else if (e.PropertyName == NavigationPage.BackButtonTitleProperty.PropertyName)
			{
				var pack = (ParentingViewController)TopViewController;
				pack?.UpdateTitleArea(pack.Child);
			}
			else if (e.PropertyName == HideNavigationBarSeparatorProperty.PropertyName)
			{
				UpdateHideNavigationBarSeparator();
			}
		}
 
		void ValidateNavbarExists(Page newCurrentPage)
		{
			//if the last time we did ViewDidLayoutSubviews we had other value for _hasNavigationBar
			//we will need to relayout. This is because Current is updated async of the layout happening
			if (_hasNavigationBar != NavigationPage.GetHasNavigationBar(newCurrentPage))
				ViewDidLayoutSubviews();
		}
 
		void UpdateHideNavigationBarSeparator()
		{
			bool shouldHide = NavPage.OnThisPlatform().HideNavigationBarSeparator();
 
			// Just setting the ShadowImage is good for iOS11
			if (_defaultNavBarShadowImage == null)
				_defaultNavBarShadowImage = NavigationBar.ShadowImage;
 
			if (Forms.IsiOS13OrNewer)
			{
				if (shouldHide)
				{
					NavigationBar.CompactAppearance.ShadowColor = UIColor.Clear;
					NavigationBar.StandardAppearance.ShadowColor = UIColor.Clear;
					NavigationBar.ScrollEdgeAppearance.ShadowColor = UIColor.Clear;
				}
				else
				{
					NavigationBar.CompactAppearance.ShadowColor = UIColor.FromRGBA(0, 0, 0, 76); //default ios13 shadow color
					NavigationBar.StandardAppearance.ShadowColor = UIColor.FromRGBA(0, 0, 0, 76);
					NavigationBar.ScrollEdgeAppearance.ShadowColor = UIColor.FromRGBA(0, 0, 0, 76);
				}
			}
			else
			{
				if (shouldHide)
					NavigationBar.ShadowImage = new UIImage();
				else
					NavigationBar.ShadowImage = _defaultNavBarShadowImage;
			}
 
			if (!Forms.IsiOS11OrNewer)
			{
				// For iOS 10 and lower, you need to set the background image.
				// If you set this for iOS11, you'll remove the background color.
				if (_defaultNavBarBackImage == null)
					_defaultNavBarBackImage = NavigationBar.GetBackgroundImage(UIBarMetrics.Default);
 
				if (shouldHide)
					NavigationBar.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
				else
					NavigationBar.SetBackgroundImage(_defaultNavBarBackImage, UIBarMetrics.Default);
			}
		}
 
		void UpdateCurrentPagePreferredStatusBarUpdateAnimation()
		{
			// Not using the extension method syntax here because for some reason it confuses the mono compiler
			// and throws a CS0121 error
			PageUIStatusBarAnimation animation = PlatformConfiguration.iOSSpecific.Page.PreferredStatusBarUpdateAnimation(((Page)Element).OnThisPlatform());
			PlatformConfiguration.iOSSpecific.Page.SetPreferredStatusBarUpdateAnimation(Current.OnThisPlatform(), animation);
		}
 
		[PortHandler]
		void UpdateUseLargeTitles()
		{
			if (Forms.IsiOS11OrNewer && NavPage != null)
				NavigationBar.PrefersLargeTitles = NavPage.OnThisPlatform().PrefersLargeTitles();
		}
 
		[PortHandler]
		void UpdateTranslucent()
		{
			NavigationBar.Translucent = NavPage.OnThisPlatform().IsNavigationBarTranslucent();
		}
 
		void InsertPageBefore(Page page, Page before)
		{
			if (before == null)
				throw new ArgumentNullException("before");
			if (page == null)
				throw new ArgumentNullException("page");
 
			var pageContainer = CreateViewControllerForPage(page);
			var target = Platform.GetRenderer(before).ViewController.ParentViewController;
			ViewControllers = ViewControllers.Insert(ViewControllers.IndexOf(target), pageContainer);
		}
 
		void OnInsertPageBeforeRequested(object sender, NavigationRequestedEventArgs e)
		{
			InsertPageBefore(e.Page, e.BeforePage);
		}
 
		void OnPopRequested(object sender, NavigationRequestedEventArgs e)
		{
			e.Task = PopViewAsync(e.Page, e.Animated);
		}
 
		void OnPopToRootRequested(object sender, NavigationRequestedEventArgs e)
		{
			e.Task = PopToRootAsync(e.Page, e.Animated);
		}
 
		void OnPushRequested(object sender, NavigationRequestedEventArgs e)
		{
			// If any text entry controls have focus, we need to end their editing session
			// so that they are not the first responder; if we don't some things (like the activity indicator
			// on pull-to-refresh) will not work correctly.
			View?.Window?.EndEditing(true);
 
			e.Task = PushPageAsync(e.Page, e.Animated);
		}
 
		void OnRemovedPageRequested(object sender, NavigationRequestedEventArgs e)
		{
			RemovePage(e.Page);
		}
 
		void RemovePage(Page page)
		{
			if (page == null)
				throw new ArgumentNullException("page");
			if (page == Current)
				throw new NotSupportedException(); // should never happen as NavPage protects against this
 
			var target = Platform.GetRenderer(page).ViewController.ParentViewController;
 
			// So the ViewControllers property is not very property like on iOS. Assigning to it doesn't cause it to be
			// immediately reflected into the property. The change will not be reflected until there has been sufficient time
			// to process it (it ends up on the event queue). So to resolve this issue we keep our own stack until we
			// know iOS has processed it, and make sure any updates use that.
 
			// In the future we may want to make RemovePageAsync and deprecate RemovePage to handle cases where Push/Pop is called
			// during a remove cycle.
 
			if (_removeControllers == null)
			{
				_removeControllers = ViewControllers.Remove(target);
				ViewControllers = _removeControllers;
				Device.BeginInvokeOnMainThread(() => { _removeControllers = null; });
			}
			else
			{
				_removeControllers = _removeControllers.Remove(target);
				ViewControllers = _removeControllers;
			}
			target.Dispose();
			var parentingViewController = GetParentingViewController();
			parentingViewController?.UpdateLeftBarButtonItem(page);
		}
 
		void RemoveViewControllers(bool animated)
		{
			var controller = TopViewController as ParentingViewController;
			if (controller == null || controller.Child == null || Platform.GetRenderer(controller.Child) == null)
				return;
 
			// Gesture in progress, lets not be proactive and just wait for it to finish
			var task = GetAppearedOrDisappearedTask(controller.Child);
 
			task.ContinueWith(t =>
			{
				// task returns true if the user lets go of the page and is not popped
				// however at this point the renderer is already off the visual stack so we just need to update the NavigationPage
				// Also worth noting this task returns on the main thread
				if (t.Result)
					return;
				// because we skip the normal pop process we need to dispose ourselves
				controller?.Dispose();
			}, TaskScheduler.FromCurrentSynchronizationContext());
		}
 
		void UpdateBackgroundColor()
		{
			var color = Element.BackgroundColor ?? Maui.Platform.ColorExtensions.BackgroundColor.ToColor();
			View.BackgroundColor = color.ToPlatform();
		}
 
		void UpdateBarBackground()
		{
			var barBackgroundColor = NavPage.BarBackgroundColor;
 
			if (Forms.IsiOS13OrNewer)
			{
				var navigationBarAppearance = NavigationBar.StandardAppearance;
 
				navigationBarAppearance.ConfigureWithOpaqueBackground();
 
				if (barBackgroundColor == null)
				{
					navigationBarAppearance.BackgroundColor = Maui.Platform.ColorExtensions.BackgroundColor;
 
					var parentingViewController = GetParentingViewController();
					parentingViewController?.SetupDefaultNavigationBarAppearance();
				}
				else
					navigationBarAppearance.BackgroundColor = barBackgroundColor.ToPlatform();
 
				var barBackgroundBrush = NavPage.BarBackground;
				var backgroundImage = NavigationBar.GetBackgroundImage(barBackgroundBrush);
				navigationBarAppearance.BackgroundImage = backgroundImage;
 
				NavigationBar.CompactAppearance = navigationBarAppearance;
				NavigationBar.StandardAppearance = navigationBarAppearance;
				NavigationBar.ScrollEdgeAppearance = navigationBarAppearance;
			}
			else
			{
				// Set navigation bar background color
				NavigationBar.BarTintColor = barBackgroundColor == null
					? UINavigationBar.Appearance.BarTintColor
					: barBackgroundColor.ToPlatform();
 
				var barBackgroundBrush = NavPage.BarBackground;
				var backgroundImage = NavigationBar.GetBackgroundImage(barBackgroundBrush);
				NavigationBar.SetBackgroundImage(backgroundImage, UIBarMetrics.Default);
			}
		}
 
		void UpdateBarTextColor()
		{
			var barTextColor = NavPage.BarTextColor;
 
			// Determine new title text attributes via global static data
			var globalTitleTextAttributes = UINavigationBar.Appearance.TitleTextAttributes;
			var titleTextAttributes = new UIStringAttributes
			{
				ForegroundColor = barTextColor == null ? globalTitleTextAttributes?.ForegroundColor : barTextColor.ToPlatform(),
				Font = globalTitleTextAttributes?.Font
			};
 
			// Determine new large title text attributes via global static data
			var largeTitleTextAttributes = titleTextAttributes;
			if (Forms.IsiOS11OrNewer)
			{
				var globalLargeTitleTextAttributes = UINavigationBar.Appearance.LargeTitleTextAttributes;
 
				largeTitleTextAttributes = new UIStringAttributes
				{
					ForegroundColor = barTextColor == null ? globalLargeTitleTextAttributes?.ForegroundColor : barTextColor.ToPlatform(),
					Font = globalLargeTitleTextAttributes?.Font
				};
			}
 
			if (Forms.IsiOS13OrNewer)
			{
				NavigationBar.CompactAppearance.TitleTextAttributes = titleTextAttributes;
				NavigationBar.CompactAppearance.LargeTitleTextAttributes = largeTitleTextAttributes;
 
				NavigationBar.StandardAppearance.TitleTextAttributes = titleTextAttributes;
				NavigationBar.StandardAppearance.LargeTitleTextAttributes = largeTitleTextAttributes;
 
				NavigationBar.ScrollEdgeAppearance.TitleTextAttributes = titleTextAttributes;
				NavigationBar.ScrollEdgeAppearance.LargeTitleTextAttributes = largeTitleTextAttributes;
			}
			else
			{
				NavigationBar.TitleTextAttributes = titleTextAttributes;
 
				if (Forms.IsiOS11OrNewer)
					NavigationBar.LargeTitleTextAttributes = largeTitleTextAttributes;
			}
 
			// set Tint color (i. e. Back Button arrow and Text)
			var iconColor = Current != null ? NavigationPage.GetIconColor(Current) : null;
			if (iconColor == null)
				iconColor = barTextColor;
 
			NavigationBar.TintColor = iconColor == null || NavPage.OnThisPlatform().GetStatusBarTextColorMode() == StatusBarTextColorMode.DoNotAdjust
				? UINavigationBar.Appearance.TintColor
				: iconColor.ToPlatform();
		}
 
		void SetStatusBarStyle()
		{
			var barTextColor = NavPage.BarTextColor;
			var statusBarColorMode = NavPage.OnThisPlatform().GetStatusBarTextColorMode();
 
#pragma warning disable CA1416, CA1422 // TODO: UIApplication.StatusBarStyle has [UnsupportedOSPlatform("ios9.0")]
			if (statusBarColorMode == StatusBarTextColorMode.DoNotAdjust || barTextColor?.GetLuminosity() <= 0.5)
			{
				// Use dark text color for status bar
				if (Forms.IsiOS13OrNewer)
				{
					UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.DarkContent;
				}
				else
				{
					UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.Default;
				}
			}
			else
			{
				// Use light text color for status bar
				UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent;
			}
#pragma warning restore CA1416, CA1422
		}
 
		void UpdateToolBarVisible()
		{
			if (_secondaryToolbar == null)
				return;
			if (TopViewController != null && TopViewController.ToolbarItems != null && TopViewController.ToolbarItems.Any())
			{
				_secondaryToolbar.Hidden = false;
				_secondaryToolbar.Items = TopViewController.ToolbarItems;
			}
			else
			{
				_secondaryToolbar.Hidden = true;
				//secondaryToolbar.Items = null;
			}
 
			TopViewController?.NavigationItem?.TitleView?.SizeToFit();
			TopViewController?.NavigationItem?.TitleView?.LayoutSubviews();
		}
 
		internal async Task UpdateFormsInnerNavigation(Page pageBeingRemoved)
		{
			if (NavPage == null)
				return;
			if (_ignorePopCall)
				return;
 
			_ignorePopCall = true;
			if (Element.Navigation.NavigationStack.Contains(pageBeingRemoved))
				await (NavPage as INavigationPageController)?.RemoveAsyncInner(pageBeingRemoved, false, true);
			_ignorePopCall = false;
 
		}
 
		internal static async void SetFlyoutLeftBarButton(UIViewController containerController, FlyoutPage FlyoutPage)
		{
			if (!FlyoutPage.ShouldShowToolbarButton())
			{
				containerController.NavigationItem.LeftBarButtonItem = null;
				return;
			}
 
			await FlyoutPage.Flyout.ApplyNativeImageAsync(Page.IconImageSourceProperty, icon =>
			{
				if (icon != null)
				{
					try
					{
						containerController.NavigationItem.LeftBarButtonItem = new UIBarButtonItem(icon, UIBarButtonItemStyle.Plain, OnItemTapped);
					}
					catch (Exception)
					{
						// Throws Exception otherwise would catch more specific exception type
					}
				}
 
				if (icon == null || containerController.NavigationItem.LeftBarButtonItem == null)
				{
					containerController.NavigationItem.LeftBarButtonItem = new UIBarButtonItem(FlyoutPage.Flyout.Title, UIBarButtonItemStyle.Plain, OnItemTapped);
				}
 
				if (FlyoutPage != null && !string.IsNullOrEmpty(FlyoutPage.AutomationId))
					SetAutomationId(containerController.NavigationItem.LeftBarButtonItem, $"btn_{FlyoutPage.AutomationId}");
#if __MOBILE__
				containerController.NavigationItem.LeftBarButtonItem.SetAccessibilityHint(FlyoutPage);
				containerController.NavigationItem.LeftBarButtonItem.SetAccessibilityLabel(FlyoutPage);
#endif
			});
 
			void OnItemTapped(object sender, EventArgs e)
			{
				FlyoutPage.IsPresented = !FlyoutPage.IsPresented;
			}
		}
 
		static void SetAccessibilityHint(UIBarButtonItem uIBarButtonItem, Element element)
		{
			if (element == null)
				return;
 
			if (_defaultAccessibilityHint == null)
				_defaultAccessibilityHint = uIBarButtonItem.AccessibilityHint;
 
			uIBarButtonItem.AccessibilityHint = (string)element.GetValue(AutomationProperties.HelpTextProperty) ?? _defaultAccessibilityHint;
		}
 
		static void SetAccessibilityLabel(UIBarButtonItem uIBarButtonItem, Element element)
		{
			if (element == null)
				return;
 
			if (_defaultAccessibilityLabel == null)
				_defaultAccessibilityLabel = uIBarButtonItem.AccessibilityLabel;
 
			uIBarButtonItem.AccessibilityLabel = (string)element.GetValue(AutomationProperties.NameProperty) ?? _defaultAccessibilityLabel;
		}
 
		static void SetIsAccessibilityElement(UIBarButtonItem uIBarButtonItem, Element element)
		{
			if (element == null)
				return;
 
			if (!_defaultIsAccessibilityElement.HasValue)
				_defaultIsAccessibilityElement = uIBarButtonItem.IsAccessibilityElement;
 
			uIBarButtonItem.IsAccessibilityElement = (bool)((bool?)element.GetValue(AutomationProperties.IsInAccessibleTreeProperty) ?? _defaultIsAccessibilityElement);
		}
 
		static void SetAccessibilityElementsHidden(UIBarButtonItem uIBarButtonItem, Element element)
		{
			if (element == null)
				return;
 
			if (!_defaultAccessibilityElementsHidden.HasValue)
				_defaultAccessibilityElementsHidden = uIBarButtonItem.AccessibilityElementsHidden;
 
			uIBarButtonItem.AccessibilityElementsHidden = (bool)((bool?)element.GetValue(AutomationProperties.ExcludedWithChildrenProperty) ?? _defaultAccessibilityElementsHidden);
		}
 
		static void SetAutomationId(UIBarButtonItem uIBarButtonItem, string id)
		{
			uIBarButtonItem.AccessibilityIdentifier = id;
		}
 
		static string _defaultAccessibilityLabel;
		static string _defaultAccessibilityHint;
		static bool? _defaultIsAccessibilityElement;
		static bool? _defaultAccessibilityElementsHidden;
 
		internal void ValidateInsets()
		{
			nfloat navBottom = NavigationBar.Frame.Bottom;
 
			if (_navigationBottom != navBottom && Current != null)
				ViewDidLayoutSubviews();
		}
 
		class SecondaryToolbar : UIToolbar
		{
			readonly List<UIView> _lines = new List<UIView>();
 
			public SecondaryToolbar()
			{
				TintColor = UIColor.White;
			}
 
			public override UIBarButtonItem[] Items
			{
				get { return base.Items; }
				set
				{
					base.Items = value;
					SetupLines();
				}
			}
 
			public override void LayoutSubviews()
			{
				base.LayoutSubviews();
				if (Items == null || Items.Length == 0)
					return;
				LayoutToolbarItems(Bounds.Width, Bounds.Height, 0);
			}
 
			void LayoutToolbarItems(nfloat toolbarWidth, nfloat toolbarHeight, nfloat padding)
			{
				var x = padding;
				var y = 0;
				var itemH = toolbarHeight;
				var itemW = toolbarWidth / Items.Length;
 
				foreach (var item in Items)
				{
					var frame = new RectangleF(x, y, itemW, itemH);
					if (frame == item.CustomView.Frame)
						continue;
					item.CustomView.Frame = frame;
					x += itemW + padding;
				}
 
				x = itemW + padding * 1.5f;
				y = (int)Bounds.GetMidY();
				foreach (var l in _lines)
				{
					l.Center = new PointF(x, y);
					x += itemW + padding;
				}
			}
 
			void SetupLines()
			{
				_lines.ForEach(l => l.RemoveFromSuperview());
				_lines.Clear();
				if (Items == null)
					return;
				for (var i = 1; i < Items.Length; i++)
				{
					var l = new UIView(new RectangleF(0, 0, 1, 24)) { BackgroundColor = new UIColor(0, 0, 0, 0.2f) };
					AddSubview(l);
					_lines.Add(l);
				}
			}
		}
 
		class ParentingViewController : UIViewController
		{
			readonly WeakReference<NavigationRenderer> _navigation;
 
			Page _child;
			bool _disposed;
			ToolbarTracker _tracker = new ToolbarTracker();
 
			public ParentingViewController(NavigationRenderer navigation)
			{
#pragma warning disable CA1416, CA1422 // TODO: 'UIViewController.AutomaticallyAdjustsScrollViewInsets' is unsupported on: 'ios' 11.0 and later
				AutomaticallyAdjustsScrollViewInsets = false;
#pragma warning restore CA1416, CA1422
 
				_navigation = new WeakReference<NavigationRenderer>(navigation);
			}
 
			public Page Child
			{
				get { return _child; }
				set
				{
					if (_child == value)
						return;
 
					if (_child != null)
						_child.PropertyChanged -= HandleChildPropertyChanged;
 
					_child = value;
 
					if (_child != null)
						_child.PropertyChanged += HandleChildPropertyChanged;
 
					UpdateHasBackButton();
					UpdateLargeTitles();
					UpdateIconColor();
				}
			}
 
			public event EventHandler Appearing;
 
			[UnsupportedOSPlatform("ios8.0")]
			[UnsupportedOSPlatform("tvos")]
			public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
			{
				base.DidRotate(fromInterfaceOrientation);
 
				View.SetNeedsLayout();
			}
 
			public event EventHandler Disappearing;
 
			public override void ViewDidAppear(bool animated)
			{
				base.ViewDidAppear(animated);
 
				Appearing?.Invoke(this, EventArgs.Empty);
			}
 
			public override void ViewDidDisappear(bool animated)
			{
				base.ViewDidDisappear(animated);
 
				// force a redraw for right toolbar items by resetting TintColor to prevent
				// toolbar items being grayed out when canceling swipe to a previous page
				foreach (var item in NavigationItem?.RightBarButtonItems)
				{
					if (item.Image != null)
						continue;
 
					var tintColor = item.TintColor;
					item.TintColor = tintColor == null ? UIColor.Clear : null;
					item.TintColor = tintColor;
				}
 
				Disappearing?.Invoke(this, EventArgs.Empty);
			}
 
			public override void ViewWillLayoutSubviews()
			{
				base.ViewWillLayoutSubviews();
 
				NavigationRenderer n;
				if (_navigation.TryGetTarget(out n))
					n.ValidateInsets();
			}
 
			public override void ViewDidLayoutSubviews()
			{
				IVisualElementRenderer childRenderer;
				if (Child != null && (childRenderer = Platform.GetRenderer(Child)) != null)
					childRenderer.NativeView.Frame = Child.Bounds.ToRectangleF();
				base.ViewDidLayoutSubviews();
			}
 
			public override void ViewDidLoad()
			{
				base.ViewDidLoad();
 
				_tracker.Target = Child;
				_tracker.AdditionalTargets = Child.GetParentPages();
				_tracker.CollectionChanged += TrackerOnCollectionChanged;
 
				UpdateToolbarItems();
			}
 
			public override void ViewWillAppear(bool animated)
			{
				SetupDefaultNavigationBarAppearance();
				UpdateNavigationBarVisibility(animated);
 
				NavigationRenderer n;
				var isTranslucent = false;
				if (_navigation.TryGetTarget(out n))
					isTranslucent = n.NavigationBar.Translucent;
				EdgesForExtendedLayout = isTranslucent ? UIRectEdge.All : UIRectEdge.None;
 
				base.ViewWillAppear(animated);
			}
 
			protected override void Dispose(bool disposing)
			{
				if (_disposed)
				{
					return;
				}
 
				_disposed = true;
 
				if (disposing)
				{
					if (Child != null)
					{
						Child.SendDisappearing();
						Child.PropertyChanged -= HandleChildPropertyChanged;
						Child = null;
					}
 
					_tracker.Target = null;
					_tracker.CollectionChanged -= TrackerOnCollectionChanged;
					_tracker = null;
 
					if (NavigationItem.TitleView != null)
					{
						NavigationItem.TitleView.Dispose();
						NavigationItem.TitleView = null;
					}
 
					if (NavigationItem.RightBarButtonItems != null)
					{
						for (var i = 0; i < NavigationItem.RightBarButtonItems.Length; i++)
							NavigationItem.RightBarButtonItems[i].Dispose();
					}
 
					if (ToolbarItems != null)
					{
						for (var i = 0; i < ToolbarItems.Length; i++)
							ToolbarItems[i].Dispose();
					}
				}
 
				base.Dispose(disposing);
			}
 
			void HandleChildPropertyChanged(object sender, PropertyChangedEventArgs e)
			{
				if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName)
					UpdateNavigationBarVisibility(true);
				else if (e.PropertyName == Page.TitleProperty.PropertyName)
					NavigationItem.Title = Child.Title;
				else if (e.PropertyName == NavigationPage.HasBackButtonProperty.PropertyName)
					UpdateHasBackButton();
				else if (e.PropertyName == PrefersStatusBarHiddenProperty.PropertyName)
					UpdatePrefersStatusBarHidden();
				else if (e.PropertyName == LargeTitleDisplayProperty.PropertyName)
					UpdateLargeTitles();
				else if (e.PropertyName == NavigationPage.TitleIconImageSourceProperty.PropertyName ||
					 e.PropertyName == NavigationPage.TitleViewProperty.PropertyName)
					UpdateTitleArea(Child);
				else if (e.PropertyName == NavigationPage.IconColorProperty.PropertyName)
					UpdateIconColor();
			}
 
			internal void SetupDefaultNavigationBarAppearance()
			{
				if (!Forms.IsiOS13OrNewer)
					return;
 
				if (!_navigation.TryGetTarget(out NavigationRenderer navigationRenderer))
					return;
 
				// We will use UINavigationBar.Appareance to infer settings that
				// were already set to navigation bar in older versions of
				// iOS.
				var navBar = navigationRenderer.NavigationBar;
				var navAppearance = navBar.StandardAppearance;
 
				if (navAppearance.BackgroundColor == null)
				{
					var backgroundColor = navBar.BarTintColor;
					navBar.CompactAppearance.BackgroundColor = navBar.StandardAppearance.BackgroundColor = navBar.ScrollEdgeAppearance.BackgroundColor = backgroundColor;
				}
 
				if (navAppearance.BackgroundImage == null)
				{
					var backgroundImage = navBar.GetBackgroundImage(UIBarMetrics.Default);
					navBar.CompactAppearance.BackgroundImage = navBar.StandardAppearance.BackgroundImage = navBar.ScrollEdgeAppearance.BackgroundImage = backgroundImage;
				}
 
				if (navAppearance.ShadowImage == null)
				{
					var shadowImage = navBar.ShadowImage;
					navBar.CompactAppearance.ShadowImage = navBar.StandardAppearance.ShadowImage = navBar.ScrollEdgeAppearance.ShadowImage = shadowImage;
 
					if (shadowImage != null && shadowImage.Size == SizeF.Empty)
						navBar.CompactAppearance.ShadowColor = navBar.StandardAppearance.ShadowColor = navBar.ScrollEdgeAppearance.ShadowColor = UIColor.Clear;
				}
 
				UIImage backIndicatorImage = navBar.BackIndicatorImage;
				UIImage backIndicatorTransitionMaskImage = navBar.BackIndicatorTransitionMaskImage;
 
				if (backIndicatorImage != null && backIndicatorImage.Size == SizeF.Empty)
					backIndicatorImage = GetEmptyBackIndicatorImage();
 
				if (backIndicatorTransitionMaskImage != null && backIndicatorTransitionMaskImage.Size == SizeF.Empty)
					backIndicatorTransitionMaskImage = GetEmptyBackIndicatorImage();
 
				navBar.CompactAppearance.SetBackIndicatorImage(backIndicatorImage, backIndicatorTransitionMaskImage);
				navBar.StandardAppearance.SetBackIndicatorImage(backIndicatorImage, backIndicatorTransitionMaskImage);
				navBar.ScrollEdgeAppearance.SetBackIndicatorImage(backIndicatorImage, backIndicatorTransitionMaskImage);
			}
 
			UIImage GetEmptyBackIndicatorImage()
			{
				var rect = RectangleF.Empty;
				var size = rect.Size;
 
				UIGraphics.BeginImageContext(size);
				var context = UIGraphics.GetCurrentContext();
				context?.SetFillColor(1, 1, 1, 0);
				context?.FillRect(rect);
 
				var empty = UIGraphics.GetImageFromCurrentImageContext();
				context?.Dispose();
 
				return empty;
			}
 
			internal void UpdateLeftBarButtonItem(Page pageBeingRemoved = null)
			{
				NavigationRenderer n;
				if (!_navigation.TryGetTarget(out n))
					return;
 
				var currentChild = this.Child;
				var firstPage = n.NavPageController.Pages.FirstOrDefault();
 
 
				if (n._parentFlyoutPage == null)
					return;
 
				if (firstPage != pageBeingRemoved && currentChild != firstPage && NavigationPage.GetHasBackButton(currentChild))
				{
					NavigationItem.LeftBarButtonItem = null;
					return;
				}
 
				SetFlyoutLeftBarButton(this, n._parentFlyoutPage);
			}
 
 
			public bool NeedsTitleViewContainer(Page page) => NavigationPage.GetTitleIconImageSource(page) != null || NavigationPage.GetTitleView(page) != null;
 
			internal void UpdateBackButtonTitle(Page page) => UpdateBackButtonTitle(page.Title, NavigationPage.GetBackButtonTitle(page));
 
			internal void UpdateBackButtonTitle(string title, string backButtonTitle)
			{
				if (!string.IsNullOrWhiteSpace(title))
					NavigationItem.Title = title;
 
				if (backButtonTitle != null)
					// adding a custom event handler to UIBarButtonItem for navigating back seems to be ignored.
					NavigationItem.BackBarButtonItem = new UIBarButtonItem { Title = backButtonTitle, Style = UIBarButtonItemStyle.Plain };
				else
					NavigationItem.BackBarButtonItem = null;
			}
 
			internal void UpdateTitleArea(Page page)
			{
				if (page == null)
					return;
 
				ImageSource titleIcon = NavigationPage.GetTitleIconImageSource(page);
				View titleView = NavigationPage.GetTitleView(page);
				bool needContainer = titleView != null || titleIcon != null;
 
				string backButtonText = NavigationPage.GetBackButtonTitle(page);
				bool isBackButtonTextSet = page.IsSet(NavigationPage.BackButtonTitleProperty);
 
				// on iOS 10 if the user hasn't set the back button text
				// we set it to an empty string so it's consistent with iOS 11
				if (!Forms.IsiOS11OrNewer && !isBackButtonTextSet)
					backButtonText = "";
 
				// First page and we have a flyout detail to contend with
				UpdateLeftBarButtonItem();
				UpdateBackButtonTitle(page.Title, backButtonText);
 
				//var hadTitleView = NavigationItem.TitleView != null;
				ClearTitleViewContainer();
				if (needContainer)
				{
					NavigationRenderer n;
					if (!_navigation.TryGetTarget(out n))
						return;
 
					Container titleViewContainer = new Container(titleView, n.NavigationBar);
 
					UpdateTitleImage(titleViewContainer, titleIcon);
					NavigationItem.TitleView = titleViewContainer;
				}
			}
 
			void UpdateIconColor()
			{
				if (_navigation.TryGetTarget(out NavigationRenderer navigationRenderer))
					navigationRenderer.UpdateBarTextColor();
			}
 
			async void UpdateTitleImage(Container titleViewContainer, ImageSource titleIcon)
			{
				if (titleViewContainer == null)
					return;
 
				if (titleIcon == null || titleIcon.IsEmpty)
				{
					titleViewContainer.Icon = null;
				}
				else
				{
					var image = await titleIcon.GetNativeImageAsync();
					try
					{
						titleViewContainer.Icon = new UIImageView(image);
					}
					catch
					{
						//UIImage ctor throws on file not found if MonoTouch.ObjCRuntime.Class.ThrowOnInitFailure is true;
					}
				}
			}
 
			void ClearTitleViewContainer()
			{
				if (NavigationItem.TitleView != null && NavigationItem.TitleView is Container titleViewContainer)
				{
					titleViewContainer.Dispose();
					titleViewContainer = null;
					NavigationItem.TitleView = null;
				}
			}
 
			void UpdatePrefersStatusBarHidden()
			{
				View.SetNeedsLayout();
				ParentViewController?.View.SetNeedsLayout();
			}
 
			void TrackerOnCollectionChanged(object sender, EventArgs eventArgs)
			{
				UpdateToolbarItems();
			}
 
			void UpdateHasBackButton()
			{
				if (Child == null || NavigationItem.HidesBackButton == !NavigationPage.GetHasBackButton(Child))
					return;
 
				NavigationItem.HidesBackButton = !NavigationPage.GetHasBackButton(Child);
 
				NavigationRenderer n;
				if (!_navigation.TryGetTarget(out n))
					return;
 
				if (!Forms.IsiOS11OrNewer || n._parentFlyoutPage != null)
					UpdateTitleArea(Child);
			}
 
			void UpdateNavigationBarVisibility(bool animated)
			{
				var current = Child;
 
				if (current == null || NavigationController == null)
					return;
 
				var hasNavBar = NavigationPage.GetHasNavigationBar(current);
 
				if (NavigationController.NavigationBarHidden == hasNavBar)
				{
					// prevent bottom content "jumping"
					current.IgnoresContainerArea = !hasNavBar;
					NavigationController.SetNavigationBarHidden(!hasNavBar, animated);
				}
			}
 
			void UpdateToolbarItems()
			{
				if (NavigationItem.RightBarButtonItems != null)
				{
					for (var i = 0; i < NavigationItem.RightBarButtonItems.Length; i++)
						NavigationItem.RightBarButtonItems[i].Dispose();
				}
				if (ToolbarItems != null)
				{
					for (var i = 0; i < ToolbarItems.Length; i++)
						ToolbarItems[i].Dispose();
				}
 
				List<UIBarButtonItem> primaries = null;
				List<UIBarButtonItem> secondaries = null;
				var toolbarItems = _tracker.ToolbarItems;
				foreach (var item in toolbarItems)
				{
					if (item.Order == ToolbarItemOrder.Secondary)
						(secondaries = secondaries ?? new List<UIBarButtonItem>()).Add(item.ToUIBarButtonItem(true));
					else
						(primaries = primaries ?? new List<UIBarButtonItem>()).Add(item.ToUIBarButtonItem());
				}
 
				if (primaries != null)
					primaries.Reverse();
				NavigationItem.SetRightBarButtonItems(primaries == null ? Array.Empty<UIBarButtonItem>() : primaries.ToArray(), false);
				ToolbarItems = secondaries == null ? Array.Empty<UIBarButtonItem>() : secondaries.ToArray();
 
				NavigationRenderer n;
				if (_navigation.TryGetTarget(out n))
					n.UpdateToolBarVisible();
			}
 
			void UpdateLargeTitles()
			{
				var page = Child;
				if (page != null && Forms.IsiOS11OrNewer)
				{
					var largeTitleDisplayMode = page.OnThisPlatform().LargeTitleDisplay();
					switch (largeTitleDisplayMode)
					{
						case LargeTitleDisplayMode.Always:
							NavigationItem.LargeTitleDisplayMode = UINavigationItemLargeTitleDisplayMode.Always;
							break;
						case LargeTitleDisplayMode.Automatic:
							NavigationItem.LargeTitleDisplayMode = UINavigationItemLargeTitleDisplayMode.Automatic;
							break;
						case LargeTitleDisplayMode.Never:
							NavigationItem.LargeTitleDisplayMode = UINavigationItemLargeTitleDisplayMode.Never;
							break;
					}
				}
			}
 
			public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
			{
				IVisualElementRenderer childRenderer;
				if (Child != null && (childRenderer = Platform.GetRenderer(Child)) != null)
					return childRenderer.ViewController.GetSupportedInterfaceOrientations();
				return base.GetSupportedInterfaceOrientations();
			}
 
			public override UIInterfaceOrientation PreferredInterfaceOrientationForPresentation()
			{
				IVisualElementRenderer childRenderer;
				if (Child != null && (childRenderer = Platform.GetRenderer(Child)) != null)
					return childRenderer.ViewController.PreferredInterfaceOrientationForPresentation();
				return base.PreferredInterfaceOrientationForPresentation();
			}
#pragma warning disable CA1416, CA1422 // TODO: ShouldAutorotateToInterfaceOrientation(...) has [UnsupportedOSPlatform("ios6.0")]
			public override bool ShouldAutorotate()
			{
				IVisualElementRenderer childRenderer;
				if (Child != null && (childRenderer = Platform.GetRenderer(Child)) != null)
					return childRenderer.ViewController.ShouldAutorotate();
				return base.ShouldAutorotate();
			}
 
			public override bool ShouldAutorotateToInterfaceOrientation(UIInterfaceOrientation toInterfaceOrientation)
			{
				IVisualElementRenderer childRenderer;
 
				if (Child != null && (childRenderer = Platform.GetRenderer(Child)) != null)
					return childRenderer.ViewController.ShouldAutorotateToInterfaceOrientation(toInterfaceOrientation);
				return base.ShouldAutorotateToInterfaceOrientation(toInterfaceOrientation);
			}
#pragma warning restore CA1416, CA1422
			public override bool ShouldAutomaticallyForwardRotationMethods => true;
 
			public override async void DidMoveToParentViewController(UIViewController parent)
			{
				//we are being removed from the UINavigationPage
				if (parent == null)
				{
					NavigationRenderer navRenderer;
					if (_navigation.TryGetTarget(out navRenderer))
						await navRenderer.UpdateFormsInnerNavigation(Child);
				}
				base.DidMoveToParentViewController(parent);
			}
		}
 
		public override UIViewController ChildViewControllerForStatusBarHidden()
		{
			return Platform.GetRenderer(Current)?.ViewController ??
				(Current.Handler as IPlatformViewHandler)?.ViewController;
		}
 
		public override UIViewController ChildViewControllerForHomeIndicatorAutoHidden =>
			ChildViewControllerForStatusBarHidden();
 
		void IEffectControlProvider.RegisterEffect(Effect effect)
		{
			VisualElementRenderer<VisualElement>.RegisterEffect(effect, View);
		}
 
		internal class FormsNavigationBar : UINavigationBar
		{
			[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
			public FormsNavigationBar() : base()
			{
			}
 
			[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
			public FormsNavigationBar(Foundation.NSCoder coder) : base(coder)
			{
			}
 
			[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
			protected FormsNavigationBar(Foundation.NSObjectFlag t) : base(t)
			{
			}
 
			[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
			protected internal FormsNavigationBar(NativeHandle handle) : base(handle)
			{
			}
 
			[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]
			public FormsNavigationBar(RectangleF frame) : base(frame)
			{
			}
 
			public RectangleF BackButtonFrameSize { get; private set; }
			public UILabel NavBarLabel { get; private set; }
 
			public override void LayoutSubviews()
			{
				if (!Forms.IsiOS11OrNewer)
				{
					for (int i = 0; i < this.Subviews.Length; i++)
					{
						if (Subviews[i] is UIView view)
						{
							if (view.Class.Name == "_UINavigationBarBackIndicatorView")
							{
								if (view.Alpha == 0)
									BackButtonFrameSize = CGRect.Empty;
								else
									BackButtonFrameSize = view.Frame;
 
								break;
							}
							else if (view.Class.Name == "UINavigationItemButtonView")
							{
								if (view.Subviews.Length == 0)
									NavBarLabel = null;
								else if (view.Subviews[0] is UILabel titleLabel)
									NavBarLabel = titleLabel;
							}
						}
					}
				}
 
				base.LayoutSubviews();
			}
		}
 
		class Container : UIView
		{
			View _view;
			FormsNavigationBar _bar;
			IVisualElementRenderer _child;
			UIImageView _icon;
			bool _disposed;
 
			public Container(View view, UINavigationBar bar) : base(bar.Bounds)
			{
				if (Forms.IsiOS11OrNewer)
				{
					TranslatesAutoresizingMaskIntoConstraints = false;
				}
				else
				{
					TranslatesAutoresizingMaskIntoConstraints = true;
					AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth;
				}
 
				_bar = bar as FormsNavigationBar;
				if (view != null)
				{
					_view = view;
					_child = Platform.CreateRenderer(view);
					Platform.SetRenderer(view, _child);
					AddSubview(_child.NativeView);
				}
 
				ClipsToBounds = true;
			}
 
			public override CGSize IntrinsicContentSize => UILayoutFittingExpandedSize;
 
			nfloat IconHeight => _icon?.Frame.Height ?? 0;
			nfloat IconWidth => _icon?.Frame.Width ?? 0;
 
			// Navigation bar will not stretch past these values. Prevent content clipping.
			// iOS11 does this for us automatically, but apparently iOS10 doesn't.
			nfloat ToolbarHeight
			{
				get
				{
					if (Superview?.Bounds.Height > 0)
						return Superview.Bounds.Height;
 
					return (DeviceInfo.Idiom == DeviceIdiom.Phone && DeviceDisplay.MainDisplayInfo.Orientation.IsLandscape()) ? 32 : 44;
				}
			}
 
			public override CGRect Frame
			{
				get => base.Frame;
				set
				{
					if (Superview != null)
					{
						if (!Forms.IsiOS11OrNewer)
						{
							value.Y = Superview.Bounds.Y;
 
							if (_bar != null && String.IsNullOrWhiteSpace(_bar.NavBarLabel?.Text) && _bar.BackButtonFrameSize != RectangleF.Empty)
							{
								var xSpace = _bar.BackButtonFrameSize.Width + (_bar.BackButtonFrameSize.X * 2);
								value.Width = (value.X - xSpace) + value.Width;
								value.X = xSpace;
							}
						};
 
						value.Height = ToolbarHeight;
					}
 
					base.Frame = value;
				}
			}
 
			public UIImageView Icon
			{
				set
				{
					if (_icon != null)
						_icon.RemoveFromSuperview();
 
					_icon = value;
 
					if (_icon != null)
						AddSubview(_icon);
				}
			}
 
			public override SizeF SizeThatFits(SizeF size)
			{
				return new SizeF(size.Width, ToolbarHeight);
			}
 
			public override void LayoutSubviews()
			{
				base.LayoutSubviews();
				if (Frame == CGRect.Empty || Frame.Width >= 10000 || Frame.Height >= 10000)
					return;
 
				nfloat toolbarHeight = ToolbarHeight;
 
				double height = Math.Min(toolbarHeight, Bounds.Height);
 
				if (_icon != null)
					_icon.Frame = new RectangleF(0, 0, IconWidth, Math.Min(toolbarHeight, IconHeight));
 
				if (_child?.Element != null)
				{
					Rect layoutBounds = new Rect(IconWidth, 0, Bounds.Width - IconWidth, height);
					if (_child.Element.Bounds != layoutBounds)
						Layout.LayoutChildIntoBoundingRegion(_child.Element, layoutBounds);
				}
				else if (_icon != null && Superview != null)
				{
					_icon.Center = new PointF(Superview.Frame.Width / 2 - Frame.X, Superview.Frame.Height / 2);
				}
			}
 
			protected override void Dispose(bool disposing)
			{
				if (_disposed)
				{
					return;
				}
 
				_disposed = true;
 
				if (disposing)
				{
 
					if (_child != null)
					{
						_child.Element?.DisposeModalAndChildRenderers();
						_child.NativeView.RemoveFromSuperview();
						_child.Dispose();
						_child = null;
					}
 
					_view = null;
 
					_icon?.Dispose();
					_icon = null;
				}
 
				base.Dispose(disposing);
			}
		}
	}
}