When using navigationpage's InsertPageBefore method toolbar items disappear on iOS - xamarin

When I navigate to a page that uses a flow of NavigationPage.Navigation.InsertPageBefore(ContentPage) then NavigationPage.PopAsync().ConfigureAwait(false) the toolbar item on the global navigation page disappears but when using push/pop the tool bar always remains. This only happens on iOS.
The navigation page is a global scoped to the app level along with a master detail page in which the navigation page serves as the detail portion.
Add ToolbarItem
var toobaritemMenu = new ToolbarItem
{
Icon = "Screenshot",
Command = new Command(() => Device.BeginInvokeOnMainThread(() => App.MasterNavigation.PushAsync(new ContentPage(), false)))
};
App.MasterNavigation.ToolbarItems.Add(toobaritemMenu);
Moving to a Page Like This Causes Toolbar Item to Disappear on iOS
Device.BeginInvokeOnMainThread(() => App.MasterNavigation.Navigation.InsertPageBefore(new ContentPage(), App.MasterNavigation.CurrentPage));
Device.BeginInvokeOnMainThread(() => App.MasterNavigation.PopAsync().ConfigureAwait(false));
Is there a reason for this as it seems inconsistent since it works on Android.

I ran into the same issue. My workaround was to change how I was manipulating the navigation stack: instead of inserting the new page and popping off the current page, push the new page onto the stack and then remove the current one:
await currentPage.Navigation.PushAsync(nextPage, animated: true);
currentPage.Navigation.RemovePage(currentPage);
You end up with the same final navigation stack, but it avoids whatever issue is causing the problem with the secondary toolbar items in iOS.

Related

Xamarin Shell: Redirect to different page and remove current page from nav stack

I imagine this is pretty straightforward, but I'm struggling to make sense of how to do it via the documentation...
I've got a tab page, that is opened from a flyout item.
The tabs for this page are dynamically loaded from saved data in the code behind, and all works exactly as expected.
However, in the scenario when there is no saved data (and thus, no tabs) I want to redirect the user to a different page, and crucially, not be able to navigate back to this tab page (but allow going back to the page they were initially on BEFORE they navigated to the tabbed page)
I've got a method in initalize for the tabbed page that, if it doesn't have any saved data, attempts to do this. I started with:
Shell.Current.Navigation.PopAsync();
Shell.Current.GoToAsync(nameof(AddNewDataPage));
which navigated to the add page, but pressing the back button just resulted in the add page being shown again, over and over.
I then tried:
Shell.Current.Navigation.PopToRootAsync();
Shell.Current.GoToAsync(nameof(AddNewDataPage));
which did the same.
So next I went with trying to navigate backwards:
Shell.Current.GoToAsync("../" + nameof(AddNewDataPage));
which showed the right page again, but now the back button doesn't work
Next, I went with trying an absolute route:
Shell.Current.GoToAsync("//HomePage/" + nameof(AddNewDataPage));
which worked... sort of.
The first time the user clicks the flyout for the tabbed page, it all works great. back button takes you to the home page etc... but the second and subsequent times the user clicked the flyout, they navigate to the tabbed page and my LoadData method isn't called.
I assumed this is because the tabbed page is still loaded, so I added:
protected override void OnAppearing()
{
LoadData();
}
Now, when the user clicks the flyout for the second and subsequent times, they navigate to the HomePage page instead of the AddNewDataPage page (an improvement, I guess?)
So, now I'm at a loss.. it seems like this should be really simple, but I can't figure it out.
You can try this.
//route The route hierarchy will be searched for the specified route, upwards from the current position. The matching page will replace the navigation stack.
https://learn.microsoft.com/nl-nl/xamarin/xamarin-forms/app-fundamentals/shell/navigation?WT.mc_id=friends-0000-jamont#relative-routes
await Shell.Current.GoToAsync($"//{nameof(AddNewDataPage)}");
https://www.youtube.com/watch?v=ylbgWHB_gMI&t=703s&ab_channel=JamesMontemagno
An explanation about relative routes.
May I don't understand your problem clearly. But I created a simple with the flyout shell app.
At first, it seems that the construction method of the content page will jut call once which the page shows first time. So I put the following code into it. Such as:
public ItemsPage()
{
InitializeComponent();
BindingContext = _viewModel = new ItemsViewModel();
Shell.Current.Navigation.PopAsync();
Shell.Current.GoToAsync(nameof(NewItemPage));
}
And then when the app got into the ItemsPage first time, it will get into the NewItemPage. User can add new item here. After that, when I clicked the back button, I will get into the ItemsPage back without it's construction method called. So you can't add the LoadData(); in it.
You can put it into the OnAppearing method. Such as:
protected override void OnAppearing()
{
//LoadData();
if (_viewModel.Items.Count < 8)
{
Shell.Current.Navigation.PopAsync();
Shell.Current.GoToAsync(nameof(NewItemPage));
// then add two item in the NewItemPage
}
else {
base.OnAppearing();
_viewModel.OnAppearing();
}
}
This will get the same effect as above. I also try to move the Shell.Current.Navigation.PopAsync();. The result is same. It will get back to the ItemsPage when you clicked the back button in the NewItemPage after you add two item.
which navigated to the add page, but pressing the back button just resulted in the add page being shown again, over and over.
You may just need a if() to judge the condition the app need to go to the AddNewDataPage with skipping the tab page.

Deep link only reopens the same screen and cannot add to the navigation stack

Currently in my application I have a screen that is connected to the deep links through react navigation. When I open this screen, background the application and then try to open a deep-link, react navigation opens the existing screen on the navigation stack.
I want the linking options to trigger a completely new page on top of the existing one and put it in the stack, is this possible?
You can do this by specifying a getId function:
<Stack.Screen
name="Profile"
component={ProfileScreen}
getId={({ params }) => params.userId}
/>
The getId function should return a unique ID to identify the screen - usually, the ID of the item shown in the screen, React Navigation will only reuse the screen if the ID is the same.
https://reactnavigation.org/docs/screen/#getid

Xamarin Forms Navigation.PushAsync works only once

In our Xamarin Forms app we have multiple pages. When we navigate to an other page for the first time from the main navigation page it works as expected. When we then use the back button (in the app or the Android system button) and navigate again to the next page then it doesn't load. It changes the title but it shows an empty page.
It does not matter what page you use as first page to go to and what page is the second page to go to. It also doesn't work if the first and second page is the same page.
In the app we initialize the main page as followed:
MainPage = new NavigationPage(new MainPage());
The MainPage is an TabbedPage.
We just navigate like this:
if (Application.Current.MainPage is NavigationPage mainPage)
{
await mainPage.Navigation.PushAsync(new DetailPage());
}
This worked before, but I think that during the update of Xamarin.Forms from 4.2.0.848062 to 4.3.0.947036 it broke. But I cannot find anything that could brake this is release notes.
== Edit ==
We saw some inconsistency in all the calls to navigate. So we created an helper class to do all the navigation. This now looks like this:
public static class NavigationHelper
{
public static void NavigateTo(Page page)
{
if (Application.Current.MainPage is NavigationPage mainPage)
{
Device.BeginInvokeOnMainThread(async () =>
{
await mainPage.Navigation.PushAsync(page);
});
}
}
}
So we now only navigate like this:
NavigationHelper.NavigateTo(new DetailPage());
But this still doesn't fix the issue. The first time we get the correct page, the second navigation we get an empty screen. The title in the navigation bar is changing to the page title where we navigate to. But the rest keeps empty.
Are you remembering to use the "Navigation" of the (NavigationPage)Application.Current.MainPage; - and not the current pages Navigation?
I've found it with some help. In the Output Window we found the message
System.ObjectDisposedException: Cannot access a disposed object.
This happened when we went back to the previous page and made it impossible to go to the next page. Turned out to be an bug in the view model. We where trying to add items to an observable list that was already disposed. Fixing this fixed the navigation issue.

Xamarin Forms MasterDetailPage - Secondary menu drawn twice on iOS

My app uses Master-Detail navigation.
I push pages on the navigation stack to have back button functionality.
When pushing a page with ToolbarItems (secondary menu), that's fine on Android, but it doubles the menu items on iOS.
Switching to the same page (not pushing), there is no problem on iOS (secondary menu shown once, as expected).
I adopted the official MasterDetail sample (https://github.com/xamarin/xamarin-forms-samples/tree/master/Navigation/MasterDetailPage) to show what I mean, see solution zip file.
Compile and start it on iOS device, press the button on the first page to open a sub page, and you should see doubled menu items (actually one which is drawn twice), so there are two rows of secondary menu items visible instead of just one.
The two rows are identical, both rows (and their menu items) react when tapping.
If you select the Reminder page through the Hamburger menu, the secondary menu is fine (even on iOS), as the page is not pushed on the navigation stack but switched.
Some interesting parts:
ContactsPage.xaml.cs:
SwitchToDetailPageAndPushOnNavStack()
ReminderPage.xaml:
<ContentPage.ToolbarItems>
<ToolbarItem Text="Do something" Clicked="OnDoSomethingClicked" Order="Secondary" />
</ContentPage.ToolbarItems>
What can I do to get rid of this weird effect on iOS?
It looks like you are adding a new NavigationPage which seems to be causing the issue. You block the extra nav bar with:
NavigationPage.SetHasNavigationBar(newPage, false);
but still the added toolbar item is getting added twice.
If instead you do the usual way of pushing a page, then this issue disappears. E.g. in ContactsPage.xaml.cs file, change:
private void OnOpenSubPageClicked(object sender, EventArgs args)
{
Device.BeginInvokeOnMainThread(() =>
{
MainPage mainPage = App.Instance.MainPage as MainPage;
if (null != mainPage)
mainPage.SwitchToDetailPageAndPushOnNavStack(mainPage.MasterPage.RemindersPageItem);
})
}
to:
private async void OnOpenSubPageClicked(object sender, EventArgs args)
{
await Navigation.PushAsync(new ReminderPage());
}
That seems to work as expected.
I had a similar issue. Posting here in case anyone else finds themselves in this bind.
I was getting the doubled toolbar when I had this code in App.cs...
MainPage = new NavigationPage(new MyApp.Home());
But changing it to
MainPage = new MyApp.Home();
solved the issue for me.

Correct use of Prism navigation service in xamarin forms

I want to confirm if I am using the prism navigation service with xamarin forms correctly. I have a master detail page, a styled navigation page and a bunch of content pages.
right now I am using the service in the following way:
var prj = await dataService.GetLwdProject(appState.SelectedProjectId);
var nparam = new NavigationParameters();
nparam.Add("Project", prj);
await NavigateTo("RootPage/StyledNavigationPage/SessionsListPage", nparam);
Where the Master detail page is the RootPage object. So expecting that when a user selects an item from this list page the correct way to the service should be:
var nparma = new NavigationParameters();
nparma.Add("Session", option);
await App.NavigateTo("RootPage/StyledNavigationPage/SessionsListPage?ProjectId=" + option.ProjectId + "/LocationListPage", nparma);
What I expect that just a LocationListPage would be added to the navigation stack, but when I use the the hardware back button on android it looks like that not only the last page was added but the whole path (all pages). So is this the correct way auto construct the desired path?
No. Navigation is always relative to where you are calling it. What you have now will navigate to the entire deep link you have created every time. Just navigate to your target NavigationPage/SessionListPage and pass your parameter. Though, you won't get a new page every time in this case, since you are navigating to the same view, but just passing different state.

Resources