Xamarin.Tips – iOS Bar Background Images in Xamarin.Forms

Xamarin.Forms doesn’t ship with any way to provide images as your background of either NavigationBars or TabBars. So in order to do this, we will have to build some custom renderers.

iOS

Morgan skinner has a great post here about an approach that he took to apply background images to his NavigationBars: http://www.morganskinner.com/2015/01/xamarin-formsusing-background-images-on.html

The give you the synopsis, you take your entire background image, crop it, and apply the cropped piece as the background of the bar in your renderer.

Here’s a look at how he cropped his image:
Original Marked Up_thumb[5]

I want to take a different approach here and do this programmatically so that you can be EXACT in the background of the bar so that it can match the background of your image.

Although we are approaching this as if you are applying the background image to be the same as your page background image, the same principle applies if you just want to set any image.

Here’s a renderer to use for a NavigationBar:

BackgroundImageNavigationRenderer.cs

[assembly: ExportRenderer(typeof(NavigationPage), typeof(BackgroundImageNavigationRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class BackgroundImageNavigationRenderer : NavigationRenderer
    {
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            View.BackgroundColor = UIColor.Clear;
            NavigationBar.BackgroundColor = UIColor.Clear;
            var image = UIImage.FromBundle("YOUR_PAGE_BACKGROUND_IMAGE.png");
            image = image.Crop(0,
                (int)(image.CGImage.Height - NavigationBar.Frame.Height),
                (int)image.CGImage.Width,
                (int)NavigationBar.Frame.Height);
            image = image.Scale(new CGSize(NavigationBar.Frame.Width, NavigationBar.Frame.Height));
            NavigationBar.BarTintColor = UIColor.FromPatternImage(image);
            NavigationBar.ShadowImage = new UIImage();
        }

    }
}

Breaking it down, we are taking the image that we use as the background for our entire page, then opening cropping it to the size of the NavigationBar itself.

I’ve also created an extension method called Crop so that we can reuse it here and for our UITabBar:

ImageExtensions.cs

    public static class ImageExtensions
    {
        public static UIImage Crop(this UIImage image, int x, int y, int width, int height)
        {
            var imgSize = image.Size;

            UIGraphics.BeginImageContext(new SizeF(width, height));
            var imgToCrop = UIGraphics.GetCurrentContext();

            var croppingRectangle = new CGRect(0, 0, width, height);
            imgToCrop.ClipToRect(croppingRectangle);

            var drawRectangle = new CGRect(-x, -y, imgSize.Width, imgSize.Height);

            image.Draw(drawRectangle);
            var croppedImg = UIGraphics.GetImageFromCurrentImageContext();

            UIGraphics.EndImageContext();
            return croppedImg;
        }

    }

Now, if you are not doing this to have a full background image, and just want some image for your UINavigationBar only, you can do so like this:

BackgroundImageNavigationRenderer.cs

[assembly: ExportRenderer(typeof(NavigationPage), typeof(BackgroundImageNavigationRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class BackgroundImageNavigationRenderer : NavigationRenderer
    {
        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            View.BackgroundColor = UIColor.Clear;
            NavigationBar.BackgroundColor = UIColor.Clear;
            var image = UIImage.FromBundle("YOUR_BAR_BACKGROUND_IMAGE.png");

            image = image.Scale(new CGSize(NavigationBar.Frame.Width, NavigationBar.Frame.Height));
            NavigationBar.BarTintColor = UIColor.FromPatternImage(image);
            NavigationBar.ShadowImage = new UIImage();
        }

    }
}

Essentially just cutting out the cropping process.

Also note that I set the NavigationBar.ShadowImage to a new UIImage();. Doing this gets rid of the line below your UINavigationBar. If you don’t want to remove the line, just don’t set the ShadowImage.

Now that we’ve done this for our UINavigationBar, we can apply the same thing to our UITabBars. So let’s create another custom renderer for that!

BackgroundImageTabbedPageRenderer.cs


[assembly: ExportRenderer(typeof(TabbedPage), typeof(BackgroundImageTabbedPageRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class BackgroundImageTabbedPageRenderer : TabbedRenderer
    {
        public override void ViewWillLayoutSubviews()
        {
            base.ViewWillLayoutSubviews();

            var image = UIImage.FromBundle("YOUR_PAGE_BACKGROUND_IMAGE.jpg");
            image =image.Crop(0,
                (int)(image.CGImage.Height - TabBar.Frame.Height),
                (int)image.CGImage.Width,
                (int)TabBar.Frame.Height);
            image = image.Scale(new CGSize(TabBar.Frame.Width, TabBar.Frame.Height));
            TabBar.BackgroundImage = image;
            TabBar.UnselectedItemTintColor = UIColor.FromRGBA(255, 255, 255, 150);
            TabBar.ShadowImage = new UIImage();
        }

    }
}

just as we did with our UINavigationBar, we crop the page background image to the location and dimensions of our UITabBar, and set the background image there. The same thing also applies with removing the ShadowImage.

And if you are not using a full background image, and just want an image for your bar, take this:

BackgroundImageTabbedPageRenderer.cs


[assembly: ExportRenderer(typeof(TabbedPage), typeof(BackgroundImageTabbedPageRenderer))]
namespace YOUR_IOS_NAMESPACE
{
    public class BackgroundImageTabbedPageRenderer : TabbedRenderer
    {
        public override void ViewWillLayoutSubviews()
        {
            base.ViewWillLayoutSubviews();

            var image = UIImage.FromBundle("YOUR_PAGE_BACKGROUND_IMAGE.jpg");

            image = image.Scale(new CGSize(TabBar.Frame.Width, TabBar.Frame.Height));
            TabBar.BackgroundImage = image;
            TabBar.UnselectedItemTintColor = UIColor.FromRGBA(255, 255, 255, 150);
            TabBar.ShadowImage = new UIImage();
        }

    }
}

And now you can use full page images over your NavigationBars and TabBars, or just set an image as the background for your bars!

 

Simulator Screen Shot Apr 7, 2017, 11.23.33 AM

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

6 thoughts on “Xamarin.Tips – iOS Bar Background Images in Xamarin.Forms”

  1. Hi Alex, thanks for sharing!
    Do you think that it’s possible to “hack” your code to achieve something like this:

    In my case, I don’t have a “global” background Image, but an Image (or other content) that is displayed in a Grid. And I would like that the NavigationBar is displayed above the content of the page, but with opacity.

    Like

    1. This solution wouldn’t necessarily work for this. What I would suggest is building a native ViewController and Activity, then using the Xamarin.Forms embedding to embed. This means that your ContentPage won’t be able to control the NavigationBar since the native control will handle it, but that will give you the native freedom to render a transparent nav bar over the content of your xamarin.forms content page. I’m working on a blog post similar to this actually

      Like

      1. Your approach seems to be interesting. However that mean that it would be necessary to create the same thing on each platform, isn’t it?
        The app is B2B and will work on Android, UWP and iOS, even if today the priority is focused on Android.

        Like

Leave a comment