Sorry Xamarin, I shouldn’t need to use Reflection
I’m a big fan of Xamarin and the accompanying library Xamarin Forms. Creating a cross-platform (iOS and Android) app using C# is a great thing and saves a huge amount of time.
Apps built in Xamarin compile natively and run directly using the platform API’s instead of going through a virtual machine, sandboxed web-app or translation layer…bliss.
And better yet, Xamarin Forms provides a really friendly and easy set of libraries to make cross-platform apps. However, there are many times when you find yourself wanting to do something slightly out of the ordinary and a bit platform-specific.
- Want to use the iOS system toolbar icons? No.
- Add a subtitle to the Android action bar? No.
These limitations are frustrating but at least Xamarin Forms is open source so if you’re feeling brave you can browse the source, work out what you need to do and go from there.
However, this morning I was working through an issue I had no choice but to use reflection (ugliness at its best). In other words, I had to do things I’m “not supposed to do” in order to achieve the desired effect. In this case, I’d like to override the iOS navigation bar with my own custom view, something that any native iOS developer could easily do without any crazy hackery.
To do things that standard Xamarin Forms framework doesn’t support involves using renderers. These allow you to override the default functionality of a native object without compromising your cross-platform code. In the case of changing the title bar I’m looking for the NavigationRenderer which controls the navigation bars. To achieve what I want I can override 2 methods.
- OnPushAsync — Called when navigating to a new page
- PushViewController — Called when iOS needs to add the new view controller (your native page)
From reading the source the order at which the TitleView is initialised is as follows:
- OnPushAsync is called
- This sets the title bar using the Title property in Page
- Call PushViewController
Overriding OnPushAsync was my first attempt. However, this caused a really ugly artefact in that the transition from the old title bar to the new was really obvious.
My second attempt was to override PushViewController. From reading the source I can see see that this was being called just after the title was being set. But, I hit a roadblock, the current page isn’t accessible from the parameters being passed in (ViewController or Animated)! Without this I won’t know what text to put into the title bar. From digging around it seemed that my only alternative was to use Reflection.
Success! By casting the ViewController to a ParentingViewController (a hidden class) I was able to override the TitleView before the page was being rendered.
This isn’t the first time I’ve had to use reflection to solve these types of problems and I’m sure it won’t be the last. It is a little frustrating because if Xamarin decide to change the name of the internal classes then my code will break as there is no strict type checking (something C# is great at).
I don’t see why Xamarin can’t add more hooks to allow the customisation of basic UI elements. There doesn’t seem to be any good reason to want to block it off.
Fortunately, as it is an Open Source project, I am just contribute myself. Stay tuned.