Skip to content

Commit f5654fe

Browse files
committed
Fire Navigation Events on Root Window Page
1 parent 5054b26 commit f5654fe

File tree

13 files changed

+336
-52
lines changed

13 files changed

+336
-52
lines changed

src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@
2222
using PointF = CoreGraphics.CGPoint;
2323
using RectangleF = CoreGraphics.CGRect;
2424
using SizeF = CoreGraphics.CGSize;
25+
using System.Diagnostics.CodeAnalysis;
2526

2627
namespace Microsoft.Maui.Controls.Handlers.Compatibility
2728
{
28-
public class NavigationRenderer : UINavigationController, INavigationViewHandler, IPlatformViewHandler
29+
public class NavigationRenderer : UINavigationController, INavigationViewHandler, IPlatformViewHandler, IUIViewLifeCycleEvents
2930
{
3031
internal const string UpdateToolbarButtons = "Xamarin.UpdateToolbarButtons";
3132
bool _appeared;
@@ -155,6 +156,11 @@ public override void ViewDidAppear(bool animated)
155156
base.ViewDidAppear(animated);
156157

157158
View.SetNeedsLayout();
159+
160+
if(View.Window is not null)
161+
{
162+
_movedToWindow?.Invoke(this, EventArgs.Empty);
163+
}
158164
}
159165

160166
public override void ViewWillAppear(bool animated)
@@ -175,6 +181,11 @@ public override void ViewDidDisappear(bool animated)
175181

176182
_appeared = false;
177183
PageController.SendDisappearing();
184+
185+
if(View.Window is null)
186+
{
187+
_movedToWindow?.Invoke(this, EventArgs.Empty);
188+
}
178189
}
179190

180191
public override void ViewWillLayoutSubviews()
@@ -1775,6 +1786,16 @@ public override UIViewController ChildViewControllerForStatusBarHidden()
17751786
return (Current.Handler as IPlatformViewHandler)?.ViewController;
17761787
}
17771788

1789+
1790+
1791+
[UnconditionalSuppressMessage("Memory", "MEM0002", Justification = IUIViewLifeCycleEvents.UnconditionalSuppressMessage)]
1792+
EventHandler _movedToWindow;
1793+
event EventHandler IUIViewLifeCycleEvents.MovedToWindow
1794+
{
1795+
add => _movedToWindow += value;
1796+
remove => _movedToWindow -= value;
1797+
}
1798+
17781799
public override UIViewController ChildViewControllerForHomeIndicatorAutoHidden =>
17791800
ChildViewControllerForStatusBarHidden();
17801801

src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutRenderer.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#nullable disable
22
using System;
33
using System.ComponentModel;
4+
using System.Diagnostics.CodeAnalysis;
45
using CoreAnimation;
56
using CoreGraphics;
67
using Foundation;
@@ -11,7 +12,7 @@
1112

1213
namespace Microsoft.Maui.Controls.Platform.Compatibility
1314
{
14-
public class ShellFlyoutRenderer : UIViewController, IShellFlyoutRenderer, IFlyoutBehaviorObserver, IAppearanceObserver
15+
public class ShellFlyoutRenderer : UIViewController, IShellFlyoutRenderer, IFlyoutBehaviorObserver, IAppearanceObserver, IUIViewLifeCycleEvents
1516
{
1617
#region IAppearanceObserver
1718

@@ -219,11 +220,40 @@ public override void ViewDidLayoutSubviews()
219220
LayoutSidebar(false);
220221
}
221222

223+
224+
[UnconditionalSuppressMessage("Memory", "MEM0002", Justification = IUIViewLifeCycleEvents.UnconditionalSuppressMessage)]
225+
EventHandler _movedToWindow;
226+
event EventHandler IUIViewLifeCycleEvents.MovedToWindow
227+
{
228+
add => _movedToWindow += value;
229+
remove => _movedToWindow -= value;
230+
}
231+
222232
public override void ViewWillAppear(bool animated)
223233
{
224234
UpdateFlowDirection();
225235
base.ViewWillAppear(animated);
226236
}
237+
238+
public override void ViewDidDisappear(bool animated)
239+
{
240+
base.ViewDidDisappear(animated);
241+
242+
if(View.Window is null)
243+
{
244+
_movedToWindow?.Invoke(this, EventArgs.Empty);
245+
}
246+
}
247+
248+
public override void ViewDidAppear(bool animated)
249+
{
250+
base.ViewDidAppear(animated);
251+
252+
if(View.Window is not null)
253+
{
254+
_movedToWindow?.Invoke(this, EventArgs.Empty);
255+
}
256+
}
227257

228258
public override void ViewDidLoad()
229259
{

src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Collections.Specialized;
55
using System.ComponentModel;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Threading.Tasks;
78
using System.Xml.Linq;
89
using Microsoft.Maui.Controls.Internals;
@@ -16,7 +17,7 @@
1617

1718
namespace Microsoft.Maui.Controls.Handlers.Compatibility
1819
{
19-
public class TabbedRenderer : UITabBarController, IPlatformViewHandler
20+
public class TabbedRenderer : UITabBarController, IPlatformViewHandler, IUIViewLifeCycleEvents
2021
{
2122
bool _barBackgroundColorWasSet;
2223
bool _barTextColorWasSet;
@@ -115,12 +116,22 @@ public override void ViewDidAppear(bool animated)
115116
{
116117
Page?.SendAppearing();
117118
base.ViewDidAppear(animated);
119+
120+
if(View.Window is not null)
121+
{
122+
_movedToWindow?.Invoke(this, EventArgs.Empty);
123+
}
118124
}
119125

120126
public override void ViewDidDisappear(bool animated)
121127
{
122128
base.ViewDidDisappear(animated);
123129
Page?.SendDisappearing();
130+
131+
if(View.Window is null)
132+
{
133+
_movedToWindow?.Invoke(this, EventArgs.Empty);
134+
}
124135
}
125136

126137
public override void ViewDidLayoutSubviews()
@@ -554,6 +565,14 @@ void UpdateiOS15TabBarAppearance()
554565
tabbed.IsSet(TabbedPage.BarTextColorProperty) ? tabbed.BarTextColor : null);
555566
}
556567

568+
[UnconditionalSuppressMessage("Memory", "MEM0002", Justification = IUIViewLifeCycleEvents.UnconditionalSuppressMessage)]
569+
EventHandler _movedToWindow;
570+
event EventHandler IUIViewLifeCycleEvents.MovedToWindow
571+
{
572+
add => _movedToWindow += value;
573+
remove => _movedToWindow -= value;
574+
}
575+
557576
#region IPlatformViewHandler
558577
bool IViewHandler.HasContainer { get => false; set { } }
559578

src/Controls/src/Core/Page/Page.cs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,5 +963,123 @@ private protected override string GetDebuggerDisplay()
963963
var debugText = DebuggerDisplayHelpers.GetDebugText(nameof(BindingContext), BindingContext, nameof(Title), Title);
964964
return $"{this.GetType().FullName}: {debugText}";
965965
}
966+
967+
#nullable enable
968+
IDisposable? _handleNavigatedEventsForRootPage = null!;
969+
970+
internal void WireUpAsOutgoingPage(Page? outgoingPage, NavigationType navigationType)
971+
{
972+
_handleNavigatedEventsForRootPage?.Dispose();
973+
_handleNavigatedEventsForRootPage = null;
974+
975+
if (HasNavigatedTo)
976+
{
977+
SendNavigatingFrom(new NavigatingFromEventArgs());
978+
}
979+
980+
if (IsLoaded)
981+
{
982+
_handleNavigatedEventsForRootPage =
983+
this.OnUnloaded(() =>
984+
{
985+
if (HasNavigatedTo)
986+
{
987+
SendNavigatedFrom(new NavigatedFromEventArgs(outgoingPage, navigationType));
988+
}
989+
990+
outgoingPage?.DisconnectHandlers();
991+
_handleNavigatedEventsForRootPage?.Dispose();
992+
_handleNavigatedEventsForRootPage = null;
993+
outgoingPage = null;
994+
});
995+
}
996+
else
997+
{
998+
if (HasNavigatedTo)
999+
{
1000+
SendNavigatedFrom(new NavigatedFromEventArgs(outgoingPage, navigationType));
1001+
}
1002+
1003+
outgoingPage?.DisconnectHandlers();
1004+
}
1005+
}
1006+
1007+
internal void WireUpAsIncomingPage(Page? oldPage)
1008+
{
1009+
oldPage?.WireUpAsOutgoingPage(this, NavigationType.PageSwap);
1010+
_handleNavigatedEventsForRootPage?.Dispose();
1011+
_handleNavigatedEventsForRootPage = null;
1012+
1013+
IDisposable? newPageUnloaded = null;
1014+
IDisposable? newPageLoaded = null;
1015+
1016+
var previousPage = oldPage;
1017+
1018+
if (!IsLoaded)
1019+
{
1020+
EventHandler onLoaded = (object? sender, EventArgs args) =>
1021+
{
1022+
if (sender is Page page)
1023+
{
1024+
if(!page.HasNavigatedTo)
1025+
{
1026+
page.SendNavigatedTo(new NavigatedToEventArgs(previousPage));
1027+
}
1028+
1029+
previousPage = null;
1030+
1031+
// rewire up the events to watch for unloaded
1032+
WireUpAsIncomingPage(null);
1033+
}
1034+
};
1035+
Loaded += onLoaded;
1036+
newPageLoaded = new ActionDisposable(() =>
1037+
{
1038+
Loaded -= onLoaded;
1039+
newPageLoaded = null;
1040+
});
1041+
}
1042+
else
1043+
{
1044+
if(!HasNavigatedTo)
1045+
{
1046+
SendNavigatedTo(new NavigatedToEventArgs(oldPage));
1047+
}
1048+
1049+
// If the Window.Page gets unloaded we'll fire the Navigated events
1050+
// The idea of Navigated is that we've navigated to the page and it's ready to be interacted with
1051+
// so if the page is unloaded we have to signify this to the user.
1052+
EventHandler onUnloaded = (object? sender, EventArgs args) =>
1053+
{
1054+
if (sender is Page page && page.HasNavigatedTo)
1055+
{
1056+
(sender as Page)?.SendNavigatingFrom(new NavigatingFromEventArgs());
1057+
(sender as Page)?.SendNavigatedFrom(new NavigatedFromEventArgs(null, NavigationType.PageSwap));
1058+
}
1059+
1060+
// If I'm still part of the root window that means I might come around again
1061+
if (this.Window is not null)
1062+
{
1063+
// rewire up the events to watch for loaded
1064+
WireUpAsIncomingPage(null);
1065+
}
1066+
};
1067+
1068+
Unloaded += onUnloaded;
1069+
newPageUnloaded = new ActionDisposable(() =>
1070+
{
1071+
Unloaded -= onUnloaded;
1072+
newPageUnloaded = null;
1073+
});
1074+
}
1075+
1076+
_handleNavigatedEventsForRootPage = new ActionDisposable(() =>
1077+
{
1078+
newPageUnloaded?.Dispose();
1079+
newPageLoaded?.Dispose();
1080+
newPageUnloaded = null;
1081+
newPageLoaded = null;
1082+
});
1083+
}
9661084
}
9671085
}

src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,8 @@ virtual Microsoft.Maui.Controls.Handlers.Items2.ItemsViewController2<TItemsView>
342342
virtual Microsoft.Maui.Controls.Handlers.Items2.ItemsViewController2<TItemsView>.UpdateVisibility() -> void
343343
virtual Microsoft.Maui.Controls.Handlers.Items2.ItemsViewDelegator2<TItemsView, TViewController>.GetVisibleItemsIndex() -> (bool VisibleItems, int First, int Center, int Last)
344344
virtual Microsoft.Maui.Controls.Handlers.Items2.ItemsViewHandler2<TItemsView>.UpdateLayout() -> void
345+
override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewDidAppear(bool animated) -> void
346+
override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewDidDisappear(bool animated) -> void
345347
~Microsoft.Maui.Controls.Internals.TypedBindingBase.UpdateSourceEventName.set -> void
346348
*REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.SetNeedsLayout() -> void
347349
*REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.MovedToWindow() -> void

src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ virtual Microsoft.Maui.Controls.Handlers.Items2.ItemsViewController2<TItemsView>
343343
virtual Microsoft.Maui.Controls.Handlers.Items2.ItemsViewController2<TItemsView>.UpdateVisibility() -> void
344344
virtual Microsoft.Maui.Controls.Handlers.Items2.ItemsViewDelegator2<TItemsView, TViewController>.GetVisibleItemsIndex() -> (bool VisibleItems, int First, int Center, int Last)
345345
virtual Microsoft.Maui.Controls.Handlers.Items2.ItemsViewHandler2<TItemsView>.UpdateLayout() -> void
346+
override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewDidAppear(bool animated) -> void
347+
override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.ViewDidDisappear(bool animated) -> void
346348
*REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.SetNeedsLayout() -> void
347-
*REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer.MovedToWindow() -> void
348349
override Microsoft.Maui.Controls.Handlers.Compatibility.VisualElementRenderer<TElement>.MovedToWindow() -> void

src/Controls/src/Core/Window/Window.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -591,9 +591,9 @@ Page is IFlowDirectionController controller &&
591591
private protected override void OnHandlerChangingCore(HandlerChangingEventArgs args)
592592
{
593593
base.OnHandlerChangingCore(args);
594-
var mauiContext = args?.NewHandler?.MauiContext;
594+
var mauiContext = args.NewHandler?.MauiContext;
595595

596-
if (FlowDirection == FlowDirection.MatchParent && mauiContext != null)
596+
if (FlowDirection == FlowDirection.MatchParent && mauiContext is not null)
597597
{
598598
var flowDirection = AppInfo.Current.RequestedLayoutDirection.ToFlowDirection();
599599
FlowController.EffectiveFlowDirection = flowDirection.ToEffectiveFlowDirection(true);
@@ -603,12 +603,15 @@ private protected override void OnHandlerChangingCore(HandlerChangingEventArgs a
603603
static void OnPageChanging(BindableObject bindable, object oldValue, object newValue)
604604
{
605605
if (oldValue is Page oldPage)
606+
{
606607
oldPage.SendDisappearing();
608+
oldPage.WireUpAsOutgoingPage(newValue as Page, NavigationType.PageSwap);
609+
}
607610
}
608611

609612
void OnPageChanged(Page? oldPage, Page? newPage)
610613
{
611-
if (oldPage != null)
614+
if (oldPage is not null)
612615
{
613616
_menuBarTracker.Target = null;
614617
_visualChildren.Remove(oldPage);
@@ -618,42 +621,39 @@ void OnPageChanged(Page? oldPage, Page? newPage)
618621
}
619622

620623
if (oldPage is Shell shell)
624+
{
621625
shell.PropertyChanged -= ShellPropertyChanged;
626+
}
622627

623-
if (newPage != null)
628+
if (newPage is not null)
624629
{
625630
_visualChildren.Add(newPage);
626631
AddLogicalChild(newPage);
627632
newPage.NavigationProxy.Inner = NavigationProxy;
628633
_menuBarTracker.Target = newPage;
629634

630-
if (Parent != null)
635+
if (Parent is not null)
631636
{
632637
SendWindowAppearing();
633638
}
634639

635640
newPage.HandlerChanged += OnPageHandlerChanged;
636-
newPage.HandlerChanging += OnPageHandlerChanging;
641+
newPage.HandlerChanging += OnPageHandlerChanging;
637642

638-
if (newPage.Handler != null)
643+
if (newPage.Handler is not null)
644+
{
639645
OnPageHandlerChanged(newPage, EventArgs.Empty);
646+
}
640647
}
641648

642649
if (newPage is Shell newShell)
643650
{
644651
newShell.PropertyChanged += ShellPropertyChanged;
645652
}
646653

647-
if (oldPage?.IsLoaded == true)
648-
{
649-
this.OnUnloaded(() => oldPage.DisconnectHandlers());
650-
}
651-
else
652-
{
653-
oldPage?.DisconnectHandlers();
654-
}
655-
656654
Handler?.UpdateValue(nameof(IWindow.FlowDirection));
655+
656+
newPage?.WireUpAsIncomingPage(oldPage);
657657
}
658658

659659
void OnPageHandlerChanged(object? sender, EventArgs e)

0 commit comments

Comments
 (0)