Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin subclassed UINavigationController with custom UINavigationBar

I'm trying to create a non-standard top navigation bar for use throughout my application. To achieve this I've been trying to subclass UINavigationController and UINavigationBar

I have a custom NavigationController class

partial class ZooNavigationController : UINavigationController
{
    public ZooNavigationController (IntPtr handle) : base (typeof(TopNavBar), null)
    {
        this.Handle = handle;
    }
}

which points to the base constructor

public UINavigationController (Type navigationBarType, Type toolbarType); 

for my custom UINavigationBar class TopNavBar which is something like...

public class TopNavBar : UINavigationBar
{
    public TopNavBar ()
    {
        InitCustom ();
    }

    public void InitCustom(){ 
        this.BackgroundColor = UIColor.Red; 
        // a bunch more custom stuff
    }
}

The problem is, TopNavBar is never called when I run this. If I try to adjust my constructor to look like this:

    public ZooNavigationController (IntPtr handle) : base (typeof(TopNavBar), null)
    {
        this.Handle = handle;
        TopNavBar test = (TopNavBar)this.NavigationBar;
    }

I get a runtime exception that it can't cast the types, so it seems that it's ignoring my call specifying the UINavigationBar type.

Can anyone help me out with what I'm missing here?

EDIT in the end it turns out I was missing the fact that you can set a custom UINavigationBar inside the storyboard. Combining that with the miguel's answer I ended up with the class

partial class TopNavBar : UINavigationBar
{
    public TopNavBar(IntPtr test) : base(test) {
    }

    [Export ("initWithCoder:")]
    public TopNavBar (NSCoder coder) : base (coder)  {
        InitCustom ();
    }
}

1 Answers

The IntPtr constructor is called in response to the object being created by Objective-C and surfaced to C#. This in general is not the way that these classes are instantiated.

The first question that you have to ask yourself is: who is creating the instance of your class?

  • You create the instance of this class from C#: you call a constructor with the proper parameters.
  • Being created during deserialization (for example, loading from a storyboard, XIB, or your own archived data), then you need to provide the constructor that takes an NSCoder parameter.
  • Having your instance recreated on demand (may indicate a problem, because it means that your object was destroyed, but Objective-C kept a reference to it, and now it is being resurfaced again), the IntPtr constructor.

In your example above, there is a mistake: you are overriding the IntPtr constructor, which should only ever call into the base class IntPtr constructor (since it means "I have a pointer to the real object in objective-c, create a wrapper for it").

My guess is that you are using C#, so in that case, what you want is to provide a new constructor that takes no arguments:

public ZooNavigationController () : base (typeof (YourNavigation), typeof(YourBar)) {
   // Your own initialization goes here
}
like image 124
miguel.de.icaza Avatar answered Sep 17 '26 23:09

miguel.de.icaza