As I am using xamarin.forms for my application in android which I have to navigate from one page to another. My question is, if navigating from one page to another page adds it to the navigation stack. For example, If my app has navigation such as Page1 --> Page2 --> Page3 --> Page4 --> Page1 (It behaves like cycle) will it replace Page1 when I navigate to that page on second time or will it be adding it to the stack. Can anyone explain about navigation in a simple way??
EDIT
What I mean by replace means if navigating from one page to another adds it to the stack, Won't it affect the performance of the application if the navigation continues like a loop and keeps on adding it to the stack??
Note: I don't want to go back to previous page, Just want to navigate from one to another continously.
Thanks in advance
Can you try to elaborate on the question a bit more? What do you mean by 'replace'?
It's a stack, so no: the first page1 will not be 'replaced', rather a 'copy' will be in place.
Example:
Imagine a list view with bound data objects. When you click an item, you get to an item details page. Imagine the details page to have previous and next buttons to navigate to other items and you press one. The stack will look like this: ListViewPage -> ItemsPage -> ItemsPage.
What you're talking about is CarouselPage
CarouselPage takes several ContentPage children and allows you to swipe sideways to switch the page.
public class App
{
public static Page GetMainPage ()
{
var carousel = new CarouselPage ();
carousel.Children.Add (new ContentPage () {
Title="One",
Content = new BoxView {
WidthRequest = 90,
HeightRequest = 100,
BackgroundColor = Color.Blue
}
});
carousel.Children.Add (new ContentPage () {
Title="TWO",
Content = new BoxView {
WidthRequest = 90,
HeightRequest = 100,
BackgroundColor = Color.Red
}
});
return carousel;
}
}
Alternatively you can put two buttons (or suitable UI elements) on each page you want to show and use Navigation.PushModalAsync() to navigate to previous/next pages
Using this.Navigation.PopModalAsync () before PushModalAsync did the trick for me.
this.Navigation.PopModalAsync ();
Now my application runs smoothly :)
Related
In my application I have created my own Loading indicator with the help of Page class in xamarin.forms, and I use 'PushModalAsync' api to show Loading Indicator wherever needed like below
var loadingindicator=new LoadingIndicator();
MyApp.CurrentAppInstance.MainPage.Navigation.PushModalAsync(loadingindicator,false);
Everything works as expected and It is looking fine and I also managed to make it look like AndHUD by tweeking the alpha for the controls in that page until I hit an issue,
The issue is that, every time I show and hide the loadingindicator page 'OnAppearing()' getting called on the current top page on view stack.
I can fix this by introducing one additional functionality on all pages where I am using my LoadingIndicator, But I feel there might be some other cleaner way to solve this issue.
Can you guys suggest me if there is any cleaner approach available to solve this issue?
(I target this solution mainly for android and I want to achieve it through common code)
If I have understand the problem, I think there are some way to add Loading indicator.
Use ActivityIndicator
Use aritchie/userdialogs's Loading
using (this.Dialogs.Loading("Test Loading"))
await Task.Delay(3000);
Create a PopUp With Rg.Plugins.Popup
// Use these methods in PopupNavigation globally or Navigation in your pages
// Open new PopupPage
Task PushAsync(PopupPage page, bool animate = true) // Navigation.PushPopupAsync
// Hide last PopupPage
Task PopAsync(bool animate = true) // Navigation.PopPopupAsync
// Hide all PopupPage with animations
Task PopAllAsync(bool animate = true) // Navigation.PopAllPopupAsync
// Remove one popup page in stack
Task RemovePageAsync(PopupPage page, bool animate = true) // Navigation.RemovePopupPageAsync
This question and its answers (copied below) provide a solution for handling the back button in Ionic, but that solution only works when other pages are pushed directly from app.component, in which case calling canGoBack and getActive() on this.nav works correctly because other pages have been pushed using this.nav.push in app.component.
However if a page is pushed from one of the pages in the tabs (lets call it page1), i.e. by calling this.navCtrl.push() in page1, then this.nav.canGoBack() in app.components still resolves to false because the push happened using the page1's this.navCtrl.push() not app.component's this.nav.push().
How can I detect inside app.components if a page was pushed from any of pages in the tabs?
platform.registerBackButtonAction(() => {
if(this.nav.canGoBack()){
this.nav.pop();
}else{
if(this.alert){
this.alert.dismiss();
this.alert =null;
}else{
this.showAlert();
}
}
});
});
}
I really like the style of the Tabs in Ionic 3 when you have an icon and a text:
https://ionicframework.com/docs/components/#tabs-icon-text
However in the App I am using I have 4 Actions. For example open the Camera, open Google Maps and so on. And this I want to display on the bottom of the page exactly in this kind of style.
How can I achieve this, since this is not the real purpose of the Tabs and since I haven't found a similar Component.
Just like you can see in the Tab docs:
Sometimes you may want to call a method instead of navigating to a new
page. You can use the (ionSelect) event to call a method on your class
when the tab is selected.
<ion-tabs>
<ion-tab (ionSelect)="chat()" tabTitle="Show Modal"></ion-tab>
</ion-tabs>
And then
export class Tabs {
constructor(public modalCtrl: ModalController) {}
chat() {
let modal = this.modalCtrl.create(ChatPage);
modal.present();
}
}
I'm using AppGyver Steroids and Supersonic to build an app and I'm having some issues navigating between views programmatically.
Based on the docs, you navigate between views like this:
var view_obj = new supersonic.ui.View("main#index");
supersonic.ui.layers.push(view_obj);
However, when I inspect things via the Chrome DevTools, it appears that a second duplicate view is created i.e. If I navigate away from the index page and then navigate back, I now have two index pages, instead of what [I think] should be one. It also doesn't close the previous view I was on.
How can I prevent this from happening and simply move to the existing view, instead of duplicating views? How do I close a view after I have navigated away from it?
Thanks.
The problem you're encountering is that you're creating a new supersonic.ui.View("main#index") every time you navigate. On top of this, I think you want to return to the same view when you navigate back to a view for the second time, i.e. you want the view to remain in memory even if it has been removed from the navigation stack with pop() (rather than pushing a new instance of that view). For this, you need to preload or "start()" the view, as described in the docs here.
I implemented my own helper function to make this easier; here is my code:
start = function(dest, isModal) {
var viewId=dest,
view=new supersonic.ui.View({
location: dest,
id: viewId
});
view.isStarted().then(function(started) {
if (started) {
if (isModal) {supersonic.ui.modal.show(view);}
else {supersonic.ui.layers.push(view);}
} else {
// Start Spinner
supersonic.ui.views.start(view).then(function() {
if (isModal) {supersonic.ui.modal.show(view);}
else {supersonic.ui.layers.push(view);}
// Stop Spinner
}, function(error) {
// Stop Spinner
A.error(error);
});
}
});
};
Use it like start('module#view');. As a bonus, you can pass true as the second argument and it gets pushed as a modal instead.
It checks if you've already started a view - if so, it just pushes that view back onto the stack. If not, it start()s (i.e. preloads) it, then pushes it. This ensures that the view stays in memory (with any user input that has been modified) even when you pop() it from the stack.
You have to imagine that the layer stack is actually a stack in the Computer Science sense. You can only add and remove views at the top of the stack. The consequence of this is that complex navigations such as A > B > C > D > B are difficult/hacky to do (in this case, you'd have to pop() D and C in succession to get back to B).
Views will close if you pop() them, as long as you didn't start() them. If you did, and you pop() them, they remain in memory. To kill that view, you have to call stop() on it, as described in the docs I linked above.
try
var view_obj = new supersonic.ui.View("main#index");
supersonic.ui.layers.replace(view_obj);
And take a look at supersonic.ui.layers.pop();
Thanks to LeedsEbooks for helping me get my head around this challenge. I was able to find a solution. Here is the code:
var start = function(route_str, isModal) {
var regex = /(.*?)#(.*)/g;
var match_obj = regex.exec(route_str);
var view_id_str = match_obj[2],
view_location_str = route_str,
view = new supersonic.ui.View({
location: view_location_str,
id: view_id_str
});
view.isStarted().then(function(started) {
if (started)
{
if (isModal)
{
supersonic.ui.modal.show(view);
}
else {
supersonic.ui.layers.push(view);
}
}
else
{
// Start Spinner
supersonic.ui.views.start(view).then(function() {
if (isModal)
{
supersonic.ui.modal.show(view);
}
else
{
supersonic.ui.layers.push(view);
}
// Stop Spinner
}, function(error) {
// Stop Spinner
A.error(error);
});
}
});
};
You must ensure that your route has the format module#view as defined in the documentation.
PLEASE NOTE
There seems to some problem with the supersonic ui method for starting views. If you run the following code:
supersonic.ui.views.start("myapp#first-view");
supersonic.ui.views.find("first-view").then( function(startedView) {
console.log(startedView);
});
You'll notice that your view id and location are identical. This seems to be wrong as the id should be first-view and location should be myapp#first-view.
So I decided to not use the AppGyver methods and create my own preload method instead, which I run from the controller attached to my home view (this ensures that all the views I want to preload are handled when the app loads). Here is the function to do this:
var preload = function(route_str)
{
var regex = /(.*?)#(.*)/g;
var match_obj = regex.exec(route_str);
var view = new supersonic.ui.View({
location: route_str,
id: match_obj[2]
});
view.start();
};
By doing this, I'm sure that the view will get loaded with the right location and id, and that when I use my start() function later, I won't have any problems.
You'll want to make sure that your structure.coffee file doesn't have any preload instructions so as not to create duplicate views that you'll have problems with later.
Finally, I have a view that is 2 levels in that is a form that posts data via AJAX operation. I wanted the view to go back to the previous view when the AJAX operation was complete. Using my earlier function resulted in the push() being rejected. It would be nice if AppGyver Supersonic could intelligently detect that pushing to a previous view should default to a layers.pop operation, but you don't always get what you want. Anyway, I managed to solve this using supersonic.ui.layers.pop(), which simply does what the Back button would have done.
Everything working as intended now.
I will try to explain this as clearly as possible. I have an android app using web view to basically load a webpage as my app. I have everything working great, however the back button seems to be an issue. I have set this page up all on one html page, it will load in a div when certain buttons are clicked to give the feel of a new page without actually having one. I basically want the back button (on the android tablet or smartphone) to load the previously loaded div, but I have no idea where to start with this. Here is what the content switching jquery looks like -
function contentSwitcher(settings){
var settings = {
contentClass : '.contentToLoad',
navigationId : '#sideMenu',
servFront : '#clickHomeHome'
};
//Hide all of the content except the first one on the nav
$(settings.contentClass).not(':first').hide();
$(settings.navigationId).find('li:first').addClass('active');
//onClick set the active state,
//hide the content panels and show the correct one
$(settings.navigationId).find('a').click(function(e){
var contentToShow = $(this).attr('href');
contentToShow = $(contentToShow);
//dissable normal link behaviour
e.preventDefault();
//set the proper active class for active state css
$(settings.navigationId).find('li').removeClass('active');
$(this).parent('li').addClass('active');
//hide the old content and show the new
$(settings.contentClass).hide();
contentToShow.show("slow");
});
}
contentSwitcher();
});
note: I've cropped out a bunch of it just to show how it works on a basic level.
Does anyone have any suggestions as to where to begin. I'd just like the back button function to be able to maybe check a started previous div name stored somewhere and load that.
thanks!
You can try using the History API. There are numerous tutorials on the web e.g. this one is quite good:
http://diveintohtml5.info/history.html
Basically this is how it works. When the user clicks the link for the div to show you push the state to the history stack.
history.pushState({<object with any information about state>}, pageTitle, newUrl);
This will push the state to the history stack meaning that when the user presses the back button on any modern browser like webkit it will take that state into consideration. When back action is taken it will then pop the state from the history stack. This action you have to listen to and handle in any way you see fit:
window.addEventListener("popstate", function(event) {
// event object contains the information from the pushed state
// do whatever needed to load the previous page here
});
The History API requires you to structure your code in a certain way for it to work well. For this I would recommend to use some existing framework that handle the back events for you e.g. Backbone.js. Hope this helps.