Detect when pages are popped from clicking on tab (#9086)

This commit is contained in:
Shane Neuville 2020-01-07 18:07:00 -07:00 коммит произвёл Samantha Houts
Родитель 418e2f994a
Коммит 8fb1bb0eea
6 изменённых файлов: 211 добавлений и 18 удалений

Просмотреть файл

@ -0,0 +1,83 @@
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif
namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 9006, "[Bug] Unable to open a new Page for the second time in Xamarin.Forms Shell Tabbar",
PlatformAffected.iOS)]
#if UITEST
[NUnit.Framework.Category(UITestCategories.Shell)]
#endif
public class Issue9006 : TestShell
{
protected override void Init()
{
Routing.RegisterRoute("Issue9006_ContentPage", typeof(ContentPage));
Routing.RegisterRoute("Issue9006_FinalPage", typeof(ContentPage));
var contentPage = AddBottomTab("Tab 1");
Items[0].CurrentItem.AutomationId = "Tab1AutomationId";
AddBottomTab("Ignore Me");
Label label = new Label()
{
Text = "Clicking on the first tab should pop you back to the root",
AutomationId = "FinalLabel"
};
Button button = null;
bool navigated = false;
button = new Button()
{
Text = "Click Me",
AutomationId = "Click Me",
Command = new Command(async () =>
{
await GoToAsync("Issue9006_ContentPage");
await GoToAsync("Issue9006_FinalPage");
button.Text = "Click me again. If pages get pushed again then test has passed.";
DisplayedPage.Content = new StackLayout()
{
Children =
{
label
}
};
if (navigated)
label.Text = "Success";
navigated = true;
})
};
contentPage.Content = new StackLayout()
{
Children =
{
button
}
};
}
#if UITEST && __IOS__
[Test]
public void ClickingOnTabToPopToRootDoesntBreakNavigation()
{
RunningApp.Tap("Click Me");
RunningApp.WaitForElement("FinalLabel");
RunningApp.Tap("Tab1AutomationId");
RunningApp.Tap("Click Me");
RunningApp.Tap("Success");
}
#endif
}
}

Просмотреть файл

@ -602,6 +602,14 @@ namespace Xamarin.Forms.Controls
#endif
}
protected ContentPage DisplayedPage
{
get
{
return (ContentPage)(CurrentItem.CurrentItem as IShellSectionController).PresentedPage;
}
}
public ContentPage AddTopTab(string title)
{
var page = new ContentPage();
@ -633,8 +641,15 @@ namespace Xamarin.Forms.Controls
public ContentPage AddBottomTab(string title)
{
ContentPage page = new ContentPage();
if (Items.Count == 0)
{
var item = AddContentPage(page);
item.Items[0].Items[0].Title = title ?? page.Title;
item.Items[0].Title = title ?? page.Title;
return page;
}
Items[0].Items.Add(new ShellSection()
{
Title = title,

Просмотреть файл

@ -11,6 +11,7 @@
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)CollectionViewGroupTypeIssue.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8262.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue9006.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8207.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue6362.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7505.cs" />
@ -1684,4 +1685,4 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>
</Project>

Просмотреть файл

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using Xamarin.Forms.Internals;
@ -21,8 +22,19 @@ namespace Xamarin.Forms
void SendInsetChanged(Thickness inset, double tabThickness);
void SendPopping(Task poppingCompleted);
void SendPoppingToRoot(Task finishedPopping);
[Obsolete]
[EditorBrowsable(EditorBrowsableState.Never)]
void SendPopped();
[Obsolete]
[EditorBrowsable(EditorBrowsableState.Never)]
void SendPopping(Page page);
[Obsolete]
[EditorBrowsable(EditorBrowsableState.Never)]
void SendPopped(Page page);
}
}

Просмотреть файл

@ -118,6 +118,45 @@ namespace Xamarin.Forms
_lastTabThickness = tabThickness;
}
async void IShellSectionController.SendPopping(Task poppingCompleted)
{
if (_navStack.Count <= 1)
throw new Exception("Nav Stack consistency error");
var page = _navStack[_navStack.Count - 1];
_navStack.Remove(page);
UpdateDisplayedPage();
await poppingCompleted;
RemovePage(page);
SendUpdateCurrentState(ShellNavigationSource.Pop);
}
async void IShellSectionController.SendPoppingToRoot(Task finishedPopping)
{
if (_navStack.Count <= 1)
throw new Exception("Nav Stack consistency error");
var oldStack = _navStack;
_navStack = new List<Page> { null };
for (int i = 1; i < oldStack.Count; i++)
oldStack[i].SendDisappearing();
UpdateDisplayedPage();
await finishedPopping;
for (int i = 1; i < oldStack.Count; i++)
RemovePage(oldStack[i]);
SendUpdateCurrentState(ShellNavigationSource.PopToRoot);
}
[Obsolete]
[EditorBrowsable(EditorBrowsableState.Never)]
void IShellSectionController.SendPopped()
{
if (_navStack.Count <= 1)
@ -131,6 +170,8 @@ namespace Xamarin.Forms
SendUpdateCurrentState(ShellNavigationSource.Pop);
}
[Obsolete]
[EditorBrowsable(EditorBrowsableState.Never)]
void IShellSectionController.SendPopping(Page page)
{
if (_navStack.Count <= 1)
@ -140,9 +181,11 @@ namespace Xamarin.Forms
SendAppearanceChanged();
}
[Obsolete]
[EditorBrowsable(EditorBrowsableState.Never)]
void IShellSectionController.SendPopped(Page page)
{
if(_navStack.Contains(page))
if (_navStack.Contains(page))
_navStack.Remove(page);
RemovePage(page);
@ -313,6 +356,7 @@ namespace Xamarin.Forms
if (previousPage != DisplayedPage)
{
previousPage?.SendDisappearing();
PresentedPageAppearing();
SendAppearanceChanged();
}

Просмотреть файл

@ -63,6 +63,7 @@ namespace Xamarin.Forms.Platform.iOS
TaskCompletionSource<bool> _popCompletionTask;
IShellSectionRootRenderer _renderer;
ShellSection _shellSection;
bool _ignorePopCall;
public ShellSectionRenderer(IShellContext context)
{
@ -265,16 +266,48 @@ namespace Xamarin.Forms.Platform.iOS
DisposePage(page);
}
protected virtual async void OnPopToRootRequested(NavigationRequestedEventArgs e)
public override UIViewController[] PopToRootViewController(bool animated)
{
var animated = e.Animated;
if (!_ignorePopCall && ViewControllers.Length > 1)
{
ProcessPopToRoot();
}
return base.PopToRootViewController(animated);
}
async void ProcessPopToRoot()
{
var task = new TaskCompletionSource<bool>();
var pages = _shellSection.Stack.ToList();
_completionTasks[_renderer.ViewController] = task;
e.Task = task.Task;
((IShellSectionController)ShellSection).SendPoppingToRoot(task.Task);
await task.Task;
PopToRootViewController(animated);
for (int i = pages.Count - 1; i >= 1; i--)
{
var page = pages[i];
DisposePage(page);
}
}
protected virtual async void OnPopToRootRequested(NavigationRequestedEventArgs e)
{
var animated = e.Animated;
var task = new TaskCompletionSource<bool>();
var pages = _shellSection.Stack.ToList();
try
{
_ignorePopCall = true;
_completionTasks[_renderer.ViewController] = task;
e.Task = task.Task;
PopToRootViewController(animated);
}
finally
{
_ignorePopCall = false;
}
await e.Task;
@ -328,6 +361,7 @@ namespace Xamarin.Forms.Platform.iOS
_ = _context.ApplyNativeImageAsync(ShellSection, ShellSection.IconProperty, icon =>
{
TabBarItem = new UITabBarItem(ShellSection.Title, icon, null);
TabBarItem.AccessibilityIdentifier = ShellSection.AutomationId ?? ShellSection.Title;
});
}
@ -399,11 +433,10 @@ namespace Xamarin.Forms.Platform.iOS
var poppedPage = _shellSection.Stack[_shellSection.Stack.Count - 1];
// this is used to setup appearance changes based on the incoming page
((IShellSectionController)_shellSection).SendPopping(poppedPage);
((IShellSectionController)_shellSection).SendPopping(popTask);
await popTask;
((IShellSectionController)_shellSection).SendPopped(poppedPage);
DisposePage(poppedPage);
}
@ -491,17 +524,22 @@ namespace Xamarin.Forms.Platform.iOS
navigationController.SetNavigationBarHidden(!navBarVisible, true);
var coordinator = viewController.GetTransitionCoordinator();
if (coordinator != null)
if (coordinator != null && coordinator.IsInteractive)
{
// handle swipe to dismiss gesture
coordinator.NotifyWhenInteractionEndsUsingBlock((context) =>
{
if (!context.IsCancelled)
{
_self._popCompletionTask = new TaskCompletionSource<bool>();
_self.SendPoppedOnCompletion(_self._popCompletionTask.Task);
}
});
if (Forms.IsiOS10OrNewer)
coordinator.NotifyWhenInteractionChanges(OnInteractionChanged);
else
coordinator.NotifyWhenInteractionEndsUsingBlock(OnInteractionChanged);
}
}
void OnInteractionChanged(IUIViewControllerTransitionCoordinatorContext context)
{
if (!context.IsCancelled)
{
_self._popCompletionTask = new TaskCompletionSource<bool>();
_self.SendPoppedOnCompletion(_self._popCompletionTask.Task);
}
}
}