Building an HTML Label for Xamarin.Forms
December 3, 2018I recently built an HTML Label control for Xamarin.Forms. It’s available on nuget if you’d like to give it a try. I decided to write this post on how and why I built this particular library. As you may or may not know, there are already several other HTML label controls out there and of course there is the WebView
control. So why did I find it necessary to build my own?
First of all, I had some very basic requirements (for a side-project) - I needed to do basic text markup like bold/strong, italics, and hyperlinks. Hyperlinks quickly turned into the deal-breaker requirement for most, if not all, of the other libraries out there. Some didn’t support hyperlinks at all while others had very complicated code to support hyperlinks (e.g., coordinates and gestures). As for the Xamarin.Forms WebView
control, it just seemed like overkill for what I needed to do. I feel like the WebView
is good for rendering screens in an app that are entirely driven by HTML, rendering very complex CSS-heavy HTML, or, of course simply displaying a web page. I just needed to display some text that is coming from a web API that may or may not have some minimal HTML for formatting and linking.
The issue with hyperlinks
I’m pretty sure the reason hyperlinks are not supported or overly complex in other libraries is because by default including hyperlinks in a UILabel
is not supported on iOS. Since the Xamarin.Forms Label
element is rendered down to a UILabel
, this limits what can be done with hyperlinks in a custom LabelRenderer
. However, hyperlinks can be included very easily in a UITextView
. But how do we make a custom LabelRenderer
use UITextView
instead of UILabel
?
My solution
My solution is to use a custom Label
class (named HtmlLabel
) mapped to a custom LabelRenderer
for Android, but not for iOS. For iOS I mapped HtmlLabel
to a UITextView
using a custom ViewRenderer<>
. So, when using the HtmlLabel
control from a Page
or ContentView
everything will act and work like a Label
since it’s literally just a subclass of Label
. But, on iOS it will be rendered differently than how Xamarin.Forms
would normally render a Label
, using a UITextView
instead of a UILabel
.
First I made a new Label
subclass named HtmlLabel
that will be the “control” used to display HTML:
public class HtmlLabel : Label
{
}
I didn’t need to add any new properties since the Text
property from the base Label
class is all that is needed to store the text value that contains the HTML.
Android
Since HTML and hyperlinks are easily rendered in an Android.Widget.TextView
, there is no issue simply subclassing LabelRenderer
to add HTML rendering support:
Control.TextFormatted = Build.VERSION.SdkInt >= BuildVersionCodes.N
? Html.FromHtml(Element.Text, FromHtmlOptions.ModeCompact)
: Html.FromHtml(Element.Text);
And to include support for hyperlinks:
Control.MovementMethod = Android.Text.Method.LinkMovementMethod.Instance;
That’s it for Android… just a few lines of code in a custom LabelRenderer
. Nothing out of the ordinary.
iOS
For iOS it’s a slightly different story but just as simple - I want to render HtmlLabel
using a UITextView
, not a UILabel
. I did this by subclassing ViewRenderer<HtmlLabel, UITextView>
instead of LabelRenderer
.
Now that the custom renderer is working with a UITextView
as its native control, HTML rendering support can easily be added:
NSError error = null;
Control.AttributedText = new NSAttributedString(
NSData.FromString(Element.Text),
new NSAttributedStringDocumentAttributes {
DocumentType = NSDocumentType.HTML
},
ref error);
Even though Control
(the native control) is a UITextView
, Element
(the Xamarin.Forms element being rendered) is still a Label
(via HtmlLabel
), so Element.Text
can still be used as well as any other Label
properties just like we would in a basic custom LabelRenderer
.
And to include support for hyperlinks:
Control.ShouldInteractWithUrl += delegate { return true; };
Also, while not really required, it’s nice to disable editing and scrolling so the UITextView
behaves more like a label:
Control.Editable = false;
Control.ScrollEnabled = false;
And that’s all it takes to enable HTML (with hyperlinks) rendering support using a UITextView
. Much simplier than trying to manipulate the UILabel
in a custom LabelRenderer
with a ton of complex code.
You can view all of the code for HtmlLabel on my github and you can get the package on nuget.