3
votes

I'm trying to follow the standard approach to creating a custom UINavigationBar in order to change its background image, but have found an issue in the subclassing process. If I subclass UINavigationController, with the intent of overriding the virtual NavigationBar property to provide my own implementation, all navigation items (any left or right buttons, and the title view) disappear. At first I thought it was due to the background being rendered over top of the navigation items, but I can reproduce the problem with a no-op subclass.

It's reproducible with the following code:

[Register("NavigationBar")]
public class NavigationBar : UINavigationBar
{
    public NavigationBar () : base()
    {

    }

    public NavigationBar (NSCoder coder) : base(coder)
    {

    }

    public NavigationBar (IntPtr ptr) : base(ptr)
    {

    }

    public NavigationBar (NSObjectFlag t) : base(t)
    {

    }

    public NavigationBar (RectangleF frame) : base(frame)
    {

    }
}

[Register("NavigationController")]
public class NavigationController : UINavigationController 
{
    private UINavigationBar _navBar;

    public NavigationController () : base()
    {

    }

    public NavigationController (NSCoder coder) : base(coder)
    {

    }

    public NavigationController (IntPtr ptr) : base(ptr)
    {

    }

    public NavigationController (NSObjectFlag t) : base(t)
    {

    }

    public override UINavigationBar NavigationBar
    {
        get
        {
            if(_navBar == null) 
            {
                return base.NavigationBar;
            }

            return _navBar;
        }
    }

    public void SetNavigationBar(UINavigationBar navigationBar)
    {
        _navBar = (UINavigationBar)navigationBar;
    }
}

Now, all you need to do to lose your navigation items is to use the custom classes instead of the default ones:

var navigationBar = new NavigationBar();
navigationBar.BarStyle = UIBarStyle.Black;
navigationBar.TintColor = HeaderColor;

var navigationController = new NavigationController();
navigationController.SetNavigationBar(navigationBar);

// ...
4

4 Answers

1
votes

Well, your SetNavigationBar() method doesn't pass that down to the native base class and since you don't do any explicit drawing yourself, how is the native drawing code ever supposed to be invoked for your custom NavigationBar class?

In your example code, that NavigationBar is just floating around in space and never gets told to draw.

1
votes

In order to subclass UINavigationBar, you must define the IntPtr constructor in your derived class and instantiate the UINavigationController using the public UINavigationController(Type navigationBarType, Type toolbarType) constructor. Example:

public class MyNavigationBar: UINavigationBar
{
    public MyNavigationBar(IntPtr h) : base(h)
    {
    }

    // Do something.
}

....

var navController = new UINavigationController(typeof(MyNavigationBar), typeof(UIToolbar));

Took me a while to figure it out. More information on this page: http://developer.xamarin.com/guides/ios/platform_features/introduction_to_ios_6/ in section Subclassing UINavigationBar.

0
votes

Can you create a sample project where you're adding NavigationItems directly to a UINavigationController and then using the sub-classed UINavigationController/UINavigationBar causes these buttons to disappear?

Thanks,

ChrisNTR

0
votes

After a lot of research and back and forth with Xamarin, the answer to this problem is that you must use an IB stub file that is essentially no-op, but exists to shuttle the desired base type for your navigation elements. There is a working example on my OSS project: http://github.com/danielcrenna/artapp