diff options
author | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 13:02:25 -0700 |
---|---|---|
committer | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 16:13:41 -0700 |
commit | 17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch) | |
tree | b5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Platform.iOS/Renderers/NavigationRenderer.cs | |
download | xamarin-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.cs | 918 |
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 |