ViewModel is null during ViewDidLoad - ios

I am getting started with MvvmCross in iOS.
public class MainView : MvxTabBarViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
var vm = (MainViewModel)this.ViewModel;
if (vm == null)
return;
}
}
Setting a breakpoint to the line where access the ViewModel, shows me, that ViewModel is null.
I can workaround this by calling ViewDidLoad() in the constructor. Then, ViewModel is null during the constructor call, but valid in the default ViewDidLoad call. But that looks like a workaround. can anybody help?

I'm guessing here the problem here will be specific to the way that TabBarViewController is constructed.
ViewDidLoad is a virtual method and it is called the first time the View is accessed.
In the case of TabBarViewController this happens during the iOS base View constructor - i.e. it occurs before the class itself has had its constructor called.
The only way around this I've found is to add a check against the situation in ViewDidLoad, and to make a second call to ViewDidLoad during the class constructor.
You can see this in action N-25 - https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/976ede3aafd3a7c6e06717ee48a9a45f08eedcd0/N-25-Tabbed/Tabbed.Touch/Views/FirstView.cs#L17
Something like:
public class MainView : MvxTabBarViewController
{
private bool _constructed;
public MainView()
{
_constructed = true;
// need this additional call to ViewDidLoad because UIkit creates the view before the C# hierarchy has been constructed
ViewDidLoad();
}
public override void ViewDidLoad()
{
if (!_constructed)
return;
base.ViewDidLoad();
var vm = (MainViewModel)this.ViewModel;
if (vm == null)
return;
}
}

Related

UITableViewDelegate disables itemselected

I am trying to make a simple change to a tableview for IOS. As my project is using Xamarin forms I use a custom renderer.
The changes I want to make is to close the keyboard is the table is scrolled, simple as that.
When I make the following changes it will work for my new scrolled override event but stops item (cell) selected from triggering (some of my cells navigate away from the page) and must be tapped.
public class CustomTableRenderer : TableViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
{
base.OnElementChanged(e);
if (Control == null)
return;
var tableView = Control as UITableView;
TableDelegate tableDelegate;
tableDelegate = new TableDelegate();
tableView.Delegate = tableDelegate;
}
}
public class TableDelegate : UITableViewDelegate
{
#region Constructors
public TableDelegate()
{
}
public TableDelegate(IntPtr handle) : base(handle)
{
}
public TableDelegate(NSObjectFlag t) : base(t)
{
}
#endregion
#region Override Methods
public override void Scrolled(UIScrollView scrollView)
{
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
//base.Scrolled(scrollView);
}
#endregion
}
I have tried changing the UITableViewDelegate to UITableViewController but seems to cause more problems.
Adding in the override for all other methods calling base also does not seem to help. Not sure what I am missing.
Any idea why this is caused?
Note: If more information is needed please comment to let me know. Same with if the question is not clear. Comments would help.

Monotouch - tableview inside UIViewController: override functions don't get called

I have a tableview inside a UIViewController. I also have some dynamic data, which I would like to load onto this tableview. I have attempted two approaches so far:
I created a nested private class inside this UIViewController, which inherits from UITableViewSource. I then made a new instance of this class in ViewDidLoad and assigned it to my table view's Source property.
For this option, the override functions are called. However,I'm loading the lists dynamically in the ViewWillAppear, so the data source at this point is empty.
I tried creating a new instance of the nested class in the ViewWillAppear, but the override functions aren't called when I do this. They're only called if I do it in theViewDidLoad.
The other option I tried was implementing the IUITableViewDelegate and the IUITableViewDataSource in my UIViewController class. Then in my ViewDidLoad assigned "this" to both the WeakDelegate and WeakDataSource properties of my table view.
The challenge I'm facing with this second option is : I keep getting this error:
[HomePageController tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x185ce440
I've added this export attribute to the function, but it doesn't solve the issue:
[Export ("tableView:numberOfRowsInSection:")]
public nint RowsInSection (UITableView tableView, nint section)
{
return 1;
}
I prefer the first option, but why can't I initialize the class in the ViewWillAppear after loading my data? I need to call it here, since the ViewDidLoad is called only once.
This is what I've done:
public async override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
try {
LoadingOverlay loadingOverlay = new LoadingOverlay (UIScreen.MainScreen.Bounds, "Loading...");
this.View.AddSubview (loadingOverlay);
await FetchAllData ();
loadingOverlay.Hide ();
if (tableViewNotice != null) {
if(allData != null)
{
if(allData.Count > 0)
{
homeDataSource = new HomeDataSource(this);
tableViewNotice.Source = homeDataSource;
}
}
}
} catch (Exception ex) {
Console.WriteLine (ex.Message + ex.StackTrace);
}
}
When UITableView be loaded at draw to the layer,it will load datas just like when viewDidLoad has been called. But the tableView's datasource is set in viewWillAppear, that's too late. so you need call reloadData() after you set datasource or set datasource in viewDidLoad function.

MvvmCross: RaisePropertyChanged not updating binding

I am instantiating a view with a ViewModel like this:
var myView = new MyView { DataContext = new MyViewModel() };
I want to make this view accessible from anywhere in the application so I am adding the view to the rootviewcontroller:
window.RootViewController.View.Add(myView.View);
Where ViewModel inherits from MvxViewModel and MyView inherist from MvxViewController
Inside the MyView I am binding a UILabel to a text property like this
this.CreateBinding(myLabel).To<MyViewModel>(vm => vm.MyTextProp).Apply();
The text property is defined inside the ViewModel like this
private string myTextProp;
public string MyTextProp
{
get { return myTextProp; }
set
{
myTextProp = value;
RaisePropertyChanged(() => MyTextProp);
}
}
The binding works initially when loaded. But when I change the MyTextProp property and RaisePropertyChanged is called the UILabel is not being updated.
I am also binding to an ICommand which works fine and triggers normally.
Instead of doing var myView = new MyView { DataContext = new MyViewModel() };
Let MvvmCross construct your MvxViewController by doing this:
var viewController = this.CreateViewControllerFor<MyViewModel>();
CreateViewController is an extensions method for IMvxCanCreateTouchView, so the class where you do your view construction should be implementing that Inteface, otherwise that method will not be available.
I know IMvxCanCreateTouchView is implemented by MvxTouchViewPresenter and MvxViewController so you can call that method from your Presenter or from another MxvViewController.

MonoTouch Instantiating a ViewController programmatically for ContainerView

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

trouble getting change a property of an outlet in a any new method

I'm having trouble getting change a property of an
Outlet in a new method, for example:
public partial class ViewApresentacao : UIViewController
{
...
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
//my ImageView
imgModelo.Image = UIImage.FromBundle ("Image/car.png");
}
//new method
public void Test(string caminho)
{
imgModelo.Image = UIImage.FromBundle (caminho);
}
....
}
At viewDidLoad functions normally, but the second
method Test did not, wanted to use it
to update when a selected row in a table, it's all
ok, but when I call the method Test, the error
"object reference not set to an instance of an object"
appears well in line of the Outlet.
Problem solved, I was in my main class with different instances, with the help Xamarin team, managed to solve my problem.

Resources