How to go back to previous ViewController in Xamarin.IOS - c#

I make an app in VisualStudio Xamarin (not Xamarin.Forms) with two view controller. First view controller is list of items, second contain item's detail info. When user tap item in list, second view opens with code
DetailViewController detailController = this.Storyboard.InstantiateViewController("DetailViewController") as DetailViewController;
detailController._idx = idx;
this.NavigationController.PushViewController(detailController, true);
But I don't know how to go back to first ViewController programmatically - for example there are button "Go Back" on DetailViewController
I try to use this code:
backButton.TouchUpInside += (s,e) => {
this.NavigationController.DismissViewController(true, null)
};
But it doesn't give any result.
Can anybody help me with this?
UPDATED
I changed code to:
backButton.TouchUpInside += (s,e) => {
this.NavigationController.DismissViewController(true, async () => { await DismissViewControllerAsync(true); });
ListViewController listController = this.Storyboard.InstantiateViewController("ListViewController ") as ListViewController;
this.NavigationController.PresentViewController(listController, true, null);
}
This work for "Go back", but when I try to choose same or another item in list and open new DetailViewController, it throw exception "System.NullReferenceException: Object reference not set to an instance of an object" on
this.NavigationController.PushViewController(detailController, true);
(In details - I run app, ListViewController opens, I choose item1, DetailViewController opens, I tap BackButton, ListViewController opens, I chhose any item, Exception)

You got Push,Pop and Present,Dismiss mixed up.
When the viewcontroller is put inside Navigation, you should use push and pop to control the stack.
Modify your code :
backButton.TouchUpInside += (s,e) => {
this.NavigationController.PopViewController(true);
}
Refer to
Presenting a View Controller
UINavigationController

If you need a "back" butten to apear automatically then you need to start with a navigation controller, and from there open a viewcontroller...
See more at this link:
https://developer.xamarin.com/guides/ios/getting_started/hello,_iOS_multiscreen/hello,_iOS_multiscreen_quickstart/
otherwise you need to make a butten and simply just use the samee method as before!
Ask if you don't understand it!

Related

How do I navigate to another page from another c# class

I have a main page which has a play, options and exit button, both the play and exit button work as I followed a tutorial on it however for the options button I do not know how to navigate it. What I am planning to do is to create another class called optionsMenu.cs and have like a credits screen or a how to play guide on it.
These are the codes that I have for my options button.
var optionsGameButton = new Button(buttonTexture, buttonFont)
{
Position = new Vector2(300, 250),
Text = "Options",
};
optionsGameButton.Click += OptionsGameButton_Click;
_components = new List<Component>()
{
titleGameButton,
playGameButton,
optionsGameButton,
exitGameButton,
};
Finally I know I have to write a code in this area to be able to get the options button working but I don't know which kind of code to put.
private void OptionsGameButton_Click(object sender, EventArgs e)
{
Console.WriteLine("Credits");
}
If you're set on using click events, then you'll need to create a new instance of your options page and set it as the content of your control object within OptionsGameButton_Click. On the options page you'll need to set the xmlns to your optionsMenu.cs file.
I think you could also set the content of your control object by passing it as a parameter within the method itself, but I've only done this using the ICommand interface.

How to replace a specific view in iCarousel on tap/select?

I'm using iCarousel by #Nick Lookwood to load a scrollable list of "flashcards".
Each View of the iCarousel control is one flashcard. By design, I require that when a user taps the flashcard (view), the flashcard flips to reveal the behind. Before using iCarousel, I was making two separate controls, one for front and one for back, and then using UIView.Transition (with a nice Flip From Top animation) to go from front to back when a Tap was detected, or the other way round.
Adding a UITapGestureRecognizer to my View is leading to weird artifacts and not functioning as expected (overlapping controls, the next one instead of the current one flipping, no animation, etc.) and I need a different approach. I could conveniently use the Selected event in the iCarousel Delegate instead of a Tap Gesture Recognizer, but what do I do there exactly?
In essence, I would like to replace the specific view which was tapped with another but I feel this is conflicting with the whole reusable views idea. Is there nothing I can do? (Once the view is out of the screen, I am fine with it being "flipped forward" again.)
Thanks!
p
P.S. I'm using C# and Xamarin.iOS, but I can understand Obj-C and Swift code fairly well, so any help will be appreciated.
There are lots of example for iCarausal. I assume you are using this :
iCarausal
and specifically folder name "Basic iOS Example"
How about the below changes in delegate methods:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
UILabel *label = nil;
//create new view if no view is available for recycling
if (view == nil)
{
//don't do anything specific to the index within
//this `if (view == nil) {...}` statement because the view will be
//recycled and used with other index values later
view = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200.0f, 200.0f)];
((UIImageView *)view).image = [UIImage imageNamed:#"page.png"];
view.contentMode = UIViewContentModeCenter;
//back view i have just added this apart from it there is no change made by me in this method
UIView *backView = [[UIView alloc] initWithFrame:view.bounds];
backView.tag = 2;
[backView setBackgroundColor:[UIColor redColor]];
[view addSubview:backView];
[backView setHidden:YES];
//back view
label = [[UILabel alloc] initWithFrame:view.bounds];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
label.font = [label.font fontWithSize:50];
label.tag = 1;
[view addSubview:label];
}
else
{
//get a reference to the label in the recycled view
label = (UILabel *)[view viewWithTag:1];
}
//set item label
//remember to always set any properties of your carousel item
//views outside of the `if (view == nil) {...}` check otherwise
//you'll get weird issues with carousel item content appearing
//in the wrong place in the carousel
label.text = [_items[index] stringValue];
return view;
}
and implementing didSelect delegate as below:
- (void)carousel:(iCarousel *)carousel didSelectItemAtIndex:(NSInteger)index{
UIView *frontview = [carousel itemViewAtIndex:index];
UIView *backView = [frontview viewWithTag:2];
BOOL isInFront = NO;
if (backView.hidden == NO) {
isInFront = YES;
}
[UIView transitionWithView: isInFront ? backView : frontview
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[backView setHidden:isInFront];
}
completion:nil];
}
You should write your code more structured, it was an example to show you that you can acheive what you want with iCarausal.

Sync Navigation Drawer menu on Back stack

I followed the https://github.com/jamesmontemagno/Xam.NavDrawer example and Im able to successfully use the drawer layout with fragments(infact nested fragments with view pager). I have one issue, when I click the back button the navigation drawer menu item on the left side is not synced with the fragment that is shown.
This is how I navigate to other fragments
SupportFragmentManager.BeginTransaction().Replace(Resource.Id.content, fragment).AddToBackStack(fragment.Name).Commit();
I tried the OnAttachFragment method, but its not called on back stack. I also tried the SupportFragmentManager BackStackChanged method, but I could not get the current fragment that is in the view to update the navigation drawer menu title.
I had the same issue and couldn't find any solution as well. Although this question is kinda old, my solution may help someone. I'm not sure if it's the best solution but it works for me.
So first you need to add variable to store ID of previously checked item:
private int previousItemChecked;
set it initially to your default checked item:
if (savedInstanceState == null) {
selectItem(0);
previousItemChecked = 0;
}
then edit the fragment transaction so that the transaction title in backstack contains position of the previously checked item converted to string and after the transaction is done set the variable previousItemChecked to the currently checked item id:
fragmentManager.beginTransaction().replace(R.id.content_frame, selectedFragment).addToBackStack(""+previousItemChecked).commit();
previousItemChecked = mDrawerList.getCheckedItemPosition();
and finally in method onBackPressed you need to get the string previously assigned to fragment transaction, parse it to int and update the drawer according to the obtained id:
#Override
public void onBackPressed() {
if(fragmentManager.getBackStackEntryCount() > 0) {
String title = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-1).getName();
int pos = Integer.parseInt(title);
previousItemChecked = pos;
mDrawerList.setItemChecked(pos, true);
}
super.onBackPressed();
}
I took the code from my app created in Android Studio so it isn't for Xamarin but if you update the code it should work with it too. What's important here is the idea how it's done. I hope the answer is understandable and will help someone.
I had the same issue and I solved like this:
Inside selectItem we are passing the position Item;
So if position is 0 (or whatever is fragment we want it appears as first indipendently from it's position on the menu) we have to avoid to save the first transaction. So...
private void selectItem(position){
//...
if (position != 0)
{
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment)
.AddToBackStack(fragment.Name)
.Commit();
}
else
{
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment)
.Commit();
}
}
Managed to achieve this by injecting a DestinationChangedListener into the NavController like this:
NavController navController.addOnDestinationChangedListener(this);
and then:
#Override
public void onDestinationChanged(#NonNull NavController controller,
#NonNull NavDestination destination,
#Nullable Bundle arguments) {
NavigationView navigationView = findViewById(R.id.nav_view);
if(navigationView != null){
navigationView.setCheckedItem(destination.getId());
}
}

ProgressHUD and TouchUpInside

I have my ViewController like
public partial class TestView
: MvxViewController
{
...code here...
}
and i load my next ViewController on button event TouchUpInside like that:
btnSearch.TouchUpInside += (object sender, EventArgs e) => {
BTProgressHUD.Show("test");
ViewModel.GoParameterizedCommand.Execute(null);
};
that event it's defined in ViewDidLoad. My "test" message it's showed on next ViewController and not during loading of this one. How could i show that message during loading and not when next ViewController is loaded? I have tried to use also MBProgressHUD
btnSearch.TouchUpInside += (object sender, EventArgs e) => {
var hud = new MTMBProgressHUD (View) {
LabelText = "Waiting...",
RemoveFromSuperViewOnHide = true
};
View.AddSubview(hud);
hud.Show (animated: true);
ViewModel.GoParameterizedCommand.Execute(null);
};
but behaviour it's same.
It seems that you are trying to work with ProgressHUD in View layer. In MVVM-way you should create a "Loading" property in your ViewModel and bind it to progressbar in View.
Here is a good Stuart's video how to do that: http://youtu.be/6fNXGErr9to
And here is sample code: https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/tree/master/N-34-Progress
I am doing something similar in one of my apps.
I have a IsLoading property in my ViewModel which I set whether something is loading and not. Then in my ViewController I am subscribing to changes on Loading like this:
ViewModel.WeakSubscribe(() => ViewModel.IsLoading, (sender, args) =>
{
if (ViewModel.IsLoading)
{
ShowLoadingDialog("Loading Resource");
}
else
{
DismissLoadingDialog();
}
});
void ShowLoadingDialog(string text)
{
DismissLoadingDialog();
BTProgressHUD.Show(text, -1, BTProgressHUD.MaskType.Black);
}
void DismissLoadingDialog()
{
if (BTProgressHUD.IsVisible)
BTProgressHUD.Dismiss();
}
I do this in ViewDidLoad(). You could do something similar in your first View, then in your second you could just call DismissLoadingDialog() or simply make sure you call ViewWillDisappear() or ViewDidDisappear(), in the first ViewModel, so it gets dismissed correctly.
Loading view controller will not take more then 'Micro Seconds'.
Loading data in the view controller will need some time and during that time you need to show that ProgressHUD.
If you are loading data in 'First View Controller' then at start loading data start ProgressHud with BTProgressHUD.Show("test"); and when your data is done loading remove that HUD from the view just before navigating to the 'Second View controller'.
And if, you are loading data in 'Second View Controller', then Do navigation first from 'First View Controller' to 'Second View Controller' and then show HUD just before loading data in Second view controller BTProgressHUD.Show("test"); and remove it after data is loaded.

Is it possible to create a dropdown list (not menu) of hyperlinks

nyone know if its possible to create a dropdown list of hyperlinks. So besides the hyperlink replacing the text field, there's also a value for each item in the list. Wondering if there's any jquery or other client side script that will let me turn my list item names into links. Using MVC2 as serverside.
Ultimately clicking on any link in the dropdown list will open a new window, this is so people can not only select a product variant but also view the details of the selected product variant in a pop-up window before submitting the form.
The hyperlink would be constructed from the items value, which is the productID and the URL that will open in a new window will just pass that as a perimeter to an action method.
Currently using this script to do the job, but I have to employ a button next to
the drop down list and its kinda ugly and confusing as you won't write too much on
a button.
function JumpToIt1(frm) {
var newPage = frm.droppa.options[frm.droppa.selectedIndex].value
if (newPage != "None") {
window.open("http://mydomain.com/category/" + newPage, "mywindow", "menubar=1,resizable=1,width=550,height=250");
}
}
$(function() {
$('#dropdownId').change(function() {
var page = $(this).val();
if (page != "None") {
window.open("http://mydomain.com/category/" + page, "mywindow", "menubar=1,resizable=1,width=550,height=250");
}
});
});
Be aware though that the new window will only open when the user selects a different value from the currently selected one. So if you remove your button the user will have no way of opening the window twice in a row without first selecting a different value.
You can have a dropdown with an onChange handler that causes it to run JavaScript whenever the dropdown changes (ie someone selects something out of the list). I think that will do what you want.
try this:
function openPopup(newPage){
if (newPage != "None") {
window.open("http://mydomain.com/category/" + newPage, "mywindow", "menubar=1,resizable=1,width=550,height=250");
}
}
$(#dropdownId").find("option").each(function(){
var $Obj= $(this);
$(this).text("<a href='javascript:void(0);' onclick='openPopup("+$Obj.val()+")'>" + $Obj.text() +"</a>");
});

Categories

Resources