Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Ensure iOS NavigationBar renders when inside of AutoLayout #525

Merged
merged 3 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,60 +1,52 @@
<Page x:Class="Uno.Toolkit.Samples.Content.NestedSamples.M3MaterialNavigationBarSample_Primary"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Uno.Toolkit.Samples.Content.NestedSamples"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:utu="using:Uno.Toolkit.UI"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Uno.Toolkit.Samples.Content.NestedSamples"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:utu="using:Uno.Toolkit.UI"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<utu:AutoLayout>
<!--<StackPanel>-->
kazo0 marked this conversation as resolved.
Show resolved Hide resolved
<utu:NavigationBar AutomationProperties.AutomationId="M3Page1PrimaryNavBar"
Content="Page Title"
MainCommandMode="Action"
Style="{StaticResource MaterialPrimaryNavigationBarStyle}">
Content="Page Title"
MainCommandMode="Action"
Style="{StaticResource MaterialPrimaryNavigationBarStyle}">
<utu:NavigationBar.MainCommand>
<AppBarButton AutomationProperties.AutomationId="M3_Primary_NavBar_Close_Button"
Click="NavigateBack"
Label="Close">
Click="NavigateBack"
Label="Close">
<AppBarButton.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/CloseIcon.png" />
</AppBarButton.Icon>
</AppBarButton>
</utu:NavigationBar.MainCommand>
<utu:NavigationBar.PrimaryCommands>
<AppBarButton Label="More"
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}">
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}">
<AppBarButton.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/MaterialIcon_Small.png" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="More"
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}">
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}">
<AppBarButton.Icon>
<BitmapIcon UriSource="ms-appx:///Assets/AppleIcon_Small.png" />
</AppBarButton.Icon>
</AppBarButton>
</utu:NavigationBar.PrimaryCommands>
<utu:NavigationBar.SecondaryCommands>
<AppBarButton Label="More"
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}" />
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}" />
<AppBarButton Label="More"
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}" />
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}" />
<AppBarButton Label="More"
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}" />
Style="{StaticResource MaterialPrimaryAppBarButtonStyle}" />
</utu:NavigationBar.SecondaryCommands>
</utu:NavigationBar>
<StackPanel Grid.Row="1"
Padding="16,0"
VerticalAlignment="Center"
Spacing="8">
<Button Click="NavigateBack"
Content="Exit sample" />
</StackPanel>
</Grid>
<Button Margin="100" Content="Back" Click="NavigateBack" />
<!--</StackPanel>-->
</utu:AutoLayout>
</Page>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Windows.Input;
using Uno.Toolkit.Samples.Entities;
using Uno.Toolkit.Samples.ViewModels;
using Uno.UI;
using Windows.Foundation;
using Windows.Foundation.Collections;
#if IS_WINUI
Expand All @@ -26,21 +27,16 @@
using Windows.UI.Xaml.Navigation;
#endif

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238

namespace Uno.Toolkit.Samples.Content.NestedSamples
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class M3MaterialNavigationBarSample_Primary : Page
{
public M3MaterialNavigationBarSample_Primary()
{
this.InitializeComponent();
}
public sealed partial class M3MaterialNavigationBarSample_Primary : Page
{
public M3MaterialNavigationBarSample_Primary()
{
this.InitializeComponent();
}

private void NavigateBack(object sender, RoutedEventArgs e) => Shell.GetForCurrentView().BackNavigateFromNestedSample();

}
private void NavigateBack(object sender, RoutedEventArgs e) => Shell.GetForCurrentView().BackNavigateFromNestedSample();
}
}
44 changes: 44 additions & 0 deletions src/Uno.Toolkit.RuntimeTests/Tests/NavigationBarTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,50 @@ public async Task MainCommand_In_Popup_With_Page_With_BackStack(MainCommandMode
popup.IsOpen = false;
}
}

#if __IOS__
[TestMethod]
public async Task NavigationBar_Does_Render()
{
var frame = new Frame { Width = 200, Height = 200 }; ;
await UnitTestUIContentHelperEx.SetContentAndWait(frame);

frame.Navigate(typeof(NavBarSimplePage));

await UnitTestsUIContentHelper.WaitForIdle();

AssertNavigationBar(frame);
}

[TestMethod]
public async Task NavigationBar_Does_Render_Within_AutoLayout()
{
var frame = new Frame { Width = 200, Height = 200 };;
await UnitTestUIContentHelperEx.SetContentAndWait(frame);

frame.Navigate(typeof(NavBarAutoLayoutPage));

await UnitTestsUIContentHelper.WaitForIdle();

AssertNavigationBar(frame);
}

private static void AssertNavigationBar(Frame frame)
{
var page = frame.Content as Page;
var presenter = frame.FindChild<NativeFramePresenter>();
var navBar = page?.FindChild<NavigationBar>();

var renderedNativeNavItem = navBar?.GetRenderer<NavigationBar, NavigationBarNavigationItemRenderer>(null)?.Native;
var renderedNativeNavBar = navBar?.GetRenderer<NavigationBar, NavigationBarRenderer>(null)?.Native;

Assert.IsNotNull(presenter);
Assert.IsFalse(presenter!.NavigationController.NavigationBarHidden);

Assert.AreSame(renderedNativeNavItem, presenter.NavigationController.TopViewController.NavigationItem);
Assert.AreSame(renderedNativeNavBar, presenter.NavigationController.NavigationBar);
}
#endif
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Page
x:Class="Uno.Toolkit.RuntimeTests.Tests.TestPages.NavBarAutoLayoutPage"
kazo0 marked this conversation as resolved.
Show resolved Hide resolved
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Uno.Toolkit.RuntimeTests.Tests.TestPages"
xmlns:utu="using:Uno.Toolkit.UI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<utu:AutoLayout>
<utu:NavigationBar Content="Testing" />
</utu:AutoLayout>
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Uno.Toolkit.UI;

#if IS_WINUI
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
#else
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
#endif

namespace Uno.Toolkit.RuntimeTests.Tests.TestPages
{
public sealed partial class NavBarAutoLayoutPage : Page
{
public NavBarAutoLayoutPage()
{
this.InitializeComponent();
}
}
}
12 changes: 12 additions & 0 deletions src/Uno.Toolkit.RuntimeTests/Tests/TestPages/NavBarSimplePage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Page x:Class="Uno.Toolkit.RuntimeTests.Tests.TestPages.NavBarSimplePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Uno.Toolkit.RuntimeTests.Tests.TestPages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:utu="using:Uno.Toolkit.UI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<utu:NavigationBar Content="Testing" />
</Page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;

// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238

namespace Uno.Toolkit.RuntimeTests.Tests.TestPages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class NavBarSimplePage : Page
{
public NavBarSimplePage()
{
this.InitializeComponent();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,20 @@ public PageViewController(Page? page)
NavigationBarHelper.PageCreated(this);
}


public override void ViewDidAppear(bool animated)
{
try
{
base.ViewDidAppear(animated);

NavigationBarHelper.PageDidAppear(this);
}
catch (Exception e)
{
this.Log().Error($"{e.Message}", e);
}
}
public override void ViewWillAppear(bool animated)
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,12 @@ public static void PageDestroyed(UIViewController pageController)
/// </summary>
/// <param name="pageController">The controller of the page</param>
public static void PageWillAppear(UIViewController pageController)
{
{
kazo0 marked this conversation as resolved.
Show resolved Hide resolved
var topNavigationBar = pageController.View.FindTopNavigationBar();
if (topNavigationBar != null)
{
EnsureNavigationItem(topNavigationBar, pageController);

if (topNavigationBar.Visibility == Visibility.Visible)
{
SetNavigationBar(topNavigationBar, pageController.NavigationController!.NavigationBar);
Expand Down Expand Up @@ -97,6 +99,53 @@ public static void PageWillAppear(UIViewController pageController)
}
}

// In some cases the NavigationBar may not be rendered yet when PageCreated/PageWillAppear is called
// This can be the case when the NavigationBar is part of an AutoLayout
// since the AutoLayout is delayed in materializing its Children
public static void PageDidAppear(UIViewController pageController)
{
var topNavigationBar = pageController.View.FindTopNavigationBar();
if (topNavigationBar != null)
{
EnsureNavigationItem(topNavigationBar, pageController);

if (topNavigationBar.Visibility == Visibility.Visible)
{
SetNavigationBar(topNavigationBar, pageController.NavigationController!.NavigationBar);

// When the NavigationBar is visible, we need to call SetNavigationBarHidden
// AFTER it has been rendered. Otherwise, it causes a bug introduced
// in iOS 11 in which the BackButtonIcon is not rendered properly.
pageController.NavigationController.SetNavigationBarHidden(hidden: false, animated: false);
}
else
{
// Even if the NavigationBar should technically be collapsed,
// we don't hide it using the NavigationController because it
// automatically disables the back gesture.
// In order to visually hide it, the NavigationBarRenderer
// will hide the native view using the UIView.Hidden property.
pageController.NavigationController!.SetNavigationBarHidden(hidden: false, animated: false);
SetNavigationBar(topNavigationBar, pageController.NavigationController.NavigationBar);
}
}
else // No NavigationBar
{
pageController.NavigationController!.SetNavigationBarHidden(true, true);
}
}

private static void EnsureNavigationItem(NavigationBar topNavigationBar, UIViewController pageController)
{
// The Native view from the renderer may have been set to a dummy NavigationItem if we were not able to find
// a NavigationBar in the PageCreated method. If that is the case, set it to the pageController's NavigationItem
var nativeItem = topNavigationBar.GetRenderer(() => (NavigationBarNavigationItemRenderer?)null)?.Native;
if (!ReferenceEquals(nativeItem, pageController.NavigationItem))
{
SetNavigationItem(topNavigationBar, pageController.NavigationItem);
}
}

/// <summary>
/// When a page <see cref="UIViewController" /> did disappear, disconnects the <see cref="NavigationBar" /> from the navigation controller.
/// </summary>
Expand Down
5 changes: 3 additions & 2 deletions src/Uno.Toolkit.UI/Controls/NavigationBar/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,13 @@ internal static class RendererHelper
{
private static readonly WeakAttachedDictionary<DependencyObject, Type> _renderers = new WeakAttachedDictionary<DependencyObject, Type>();

public static TRenderer GetRenderer<TElement, TRenderer>(this TElement element, Func<TRenderer> rendererFactory)
public static TRenderer GetRenderer<TElement, TRenderer>(this TElement element, Func<TRenderer>? rendererFactory)
where TElement : DependencyObject
{
return _renderers.GetValue(element, typeof(TRenderer), rendererFactory);
}
public static TRenderer ResetRenderer<TElement, TRenderer>(this TElement element, Func<TRenderer> rendererFactory)

public static TRenderer ResetRenderer<TElement, TRenderer>(this TElement element, Func<TRenderer>? rendererFactory)
where TElement : DependencyObject
{
return _renderers.GetValue(element, typeof(TRenderer), rendererFactory);
Expand Down