Getting System.InvalidCastException while loading View from a xib file

AnaghSharmaAnaghSharma ✭✭USMember ✭✭

I have a xib file - LoaderView.xib and associated class LoaderView.cs which contains a view for showing loading text. When I try to load it in the main ViewController as -

public override void ViewDidAppear()
{
   base.ViewDidAppear();

   NSBundle.MainBundle.LoadNibNamed("LoaderView", this, out NSArray arr);
   var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));
   view.Frame = View.Frame;
   View.AddSubview(view);
}

I am getting the System.InvalidCastException in this line -

var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));

And the strange part is I do not get the exception every time. When it works, I am seeing the loading text overlapped with default view - associated with the ViewController.

Can someone point out what is the best way to show Loading, Error and Empty states. Should I have separate ViewControllers for them? Or Can I have separate xib files which I can load/unload from the main View Controller? How to load a view from a xib file?

Tagged:

Best Answer

Answers

  • ChrisHamonsChrisHamons Xamurai USForum Administrator, Xamarin Team Xamurai

    Maybe you have a race condition? Does LoadNibNamed return sometimes before item is in memory?

    If you want your Window's view to change based upon the loaded controller, you'll likely have to remove the other views from the tree (call RemoveFromSuperview on the view) so you don't see overlap (you have two sets of view in your window tree).

    If you want just text however, you can just skip having 3 view hierarchies and just do something like:

        NSTextField field;
        public async override void ViewDidLoad()
        {
            base.ViewDidLoad ();
    
            // This really should use autolayout but it's an example
            field = new NSTextField (new CGRect (100, 100, 100, 50))
            {
                StringValue = "Loading...",
                Bezeled = false,
                Editable = false,
                Selectable = false
            };
            View.AddSubview (field);
    
            await Task.Delay (2000);
            field.StringValue = "Done...";
            await Task.Delay (1000);
            field.RemoveFromSuperview ();
        }
    
  • AnaghSharmaAnaghSharma ✭✭ USMember ✭✭

    @ChrisHamons Pardon me, but before WHAT item is in memory?

    1. I believe the problem lies within arr. I checked its value after setting breakpoint
      a. When it works -

      b. When it does not and I get an InvalidCastException

      Why is the array not holding the values in the same sequence every time?

    2. I don't want just text. What I want is to show various states - Loading, Empty, Error (which will also have a retry button) but I don't know if having them in different xib file is a good idea or should I have separate View Controllers for them. Can't call RemoveFromSuperView from View because that is the superview (Tried it and I am getting a blank view instead of overlapped text). The app is a menu bar app and hence it does not have any Window/WindowController but only ViewController.

    Thanks.

  • ChrisHamonsChrisHamons Xamurai USForum Administrator, Xamarin Team Xamurai

    Looking at the API, ignore my previous comment on race conditions. I was wondering if there was some race condition between loading the nib and surfacing it to managed land. However, since we're just looking at the out NSArray, it should be synchronous.

    It appears Cocoa is not returning a stable array, as you noted the order is reversed in some cases. Maybe they are doing some multithreaded loading? In any case, you need to walk the array looking for your type, not assuming it is item zero. I do not know the reason why, as that's all implemented in Apple's Cocoa libraries.

    Different xib / in one storyboard with view controllers / generated from code for the various views is a matter of opinion. Off the top of my head the tradeoffs are:

    Different xib:

    + Separate files make multiple people colliding when making changes to a view less likely
    + Easier to find a specific view
    - Non-trivial to load up each xib / keeping track of X files / views
    

    Storyboard:

    + One place to make changes / see all views
    + Easy to grab a certain view controller by name
    - Easy to collide when multiple people on a large team makes changes
    

    Code:

    + Easy to debug as you can step through your logic in a debugger
    + Trivial to resolve merge conflicts 
    - Setting up complicated UI / layout / hierarchy can be difficult.
    - Can be difficult to setup some controls as IB has a lot of "defaults" that creating a View by hand do not get
    

    If you forced me to make a choice, I'd shove everything in a storyboard if your UI is complicated or do it in code if it is easy. Storyboards are the more "modern" approach and what Apple is pushing.

  • AnaghSharmaAnaghSharma ✭✭ USMember ✭✭

    Hi @ChrisHamons , thank you very much for the answer.

    1. I tried iterating the NSArray but could not find any method which returns the object of my type (LoaderView). Will have to go through the documentation once more I guess.
    2. What could possibly solve the problem of overlapping while working with xib? Can't call RemoveFromSuperView from View because that is the superview. Tried it and I am getting a blank view (nothing at all) instead of overlapped text. Maybe I should give the subview a background?
Sign In or Register to comment.