summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs
diff options
context:
space:
mode:
authorJason Smith <jason.smith@xamarin.com>2016-03-22 13:02:25 -0700
committerJason Smith <jason.smith@xamarin.com>2016-03-22 16:13:41 -0700
commit17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch)
treeb5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs
downloadxamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip
Initial import
Diffstat (limited to 'Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs918
1 files changed, 918 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs
new file mode 100644
index 00000000..63330564
--- /dev/null
+++ b/Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs
@@ -0,0 +1,918 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+#if __UNIFIED__
+using UIKit;
+using CoreGraphics;
+#else
+using MonoTouch.UIKit;
+using MonoTouch.CoreGraphics;
+#endif
+#if __UNIFIED__
+using RectangleF = CoreGraphics.CGRect;
+using SizeF = CoreGraphics.CGSize;
+using PointF = CoreGraphics.CGPoint;
+
+#else
+using nfloat=System.Single;
+using nint=System.Int32;
+using nuint=System.UInt32;
+#endif
+
+namespace Xamarin.Forms.Platform.iOS
+{
+ public class NavigationRenderer : UINavigationController, IVisualElementRenderer
+ {
+ internal const string UpdateToolbarButtons = "Xamarin.UpdateToolbarButtons";
+ bool _appeared;
+ bool _ignorePopCall;
+ bool _loaded;
+ MasterDetailPage _parentMasterDetailPage;
+ Size _queuedSize;
+ UIViewController[] _removeControllers;
+ UIToolbar _secondaryToolbar;
+
+ VisualElementTracker _tracker;
+
+ public NavigationRenderer()
+ {
+ MessagingCenter.Subscribe<IVisualElementRenderer>(this, UpdateToolbarButtons, sender =>
+ {
+ if (!ViewControllers.Any())
+ return;
+ var parentingViewController = (ParentingViewController)ViewControllers.Last();
+ UpdateLeftBarButtonItem(parentingViewController);
+ });
+ }
+
+ Page Current { get; set; }
+
+ 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 = (NavigationPage)element;
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+
+ if (element != null)
+ element.SendViewInitialized(NativeView);
+ }
+
+ public void SetElementSize(Size size)
+ {
+ if (_loaded)
+ Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+ else
+ _queuedSize = size;
+ }
+
+ public UIViewController ViewController
+ {
+ get { return this; }
+ }
+
+ public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
+ {
+ base.DidRotate(fromInterfaceOrientation);
+
+ View.SetNeedsLayout();
+
+ var parentingViewController = (ParentingViewController)ViewControllers.Last();
+ UpdateLeftBarButtonItem(parentingViewController);
+ }
+
+ 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);
+ }
+
+#if __UNIFIED__
+ public override UIViewController PopViewController(bool animated)
+#else
+ public override UIViewController PopViewControllerAnimated (bool animated)
+ #endif
+ {
+ RemoveViewControllers(animated);
+#if __UNIFIED__
+ return base.PopViewController(animated);
+#else
+ return base.PopViewControllerAnimated (animated);
+ #endif
+ }
+
+ public Task<bool> PushPageAsync(Page page, bool animated = true)
+ {
+ return OnPushAsync(page, animated);
+ }
+
+ public override void ViewDidAppear(bool animated)
+ {
+ if (!_appeared)
+ {
+ _appeared = true;
+ ((NavigationPage)Element)?.SendAppearing();
+ }
+
+ base.ViewDidAppear(animated);
+
+ View.SetNeedsLayout();
+ }
+
+ public override void ViewDidDisappear(bool animated)
+ {
+ base.ViewDidDisappear(animated);
+
+ if (!_appeared || Element == null)
+ return;
+
+ _appeared = false;
+ ((NavigationPage)Element).SendDisappearing();
+ }
+
+ public override void ViewDidLayoutSubviews()
+ {
+ base.ViewDidLayoutSubviews();
+ UpdateToolBarVisible();
+
+ var navBarFrame = NavigationBar.Frame;
+
+ var toolbar = _secondaryToolbar;
+ // Use 0 if the NavBar is hidden or will be hidden
+ var toolbarY = NavigationBarHidden || !NavigationPage.GetHasNavigationBar(Current) ? 0 : navBarFrame.Bottom;
+ 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;
+ ((NavigationPage)Element).ContainerArea = new Rectangle(0, toolbar.Hidden ? 0 : toolbar.Frame.Height, modelSize.Width, modelSize.Height - trueBottom);
+
+ if (!_queuedSize.IsZero)
+ {
+ Element.Layout(new Rectangle(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();
+
+ if (Forms.IsiOS7OrNewer)
+ NavigationBar.Translucent = false;
+ else
+ WantsFullScreenLayout = false;
+
+ _secondaryToolbar = new SecondaryToolbar { Frame = new RectangleF(0, 0, 320, 44) };
+ View.Add(_secondaryToolbar);
+ _secondaryToolbar.Hidden = true;
+
+ FindParentMasterDetail();
+
+ var navPage = (NavigationPage)Element;
+
+ 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.");
+ }
+
+ navPage.PushRequested += OnPushRequested;
+ navPage.PopRequested += OnPopRequested;
+ navPage.PopToRootRequested += OnPopToRootRequested;
+ navPage.RemovePageRequested += OnRemovedPageRequested;
+ navPage.InsertPageBeforeRequested += OnInsertPageBeforeRequested;
+
+ UpdateTint();
+ UpdateBarBackgroundColor();
+ UpdateBarTextColor();
+
+ // If there is already stuff on the stack we need to push it
+ navPage.StackCopy.Reverse().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 (disposing)
+ {
+ MessagingCenter.Unsubscribe<IVisualElementRenderer>(this, UpdateToolbarButtons);
+
+ foreach (var childViewController in ViewControllers)
+ childViewController.Dispose();
+
+ if (_tracker != null)
+ _tracker.Dispose();
+
+ _secondaryToolbar.RemoveFromSuperview();
+ _secondaryToolbar.Dispose();
+ _secondaryToolbar = null;
+
+ _parentMasterDetailPage = null;
+ Current = null; // unhooks events
+
+ var navPage = (NavigationPage)Element;
+ navPage.PropertyChanged -= HandlePropertyChanged;
+ navPage.PushRequested -= OnPushRequested;
+ navPage.PopRequested -= OnPopRequested;
+ navPage.PopToRootRequested -= OnPopToRootRequested;
+ navPage.RemovePageRequested -= OnRemovedPageRequested;
+ navPage.InsertPageBeforeRequested -= OnInsertPageBeforeRequested;
+ }
+
+ base.Dispose(disposing);
+ if (_appeared)
+ {
+ ((Page)Element).SendDisappearing();
+
+ _appeared = false;
+ }
+ }
+
+ protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ var changed = ElementChanged;
+ if (changed != null)
+ changed(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;
+
+ var actuallyRemoved = 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;
+#if __UNIFIED__
+ poppedViewController = base.PopViewController(animated);
+#else
+ poppedViewController = base.PopViewControllerAnimated (animated);
+ #endif
+
+ if (poppedViewController == null)
+ {
+ // this happens only when the user does something REALLY dumb like pop right after putting the page as visible.
+ poppedViewController = TopViewController;
+ var newControllers = ViewControllers.Remove(poppedViewController);
+ ViewControllers = newControllers;
+ actuallyRemoved = true;
+ }
+ else
+ actuallyRemoved = !await task;
+
+ poppedViewController.Dispose();
+
+ UpdateToolBarVisible();
+ return actuallyRemoved;
+ }
+
+ protected virtual async Task<bool> OnPushAsync(Page page, bool animated)
+ {
+ var pack = CreateViewControllerForPage(page);
+ var task = GetAppearedOrDisappearedTask(page);
+
+ PushViewController(pack, animated);
+
+ var shown = await task;
+ UpdateToolBarVisible();
+ return shown;
+ }
+
+ 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 };
+ if (!string.IsNullOrWhiteSpace(page.Title))
+ pack.NavigationItem.Title = page.Title;
+
+ // First page and we have a master detail to contend with
+ UpdateLeftBarButtonItem(pack);
+
+ //var pack = Platform.GetRenderer (view).ViewController;
+
+ var titleIcon = NavigationPage.GetTitleIcon(page);
+ if (!string.IsNullOrEmpty(titleIcon))
+ {
+ try
+ {
+ //UIImage ctor throws on file not found if MonoTouch.ObjCRuntime.Class.ThrowOnInitFailure is true;
+ pack.NavigationItem.TitleView = new UIImageView(new UIImage(titleIcon));
+ }
+ catch
+ {
+ }
+ }
+
+ var titleText = NavigationPage.GetBackButtonTitle(page);
+ if (titleText != null)
+ {
+ pack.NavigationItem.BackBarButtonItem = new UIBarButtonItem(titleText, UIBarButtonItemStyle.Plain, async (o, e) => await PopViewAsync(page));
+ }
+
+ var pageRenderer = Platform.GetRenderer(page);
+ pack.View.AddSubview(pageRenderer.ViewController.View);
+ pack.AddChildViewController(pageRenderer.ViewController);
+ pageRenderer.ViewController.DidMoveToParentViewController(pack);
+
+ return pack;
+ }
+
+ void FindParentMasterDetail()
+ {
+ var parentPages = ((Page)Element).GetParentPages();
+ var masterDetail = parentPages.OfType<MasterDetailPage>().FirstOrDefault();
+
+ if (masterDetail != null && parentPages.Append((Page)Element).Contains(masterDetail.Detail))
+ _parentMasterDetailPage = masterDetail;
+ }
+
+ 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.TintProperty.PropertyName)
+ UpdateTint();
+ if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
+ UpdateBarBackgroundColor();
+ else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName)
+ UpdateBarTextColor();
+ else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ UpdateBackgroundColor();
+ else if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
+ Current = ((NavigationPage)Element).CurrentPage;
+ }
+
+ 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)
+ {
+ 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 protecs 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;
+ }
+ }
+
+ void RemoveViewControllers(bool animated)
+ {
+ var controller = TopViewController as ParentingViewController;
+ if (controller == null || controller.Child == null)
+ return;
+
+ // Gesture in progress, lets not be proactive and just wait for it to finish
+ var count = ViewControllers.Length;
+ var task = GetAppearedOrDisappearedTask(controller.Child);
+ task.ContinueWith(async 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;
+ _ignorePopCall = true;
+ // because iOS will just chain multiple animations together...
+ var removed = count - ViewControllers.Length;
+ for (var i = 0; i < removed; i++)
+ {
+ // lets just pop these suckers off, do not await, the true is there to make this fast
+ await ((NavigationPage)Element).PopAsyncInner(animated, true);
+ }
+ // because we skip the normal pop process we need to dispose ourselves
+ controller.Dispose();
+ _ignorePopCall = false;
+ }, TaskScheduler.FromCurrentSynchronizationContext());
+ }
+
+ void UpdateBackgroundColor()
+ {
+ var color = Element.BackgroundColor == Color.Default ? Color.White : Element.BackgroundColor;
+ View.BackgroundColor = color.ToUIColor();
+ }
+
+ void UpdateBarBackgroundColor()
+ {
+ var barBackgroundColor = ((NavigationPage)Element).BarBackgroundColor;
+ // Set navigation bar background color
+ if (Forms.IsiOS7OrNewer)
+ {
+ NavigationBar.BarTintColor = barBackgroundColor == Color.Default ? UINavigationBar.Appearance.BarTintColor : barBackgroundColor.ToUIColor();
+ }
+ else
+ {
+ NavigationBar.TintColor = barBackgroundColor == Color.Default ? UINavigationBar.Appearance.TintColor : barBackgroundColor.ToUIColor();
+ }
+ }
+
+ void UpdateBarTextColor()
+ {
+ var barTextColor = ((NavigationPage)Element).BarTextColor;
+
+ var globalAttributes = UINavigationBar.Appearance.GetTitleTextAttributes();
+
+ if (barTextColor == Color.Default)
+ {
+ if (NavigationBar.TitleTextAttributes != null)
+ {
+ var attributes = new UIStringAttributes();
+ attributes.ForegroundColor = globalAttributes.TextColor;
+ attributes.Font = globalAttributes.Font;
+ NavigationBar.TitleTextAttributes = attributes;
+ }
+ }
+ else
+ {
+ var titleAttributes = new UIStringAttributes();
+ titleAttributes.Font = globalAttributes.Font;
+ titleAttributes.ForegroundColor = barTextColor == Color.Default ? titleAttributes.ForegroundColor ?? UINavigationBar.Appearance.TintColor : barTextColor.ToUIColor();
+ NavigationBar.TitleTextAttributes = titleAttributes;
+ }
+
+ // set Tint color (i. e. Back Button arrow and Text)
+ if (Forms.IsiOS7OrNewer)
+ {
+ NavigationBar.TintColor = barTextColor == Color.Default ? UINavigationBar.Appearance.TintColor : barTextColor.ToUIColor();
+ }
+
+ if (barTextColor.Luminosity > 0.5)
+ {
+ // Use light text color for status bar
+ UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent;
+ }
+ else
+ {
+ // Use dark text color for status bar
+ UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.Default;
+ }
+ }
+
+ void UpdateLeftBarButtonItem(ParentingViewController containerController)
+ {
+ var currentChild = containerController.Child;
+ var firstPage = ((NavigationPage)Element).StackCopy.LastOrDefault();
+ if ((currentChild != firstPage && NavigationPage.GetHasBackButton(currentChild)) || _parentMasterDetailPage == null)
+ return;
+
+ if (!_parentMasterDetailPage.ShouldShowToolbarButton())
+ {
+ containerController.NavigationItem.LeftBarButtonItem = null;
+ return;
+ }
+
+ var shouldUseIcon = _parentMasterDetailPage.Master.Icon != null;
+ if (shouldUseIcon)
+ {
+ try
+ {
+ containerController.NavigationItem.LeftBarButtonItem = new UIBarButtonItem(new UIImage(_parentMasterDetailPage.Master.Icon), UIBarButtonItemStyle.Plain,
+ (o, e) => _parentMasterDetailPage.IsPresented = !_parentMasterDetailPage.IsPresented);
+ }
+ catch (Exception)
+ {
+ // Throws Exception otherwise would catch more specific exception type
+ shouldUseIcon = false;
+ }
+ }
+
+ if (!shouldUseIcon)
+ {
+ containerController.NavigationItem.LeftBarButtonItem = new UIBarButtonItem(_parentMasterDetailPage.Master.Title, UIBarButtonItemStyle.Plain,
+ (o, e) => _parentMasterDetailPage.IsPresented = !_parentMasterDetailPage.IsPresented);
+ }
+ }
+
+ void UpdateTint()
+ {
+ var tintColor = ((NavigationPage)Element).Tint;
+
+ if (Forms.IsiOS7OrNewer)
+ {
+ NavigationBar.BarTintColor = tintColor == Color.Default ? UINavigationBar.Appearance.BarTintColor : tintColor.ToUIColor();
+ if (tintColor == Color.Default)
+ NavigationBar.TintColor = UINavigationBar.Appearance.TintColor;
+ else
+ NavigationBar.TintColor = tintColor.Luminosity > 0.5 ? UIColor.Black : UIColor.White;
+ }
+ else
+ NavigationBar.TintColor = tintColor == Color.Default ? null : tintColor.ToUIColor();
+ }
+
+ 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;
+ }
+ }
+
+ 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;
+ nfloat padding = 11f;
+ var itemWidth = (Bounds.Width - padding) / Items.Length - padding;
+ var x = padding;
+ var itemH = Bounds.Height - 10;
+ foreach (var item in Items)
+ {
+ var frame = new RectangleF(x, 5, itemWidth, itemH);
+ item.CustomView.Frame = frame;
+ x += itemWidth + padding;
+ }
+ x = itemWidth + padding * 1.5f;
+ var y = Bounds.GetMidY();
+ foreach (var l in _lines)
+ {
+ l.Center = new PointF(x, y);
+ x += itemWidth + 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;
+ ToolbarTracker _tracker = new ToolbarTracker();
+
+ public ParentingViewController(NavigationRenderer navigation)
+ {
+ if (Forms.IsiOS7OrNewer)
+ AutomaticallyAdjustsScrollViewInsets = false;
+
+ _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();
+ }
+ }
+
+ public event EventHandler Appearing;
+
+ public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
+ {
+ base.DidRotate(fromInterfaceOrientation);
+
+ View.SetNeedsLayout();
+ }
+
+ public event EventHandler Disappearing;
+
+ public override void ViewDidAppear(bool animated)
+ {
+ base.ViewDidAppear(animated);
+
+ var handler = Appearing;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ public override void ViewDidDisappear(bool animated)
+ {
+ base.ViewDidDisappear(animated);
+
+ var handler = Disappearing;
+ if (handler != null)
+ handler(this, EventArgs.Empty);
+ }
+
+ 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)
+ {
+ UpdateNavigationBarVisibility(animated);
+ base.ViewWillAppear(animated);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ Child.SendDisappearing();
+ Child = null;
+ _tracker.Target = null;
+ _tracker.CollectionChanged -= TrackerOnCollectionChanged;
+ _tracker = 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();
+ }
+
+ void TrackerOnCollectionChanged(object sender, EventArgs eventArgs)
+ {
+ UpdateToolbarItems();
+ }
+
+ void UpdateHasBackButton()
+ {
+ if (Child == null)
+ return;
+
+ NavigationItem.HidesBackButton = !NavigationPage.GetHasBackButton(Child);
+ }
+
+ void UpdateNavigationBarVisibility(bool animated)
+ {
+ var current = Child;
+
+ if (current == null || NavigationController == null)
+ return;
+
+ var hasNavBar = NavigationPage.GetHasNavigationBar(current);
+
+ if (NavigationController.NavigationBarHidden == 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;
+ foreach (var item in _tracker.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 ? new UIBarButtonItem[0] : primaries.ToArray(), false);
+ ToolbarItems = secondaries == null ? new UIBarButtonItem[0] : secondaries.ToArray();
+
+ NavigationRenderer n;
+ if (_navigation.TryGetTarget(out n))
+ n.UpdateToolBarVisible();
+ }
+ }
+ }
+} \ No newline at end of file