зеркало из https://github.com/DeGsoft/maui-linux.git
1446 строки
42 KiB
C#
1446 строки
42 KiB
C#
using CoreGraphics;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using ObjCRuntime;
|
|
using UIKit;
|
|
using Xamarin.Forms.Internals;
|
|
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
|
using static Xamarin.Forms.PlatformConfiguration.iOSSpecific.Page;
|
|
using static Xamarin.Forms.PlatformConfiguration.iOSSpecific.NavigationPage;
|
|
using PageUIStatusBarAnimation = Xamarin.Forms.PlatformConfiguration.iOSSpecific.UIStatusBarAnimation;
|
|
using PointF = CoreGraphics.CGPoint;
|
|
using RectangleF = CoreGraphics.CGRect;
|
|
using SizeF = CoreGraphics.CGSize;
|
|
|
|
namespace Xamarin.Forms.Platform.iOS
|
|
{
|
|
public class NavigationRenderer : UINavigationController, IVisualElementRenderer, IEffectControlProvider
|
|
{
|
|
internal const string UpdateToolbarButtons = "Xamarin.UpdateToolbarButtons";
|
|
bool _appeared;
|
|
bool _ignorePopCall;
|
|
bool _loaded;
|
|
MasterDetailPage _parentMasterDetailPage;
|
|
Size _queuedSize;
|
|
UIViewController[] _removeControllers;
|
|
UIToolbar _secondaryToolbar;
|
|
VisualElementTracker _tracker;
|
|
nfloat _navigationBottom = 0;
|
|
bool _hasNavigationBar;
|
|
UIImage _defaultNavBarShadowImage;
|
|
UIImage _defaultNavBarBackImage;
|
|
|
|
public NavigationRenderer() : base(typeof(FormsNavigationBar), null)
|
|
{
|
|
MessagingCenter.Subscribe<IVisualElementRenderer>(this, UpdateToolbarButtons, sender =>
|
|
{
|
|
if (!ViewControllers.Any())
|
|
return;
|
|
var parentingViewController = (ParentingViewController)ViewControllers.Last();
|
|
parentingViewController?.UpdateLeftBarButtonItem();
|
|
});
|
|
}
|
|
|
|
Page Current { get; set; }
|
|
|
|
IPageController PageController => Element as IPageController;
|
|
|
|
NavigationPage NavPage => Element as NavigationPage;
|
|
|
|
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 Rectangle(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+
|
|
public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
|
|
{
|
|
base.DidRotate(fromInterfaceOrientation);
|
|
|
|
View.SetNeedsLayout();
|
|
|
|
var parentingViewController = (ParentingViewController)ViewControllers.Last();
|
|
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 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();
|
|
|
|
UpdateTranslucent();
|
|
|
|
_secondaryToolbar = new SecondaryToolbar { Frame = new RectangleF(0, 0, 320, 44) };
|
|
View.Add(_secondaryToolbar);
|
|
_secondaryToolbar.Hidden = true;
|
|
|
|
FindParentMasterDetail();
|
|
|
|
var navPage = 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.");
|
|
}
|
|
|
|
navPage.PushRequested += OnPushRequested;
|
|
navPage.PopRequested += OnPopRequested;
|
|
navPage.PopToRootRequested += OnPopToRootRequested;
|
|
navPage.RemovePageRequested += OnRemovedPageRequested;
|
|
navPage.InsertPageBeforeRequested += OnInsertPageBeforeRequested;
|
|
|
|
UpdateTint();
|
|
UpdateBarBackgroundColor();
|
|
UpdateBarTextColor();
|
|
UpdateUseLargeTitles();
|
|
UpdateHideNavigationBarSeparator();
|
|
|
|
// If there is already stuff on the stack we need to push it
|
|
navPage.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 (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 = NavPage;
|
|
navPage.PropertyChanged -= HandlePropertyChanged;
|
|
|
|
navPage.PushRequested -= OnPushRequested;
|
|
navPage.PopRequested -= OnPopRequested;
|
|
navPage.PopToRootRequested -= OnPopToRootRequested;
|
|
navPage.RemovePageRequested -= OnRemovedPageRequested;
|
|
navPage.InsertPageBeforeRequested -= OnInsertPageBeforeRequested;
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
if (_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;
|
|
|
|
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;
|
|
poppedViewController = base.PopViewController(animated);
|
|
|
|
actuallyRemoved = (poppedViewController == null) ? true : !await task;
|
|
|
|
poppedViewController?.Dispose();
|
|
|
|
UpdateToolBarVisible();
|
|
return actuallyRemoved;
|
|
}
|
|
|
|
protected virtual async Task<bool> OnPushAsync(Page page, bool animated)
|
|
{
|
|
if (page is MasterDetailPage)
|
|
System.Diagnostics.Trace.WriteLine($"Pushing a {nameof(MasterDetailPage)} 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void FindParentMasterDetail()
|
|
{
|
|
Page page = Element as Page;
|
|
|
|
var parentPages = page.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)
|
|
{
|
|
#pragma warning disable 0618 //retaining legacy call to obsolete code
|
|
if (e.PropertyName == NavigationPage.TintProperty.PropertyName)
|
|
#pragma warning restore 0618
|
|
{
|
|
UpdateTint();
|
|
}
|
|
else if (e.PropertyName == NavigationPage.BarBackgroundColorProperty.PropertyName)
|
|
{
|
|
UpdateBarBackgroundColor();
|
|
}
|
|
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 (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);
|
|
}
|
|
|
|
void UpdateUseLargeTitles()
|
|
{
|
|
if (Forms.IsiOS11OrNewer && NavPage != null)
|
|
NavigationBar.PrefersLargeTitles = NavPage.OnThisPlatform().PrefersLargeTitles();
|
|
}
|
|
|
|
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;
|
|
}
|
|
var parentingViewController = ViewControllers.Last() as ParentingViewController;
|
|
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 == Color.Default ? Color.White : Element.BackgroundColor;
|
|
View.BackgroundColor = color.ToUIColor();
|
|
}
|
|
|
|
void UpdateBarBackgroundColor()
|
|
{
|
|
var barBackgroundColor = NavPage.BarBackgroundColor;
|
|
// Set navigation bar background color
|
|
NavigationBar.BarTintColor = barBackgroundColor == Color.Default
|
|
? UINavigationBar.Appearance.BarTintColor
|
|
: barBackgroundColor.ToUIColor();
|
|
}
|
|
|
|
void UpdateBarTextColor()
|
|
{
|
|
var barTextColor = NavPage.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;
|
|
// TODO: the ternary if statement here will always return false because of the encapsulating if statement.
|
|
// What was the intention?
|
|
titleAttributes.ForegroundColor = barTextColor == Color.Default
|
|
? titleAttributes.ForegroundColor ?? UINavigationBar.Appearance.TintColor
|
|
: barTextColor.ToUIColor();
|
|
NavigationBar.TitleTextAttributes = titleAttributes;
|
|
}
|
|
|
|
if (Forms.IsiOS11OrNewer)
|
|
{
|
|
var globalLargeTitleAttributes = UINavigationBar.Appearance.LargeTitleTextAttributes;
|
|
if (globalLargeTitleAttributes == null)
|
|
NavigationBar.LargeTitleTextAttributes = NavigationBar.TitleTextAttributes;
|
|
}
|
|
|
|
var statusBarColorMode = NavPage.OnThisPlatform().GetStatusBarTextColorMode();
|
|
|
|
// set Tint color (i. e. Back Button arrow and Text)
|
|
NavigationBar.TintColor = barTextColor == Color.Default || statusBarColorMode == StatusBarTextColorMode.DoNotAdjust
|
|
? UINavigationBar.Appearance.TintColor
|
|
: barTextColor.ToUIColor();
|
|
}
|
|
|
|
void SetStatusBarStyle()
|
|
{
|
|
var barTextColor = NavPage.BarTextColor;
|
|
var statusBarColorMode = NavPage.OnThisPlatform().GetStatusBarTextColorMode();
|
|
|
|
if (statusBarColorMode == StatusBarTextColorMode.DoNotAdjust || barTextColor.Luminosity <= 0.5)
|
|
{
|
|
// Use dark text color for status bar
|
|
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.Default;
|
|
}
|
|
else
|
|
{
|
|
// Use light text color for status bar
|
|
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent;
|
|
}
|
|
}
|
|
|
|
|
|
void UpdateTint()
|
|
{
|
|
#pragma warning disable 0618 //retaining legacy call to obsolete code
|
|
var tintColor = NavPage.Tint;
|
|
#pragma warning restore 0618
|
|
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;
|
|
}
|
|
|
|
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;
|
|
_ignorePopCall = true;
|
|
if (Element.Navigation.NavigationStack.Contains(pageBeingRemoved))
|
|
await (NavPage as INavigationPageController)?.RemoveAsyncInner(pageBeingRemoved, false, true);
|
|
_ignorePopCall = false;
|
|
|
|
}
|
|
|
|
internal static async void SetMasterLeftBarButton(UIViewController containerController, MasterDetailPage masterDetailPage)
|
|
{
|
|
if (!masterDetailPage.ShouldShowToolbarButton())
|
|
{
|
|
containerController.NavigationItem.LeftBarButtonItem = null;
|
|
return;
|
|
}
|
|
|
|
EventHandler handler = (o, e) => masterDetailPage.IsPresented = !masterDetailPage.IsPresented;
|
|
|
|
bool shouldUseIcon = masterDetailPage.Master.Icon != null;
|
|
if (shouldUseIcon)
|
|
{
|
|
try
|
|
{
|
|
var source = Internals.Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(masterDetailPage.Master.Icon);
|
|
var icon = await source.LoadImageAsync(masterDetailPage.Master.Icon);
|
|
containerController.NavigationItem.LeftBarButtonItem = new UIBarButtonItem(icon, UIBarButtonItemStyle.Plain, handler);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// Throws Exception otherwise would catch more specific exception type
|
|
shouldUseIcon = false;
|
|
}
|
|
}
|
|
|
|
if (!shouldUseIcon)
|
|
{
|
|
containerController.NavigationItem.LeftBarButtonItem = new UIBarButtonItem(masterDetailPage.Master.Title, UIBarButtonItemStyle.Plain, handler);
|
|
}
|
|
}
|
|
|
|
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;
|
|
ToolbarTracker _tracker = new ToolbarTracker();
|
|
|
|
public ParentingViewController(NavigationRenderer navigation)
|
|
{
|
|
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();
|
|
UpdateLargeTitles();
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
Appearing?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
|
|
public override void ViewDidDisappear(bool animated)
|
|
{
|
|
base.ViewDidDisappear(animated);
|
|
|
|
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)
|
|
{
|
|
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 (disposing)
|
|
{
|
|
Child.SendDisappearing();
|
|
|
|
if (Child != null)
|
|
{
|
|
Child.PropertyChanged -= HandleChildPropertyChanged;
|
|
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();
|
|
else if (e.PropertyName == PrefersStatusBarHiddenProperty.PropertyName)
|
|
UpdatePrefersStatusBarHidden();
|
|
else if (e.PropertyName == LargeTitleDisplayProperty.PropertyName)
|
|
UpdateLargeTitles();
|
|
else if (e.PropertyName == NavigationPage.TitleIconProperty.PropertyName ||
|
|
e.PropertyName == NavigationPage.TitleViewProperty.PropertyName)
|
|
UpdateTitleArea(Child);
|
|
}
|
|
|
|
|
|
internal void UpdateLeftBarButtonItem(Page pageBeingRemoved = null)
|
|
{
|
|
NavigationRenderer n;
|
|
if (!_navigation.TryGetTarget(out n))
|
|
return;
|
|
|
|
var currentChild = this.Child;
|
|
var firstPage = n.NavPage.Pages.FirstOrDefault();
|
|
|
|
|
|
if (n._parentMasterDetailPage == null)
|
|
return;
|
|
|
|
if (firstPage != pageBeingRemoved && currentChild != firstPage && NavigationPage.GetHasBackButton(currentChild))
|
|
{
|
|
NavigationItem.LeftBarButtonItem = null;
|
|
return;
|
|
}
|
|
|
|
SetMasterLeftBarButton(this, n._parentMasterDetailPage);
|
|
}
|
|
|
|
|
|
public bool NeedsTitleViewContainer(Page page) => NavigationPage.GetTitleIcon(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;
|
|
|
|
FileImageSource titleIcon = NavigationPage.GetTitleIcon(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 master 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;
|
|
}
|
|
}
|
|
|
|
async void UpdateTitleImage(Container titleViewContainer, FileImageSource titleIcon)
|
|
{
|
|
if (titleViewContainer == null)
|
|
return;
|
|
|
|
if (string.IsNullOrWhiteSpace(titleIcon))
|
|
{
|
|
titleViewContainer.Icon = null;
|
|
}
|
|
else
|
|
{
|
|
var source = Internals.Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(titleIcon);
|
|
var image = await source.LoadImageAsync(titleIcon);
|
|
|
|
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._parentMasterDetailPage != 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;
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 (UIViewController)Platform.GetRenderer(Current);
|
|
}
|
|
|
|
void IEffectControlProvider.RegisterEffect(Effect effect)
|
|
{
|
|
VisualElementRenderer<VisualElement>.RegisterEffect(effect, View);
|
|
}
|
|
|
|
internal class FormsNavigationBar : UINavigationBar
|
|
{
|
|
public FormsNavigationBar() : base()
|
|
{
|
|
}
|
|
|
|
public FormsNavigationBar(Foundation.NSCoder coder) : base(coder)
|
|
{
|
|
}
|
|
|
|
protected FormsNavigationBar(Foundation.NSObjectFlag t) : base(t)
|
|
{
|
|
}
|
|
|
|
protected internal FormsNavigationBar(IntPtr handle) : base(handle)
|
|
{
|
|
}
|
|
|
|
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;
|
|
|
|
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 (Device.Idiom == TargetIdiom.Phone && Device.Info.CurrentOrientation.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)
|
|
{
|
|
var layoutBounds = new Rectangle(IconWidth, 0, Bounds.Width - IconWidth, height);
|
|
if (_child.Element.Bounds != layoutBounds)
|
|
Layout.LayoutChildIntoBoundingRegion(_child.Element, layoutBounds);
|
|
}
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (disposing)
|
|
{
|
|
|
|
if (_child != null)
|
|
{
|
|
_child.Element?.DisposeModalAndChildRenderers();
|
|
_child.NativeView.RemoveFromSuperview();
|
|
_child.Dispose();
|
|
_child = null;
|
|
}
|
|
|
|
_view = null;
|
|
|
|
_icon?.Dispose();
|
|
_icon = null;
|
|
}
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
}
|
|
}
|