I have xamarin ios app, with Tab Bar.
When I run main tab, I call methods
Here is code of main tab
public partial class ExperienceTableViewController : UIViewController
{
public ExperienceTableViewController (IntPtr handle) : base (handle)
{
}
public override async void ViewDidLoad()
{
base.ViewDidLoad();
List<GettingExperiences.Experience> tableItems = new List<GettingExperiences.Experience>();
tableItems.Clear();
tableItems = await ExperienceMethods.GetExperiencesAsync();
ExperienceTableView.Source = new ExperienceSource(tableItems, this, this.NavigationController);
ExperienceTableView.RowHeight = UITableView.AutomaticDimension;
ExperienceTableView.EstimatedRowHeight = 80f;
ExperienceTableView.ReloadData();
}
}
When I change filters in another tab, I need to get back and rerun this method - tableItems = await ExperienceMethods.GetExperiencesAsync();
I make redirect like this
this.TabBarController.SelectedIndex = 0; from another tab, but my method not reruns.
How I can solve this problem?
I found solution for this problem
just run method in ViewDidAppear() or using events
ViewDidLoad is only run once, First time view is loaded. After next time ViewWillAppear(), ViewDidAppear() will be executed. As View is already there in tab bar controller.
So If your ViewController is part of Tab bar you should put your always executing code in ViewWillAppear() and ViewDidAppear(). And code which only is needed to be run once should e placed in ViewDidLoad().
Related
I'm developing an iOS application using Xamarin.iOS (Code only, no storyboards) and I wonder what the best way to send data back to the original uiviewcontroller when I pop from the navigationcontroller.
In android I use StartActivityForResult and then override OnResult, but I can't find a similar way for iOS.
I know there's overrides for ViewDidLoad, ViewDidAppear, etc, what I'm looking for is some kind of ViewDidGetPoppedBackTo (hope you get it).
Or is there another better way to achieve this?
NavigationController keeps track of all the ViewControllers as an array: NavigationController.ViewControllers
You can get an existing instance of the ViewController Type from this array via following code:
(You may write this method in BaseViewController if you have it.)
public T InstanceFromNavigationStack<T> () where T : UIViewController
{
return (T)NavigationController.ViewControllers.FirstOrDefault(v => v is T);
}
Then use it like :
var myVCInstance = InstanceFromNavigationStack<MyTargetViewController>();
if(myVCInstance != null)
{
//Assign a value like
myVCInstance.MyVariable = "MyValue";
//Or call a method like
myVCInstance.MethodToReloadView("MyValue")
}
//Go Back Navigation Code
//Then here write your navigation logic to go back.
This not only helps passing data in Previous ViewController, but Any ViewController in the stack. Simply pass the Type of it to get an Instance from Stack.
NOTE: This should work if your Navigation stack doesn't have multiple instance of the same ViewController Type.
Use this way
ViewController viewController = (ViewController)NavigationController.TopViewController;
viewController.SendData(myevent);
Create method SendData in your ToViewController this method is called first when navigationg back and your data send to your previous ViewController.
Another option that I've started using is EventHandler methods. Here is an example I use to populate a UITextField in the parent view controller with the selection from a UITableView (child view controller) and then close the child.
Define the EventHandler method in your parent view controller:
void LocationLookup_OnSelected(object sender, EventArgs e)
{
chosenLocation = (MKPlacemark)sender;
planLocation.Text = chosenLocation.Name;
this.ParentViewController.DismissViewController(true, null);
}
Pass the EventHandler method from the parent as a property of the child.
public partial class LocationLookupViewController : UITableViewController
{
private event EventHandler OnSelected;
public LocationLookupViewController(EventHandler OnSelected)
{
this.OnSelected = OnSelected;
}
...
}
Call the EventHandler passing in the object / data that you need in the parent
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
...
OnSelected(response?.MapItems[0].Placemark, new EventArgs());
}
Note - The above class and function are incomplete but should provide you with an idea of how this technique works.
I have used IMvXModelTouchView for a custom popup screen animation. And, I have a close button on this popup view. What is the proper way to switch back to a previous view?
Here is my code look like:
public class PopupView
: MvxViewController, IMvxModalTouchView
{
public PopupView()
{
ModalPresentationStyle = UIModalPresentationStyle.PageSheet;
}
public override void ViewDidLoad()
{
Title = "Map";
base.ViewDidLoad();
var closeButton = new UIButton(new RectangleF(0, 0, 50, 30));
closeButton.TouchUpInside += CloseButtonClicked();
Add(closeButton);
}
private EventHandler CloseButtonClicked()
{
return (sender, args) => NavigationController.DismissViewController(true, null);
}
}
It worked, the first time when I click this close button, but it crash when I tried to pop up this view again.
I suspect you are probably using the standard MvxModalSupportTouchViewPresenter which only expects one modal view/viewModel to be displayed at a time, and which expects that view/viewModel to be cleared using Close(this) from the ViewModel.
See: MvxModalSupportTouchViewPresenter.cs#L29
If you have strong ideas about your UI, then (in my opinion) your best bet is to write your own custom presenter - then you can open/close/hide/show whatever you want. For more on writing custom presenters, see some of the links and videos from: http://slodge.blogspot.com/2013/06/presenter-roundup.html
I am trying to work with a container view in MonoTouch and I am following some tutorials online. They talk about adding and removing view controller programmatically from the container. I created a viewcontroller and view in the storyboard of my project and attached a few outlets and one action (for labels and buttons respectively). I created an overloaded construc
Here is the code in the view controller that I am trying to add viewControllers into the container view.
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
ContainerView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
_controllerOne = new IngredientsController("Perishables");
_controllerTwo = new IngredientsController("Spices");
AddChildViewController(_controllerOne);
ContainerView.AddSubview(_controllerOne.View);
_controllerOne.DidMoveToParentViewController(this)
}
When I add the subview for _controllerOne I get an error because the elements on my controller are marked null. Is MonoTouch incapable of having view controllers being programmatically created if the controller was made in Interface Builder? Below are the two constructors for the Ingredient Controller. When the segue is used then all of the UI controls are initialized properly. Do I need to create the controller programmatically and then instantiate it that way? Any help would be appreciated.
//This ctor does not work
public IngredientsController (string title) : base(NSObjectFlag.Empty)
{
_ingredientTitle = title;
}
//This ctor works
public IngredientsController (IntPtr handle) : base (handle)
{
}
Try to swap the AddSubView() and DidMoveToParentViewController() methods like below:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
ContainerView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
_controllerOne = new IngredientsController("Perishables");
_controllerTwo = new IngredientsController("Spices");
this.AddChildViewController(_controllerOne); // Root child controller.
_controllerOne.DidMoveToParentViewController(this); // Confirm the rooting.
ContainerView.AddSubview(_controllerOne.View); // Access the view.
}
Try instantiating the view controller like this:
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
this.ContainerView.AutoresizingMask = UIViewAutoresizing.FlexibleHeight;
var newController = this.Storyboard.InstantiateViewController("IngredientsController");
this.AddChildViewController (newController);
this.ContainerView.AddSubview (mapController.View);
}
Make sure you set the Storyboard Id in the properties panel for the ViewController
I'm having a problem going back again to my first Apps VC, the first "screen/ViewController)" is a login screen, then I call a UITabbar with their respective ViewControllers, when I'm on a certain level of a ViewController (for example the 5th one) I want to get a "Logout" behavior in my App and get back to the first ViewController (Login). But I think I can only Navigate back to the first ViewController of my Tabbar control. I was trying with this methods of my VC:
this.NavigationController.PopToViewController(previousVC,true);
or
this.NavigationController.PopToRootViewController(true);
Any help would be appreciated.
(If you need more details with the code please tell me about it)
//Call to the tab bar from the login viewcontroller
mainTBC = new TabBarMenuPpal ();
this.NavigationController.PushViewController (mainTBC, true);
…
//Tabbarmenuppal with a set of navcontrollers
public TabBarMenuPpal ()
{
var customerVC = new Customers ();
navCustomers = new UINavigationController ();
navCustomers.PushViewController (customerVC, true);
navCustomers.TopViewController.Title = customersTitle;
navCustomers.TabBarItem = new UITabBarItem (customersTitle, UIImage.FromFile ("Utility/Images/Cust-30x30.png"), 0);
…
mainVC = new UIViewController[]
{
navCustomers
, navSettings
} ;
this.ViewControllers = mainVC;
}
The problem is that when I start to navigate in the navCustomers I can't find a way to go back to the login VC (deleting my tab bar and releasing all their resources). (Sorry about my English).
I don't know if it's the best solution but I have implemented a "GoToLogin" method in my UITabBarController which only does this:
public void GoToLogin()
{
this.NavigationController.PopToRootViewController(true);
}
So when I need to go to my first page I only do this:
if(MainDialog.NavigationController.TabBarController != null)//loaded from the tab bar
((TabBarMenuPpal)MainDialog.NavigationController.TabBarController).GoToLogin();
else//loaded from the login screen
MainDialog.NavigationController.PopToRootViewController(true);
As title said, I want to show another UIViewController from an existing UIViewController which is hosted in UIPopoverController. I tried the following method:
_secondViewController = new SecondViewController();
this.ModalPresentationStyle = UIModelPresentationStyle.CurrentContext;
this.ModelInPopover = true;
this.PresentModelViewController(_secondViewController, true);
However, the secondViewController is shown in the main view controller, instead of the popover controller.
In this post somebody mentions that it cannot be done and it violates the HIG. However, I have seen this in other apps (e.g. Yahoo! Email) if I'm not mistaken.
I'm also thinking about another approach: If I could create a UINavigationController within the popover context, it might work by just adding new ViewController to the NavigationController. But how?
Remember that UINavigationController derives from UIViewController.
So, you can use the controller contained within UIPopover just like any other container... in this case it's best to use UINavigationController inside UIPopover to display ViewControllers.
Usage:
var _NavController = new NavController();
Popover = new UIPopoverController(_NavController);
Popover.PopoverContentSize = new SizeF(..., ...);
Popover.PresentFromRect(...);
NavController:
public class NavController : UINavigationController
{
UIViewController _FirstViewController;
UIViewController _SecondViewController;
public NavController()
: base()
{
}
public override void LoadView()
{
base.LoadView();
_FirstViewController = new UIViewController();
// Initialize your originating View Controller here.
// Only view related init goes here, do everything else in ViewDidLoad()
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// When a button inside the first ViewController is clicked
// The Second ViewController is shown in the stack.
_FirstViewController.NavButton.TouchUpInside += delegate {
PushSecondViewController();
};
this.PushViewController(_FirstViewController, true);
}
public void PushSecondViewController()
{
_SecondViewController = new UIViewController();
this.PushViewController(_SecondViewController, true);
}
}