Advanced custom styling in Xamarin.iOS

WilliamThomasWilliamThomas USMember ✭✭

I was wondering what my options are when it comes to doing advanced styling in iOS applications. I'm referring to things such as custom navigation bars (no gloss, my own images, etc.), custom tab bars, custom search boxes, button colors and shapes, etc. At some root level, provided I have PNG files for everything, is all of this possible?

I'm assuming that, at least for things like navigation bars and tab bars, doing flat styles may require me to simulate the standard actions using my own custom buttons and whatnot.

Has anybody tried to do things like this? The application I've been assigned to develop is very custom-style heavy and I don't have much room to move on this.

Posts

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai
    edited April 2013

    @William
    The quick answer is, yes. You can customize the appearance of whatever you would like. While it requires a decent amount of code, there are some generally accepted practices that can help reduce this.

    There are a few ways to approach this, but the way I have done it relies heavily on the AppearanceManager (may only be available in certain iOS version, I'm not sure). The AppearanceManager allows you to style certain elements of the iOS UI at a "root" level. You can start here for a quick how to:

    http://docs.xamarin.com/guides/ios/user_interface/introduction_to_the_appearance_api

    Something to keep in mind, is that not everything is included in the appearance API, so some things will have to be styled in-line with the rest of the code (not at a root level really) unless maybe you subclass things, etc. Some examples I think are table cells. You will need to create a custom cell with your images, styles and then use that or style the cell each time when you need to.

    You can also look at the Themes section of the Component store and see if any of those are an option for you. Some of them allow for a trial, so you could get the feel for what is possible.

    http://components.xamarin.com

    One way of doing it that I really liked can be found in the Xamarin example project Field Service. There, they created a static class called Theme, which uses the Lazy<T> pattern to load images from the bundle. Then where they want to "theme" something like a nav bar, you can do something like

    // Example code
    navbar.BackgroundImage = Theme.NavImageBackground;
    

    Most of the backgrounds, etc for elements can be changed using a "resizable image" and this Theme class has a few examples of that. You can also subclass a UINavigationBar (and anything else) and customize it how you want and then moving forward use that as your base class instead of just the plain UINavigationbar class. This is your option if don't have access to the Appearance API.

    Hope this helps, good luck!

  • WilliamThomasWilliamThomas USMember ✭✭

    That's what I like to hear! Thank you, that answer is very helpful. I guess I'll have to dig around some examples, I'll definitely check out the Field Service one you suggested. I did see the Appearance API, but I was a bit discouraged when it looked like I can't do things like remove the shiny overlay and change the height of the navigation bar (which is one of the requirements in the style for the app I've been assigned). I don't really mind making subclasses for special UI elements either, as long as it's in C#.

    You wouldn't happen to know any themes for Xamarin that are free, would you? I'd love to see the source code for something like the three $70.00 ones listed on the site, just so I can see how exactly they're applying these styles.

    Anyway, now that I've seen those themes I at least know that what I want to do should be possible. I was just afraid that I would have to dive into Objective-C for this to work. Thanks for your help!

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai

    I have not been able to find a source for any theme components. However, do look into the Theme.cs file in the Field Service application, becuase I think its a good starting point to see what you can do. I imagine the components do something similar and at some point are wrapping the Appearance API with easier to use methods. Its a powerful API with things like:

    var style1 = UISlider.AppearanceWhenContainedIn(typeof(BlackViewController), 
        typeof(PlainView));
    

    Field Service Theme.cs : https://github.com/xamarin/prebuilt-apps/blob/master/FieldService/FieldService.iOS/Utilities/Theme.cs

    Resizing the controls and bars will probably mean you will need to subclass your controllers to customize those attributes e.g.

    public class MyCustomNavBase : UINavigationController { ... }
    

    You will then have more control over all aspects of what makes up the UIView of the Navigation controller (Frame, color, size, etc.)

  • WilliamThomasWilliamThomas USMember ✭✭

    I guess now my only question would be how to explicitly set the navigation bar controller of a view controller to use my new UINavigationToolbar subclass. It looks like the NavigationController property of the UIViewController class is not settable, so I'm not exactly sure how that would be done.

    If at all possible, I would like to avoid using the visual Xcode editor for my UI controllers and just code everything in C#. I appreciate your help by the way, this is all pretty intimidating but I feel like I'm starting to get the hang of all this.

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai

    I was think you would so something like this:

        public class BaseNavigationController : UINavigationController
    {
        public BaseNavigationController ()
        {
            //TODO: Customize any theme options here. Font Color, Title bar color, etc.
    
    
        }
    
        public BaseNavigationController(UIViewController rootViewController) 
            : base (rootViewController)
        {
    
        }
    
        public override bool ShouldAutorotate ()
        {
            return true;
        }
    
        public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations ()
        {
            return UIInterfaceOrientationMask.All;
        }
    }
    

    You should be able to customize the UIView that is displayed in the NavigationBar. e.g.

    // Youll have to create a class or view to use. 
    CustomNavBar myCustomNavBarView = new CustomNavBar();
    NavigationBar.View.AddSubview(myCustomNavBarView);
    
    // Or if its simple, you could just do something like:
    UIView myCustomNavBarView = new UIView(...)
    myCustomNavBarView.BackgroundColor = UIColor.Black;
    ... // Customize more here
    NavigationBar.View.AddSubview(myCustomNavBarView);
    
  • WilliamThomasWilliamThomas USMember ✭✭

    Sorry, what class would your second code block go inside? I'm a little confused as to how exactly your custom UINavigationController (which I'm assuming is BaseNavigationController) is being added to the main page.

  • JohnMillerJohnMiller USForum Administrator, Xamarin Team Xamurai
    edited April 2013

    Sorry for the confusion, the block of code would go in the constructor of BaseNavigationController, where I put the comments to customize here.

    You could rename that BaseNavigationController to something more suiting like LargerNavigationController or something that doesnt make it sound like an abstract class meant for inheriting, thats up to you.

    Than in your main page you can do something like:

    LargerNavigationController mainNav = new LargerNavigationController(
        new UIViewController());
    

    Now use mainNav just like you would with any other UINavigationController. If this doesnt help, could you be a little more specific with your questions? All I am showing you is basic inheritance so that you can customize the UINavigationController class in one place.

    The flow is:

    • Sub-class (inherit from) UINaivgationController
    • Make the constructor signature the same as the base class

       LargerNavigationController(UIViewController rootController) 
           : base(rootController)
      
    • Customize any of of the properties you want inside that sub-class.

    • Add any additional features you want for the navigation controller that may not be present.
    • Now use LargerNavigationController just like you would UINavigationController.
  • WilliamThomasWilliamThomas USMember ✭✭
    edited April 2013

    That all does make sense to me, though for some reason I must be doing something wrong. I'm sure it's a simple mistake, but check it out:

    I have three classes:

    LoginViewController: The main "screen" that my app enters, currently a blank iPhone UIViewController with nothing really changed

    NavBarController.cs: Inherits UINavigationController

    NavBar.cs: Inherits UINavigationBar

    The .xib file of the LoginViewController, as viewed in Xcode, is blank and it looks like this:

    My NavBar.cs class is more or less blank apart from the inheritance:

    public class NavBar : UINavigationBar
    {
        public NavBar ()
        {
        }
    }
    

    Ideally I would like to do the custom styling in here like you had mentioned, but in any case, my NavBarController.cs class is as follows:

    public class NavBarController : UINavigationController
    {
        NavBar CustomNavBar;
    
        public NavBarController ()
        {
            CustomNavBar = new NavBar();
            SetCustomStyles();
            View.AddSubview(CustomNavBar);
        }
    
        public NavBarController (UIViewController rootViewController) 
            : base (rootViewController)
        {
            CustomNavBar = new NavBar();
            SetCustomStyles();
            View.AddSubview(CustomNavBar);
        }
    
        protected void SetCustomStyles()
        {
            CustomNavBar.BackgroundColor = UIColor.Green;
        }
    
        public override bool ShouldAutorotate ()
        {
            return true;
        }
    
        public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations ()
        {
            return UIInterfaceOrientationMask.All;
        }
    }
    

    So from what I understand, I have my own child navigation bar class (NavBar), of which a reference is being stored in my custom controller (NavBarController), and that nav bar view is added to the controller in both those constructors (after changing the background color to green for testing).

    However, when I run my program, it looks like this:

    No green bar. I've tried a view things, like changing BarStyle instead, and all kinds of stuff. I even removed the last line in those constructors expecting the NavBarController to be blank, and it still looks exactly the same.

    Am I missing something really simple?

  • WilliamThomasWilliamThomas USMember ✭✭

    Okay I think I've made a little bit of progress. Delving away from the extended UINavigationBar class, I just changed the NavigationBar's TintColor property from within the constructor of my custom NavBarController class and it seemed to work.

    However, I would very much prefer to build my own subclass of UINavigationBar and replace the NavigationBar with an instance of my custom class. However, I just simply can't find a way to do this.

    Sorry about all these questions, but I know as soon as I learn these sorts of basic things I can really build this app pretty quickly.

  • WilliamThomasWilliamThomas USMember ✭✭

    That's some terrific info, @nicwise, thank you very much. I didn't realize quite how much power the UIAppearance API had. I'll be sure to read those posts and check out your seminar on the topic and hopefully get the ball rolling on this app.

    If I get really stuck on something, would you mind if I shot you a message on here?

  • NicWiseNicWise NZMember, Insider, Beta mod
    edited April 2013

    @williamthomas within reason[1], sure - just ask it here somewhere, and add @nicwise and I get an email :)

    [1] I dont work for Xamarin, so I tend to do this stuff in my free time. Or when I'm procrastinating work. I'll be at Evolve next-next week tho.... Also, I'm in GMT+1 (UK)

  • WilliamThomasWilliamThomas USMember ✭✭

    Of course, I'll make sure not to bug you :p

    I greatly appreciate your help, and yours too @John!

  • NicWiseNicWise NZMember, Insider, Beta mod

    @williamthomas - Bugging me is ok - expecting an answer in a specific time isn't ;-)

    Happy to help tho - I enjoy doing mobile stuff and generally love sharing knowledge about it.

  • WilliamThomasWilliamThomas USMember ✭✭
    edited April 2013

    @nicwise Of course! I actually do have a question though, whenever you may be able to take a look at it. I'm achieving quite a bit so far by using the UIAppearance API; I have to say, it's pretty fantastic.

    One thing I can't currently figure out is how to modify the metrics of the selection indicator of tab bar items. I've gotten the hang of using background images to style them, which is quite nice. However, check out the following image:

    I want to make the lighter blue section (the "selected" tab image) on the bottom completely flush with the rest of the tab bar, instead of having that small amount of padding around the edges. Is this possible using the UIAppearance API?

  • NicWiseNicWise NZMember, Insider, Beta mod
    edited April 2013

    @WilliamThomas

    I just did this:

    public static UIImage TabBarBackgroundImage = UIImage.FromFile("images/tabbar_background.png");
    public static UIImage TabBarSelectedImage = UIImage.FromFile("images/tabbar_selected.png");
    
    
    
    UITabBar.Appearance.SelectionIndicatorImage = Resources.TabBarSelectedImage;
    UITabBar.Appearance.BackgroundImage = Resources.TabBarBackgroundImage;
    

    And the images I've attached. This ends up with this:

    http://fastchicken.co.nz/uploads/old/2012/04/2-footercomparison.jpg

    Note that the smaller attachment is a specific size - 1/4 of the bar - as I had 4 items. I could have done it with a resizable image I think. The Selected one is the small one

  • WilliamThomasWilliamThomas USMember ✭✭

    Okay, I see. So from tinkering around, it looks like the default "draw zone" of the selection indicator on the tab bar contains that small amount of padding, and loading a resizable background image into that will cause that padding to remain.

    But if the image is not resizable (like in your example), and it's the exact height and width that the selector should be, it all works. It's a little bit of a hack but I can't complain.

    Anyway, thank you very much for that. I'm getting a much better understanding of this API the more I tinker with it, and my app is looking prettier by the second. I'm still a little bit confused about when and when not to use resizable images, but I'm sure I'll get the hang of it as I do more and more testing.

    Again, thank you, your assistance is very much appreciated!

  • NicWiseNicWise NZMember, Insider, Beta mod

    @WilliamThomas - not problem, CocoaTouch is quite huge, so it takes a bit to get used to all the new stuff and how it works :)

  • vyelurivyeluri AUMember
    edited January 2015

    @WilliamThomas‌

    Hi,

    Im trying to remove the border or background image or bg color of a navigation bar as shown in the pic. Im using Xamarin forms iOS. I added a custom nav class and rendered in iOS then added the below code.

    Ive tried setting background color to white but its always displays in black color. Could you point where Im possibly going wrong.

    [assembly: ExportRenderer (typeof (NavigationalPageCustomized), typeof (CustomNavigationPageRenderer))]
    namespace BarcodeScanner.iOS
    {
        public class CustomNavigationPageRenderer : NavigationRenderer
        {
            public CustomNavigationPageRenderer()
                : base()
            {
                UINavigationBar bar = new UINavigationBar ();
                bar.SetBackgroundImage (new UIImage (), UIBarMetrics.Default);
            }
        }
    }
    
    nav.png 11.4K
Sign In or Register to comment.