Make Label display HTML from a string (#4527)
authorGerald Versluis <gerald.versluis@microsoft.com>
Wed, 28 Aug 2019 11:25:02 +0000 (13:25 +0200)
committerGitHub <noreply@github.com>
Wed, 28 Aug 2019 11:25:02 +0000 (13:25 +0200)
* Use UpdateText

* Added missing helper method and UI test

* Added missing helper for UWP

* Added csproj entry for helper

* Resolved rebase conflicts

* Update LabelRenderer.cs

* Update LabelRenderer.cs

* Update LabelRenderer.cs

* iOS Merge error fix

* Feedback

* - uwp fixes

* - android fix empty text

* - ios fix null and setting text when texttype starts as html

* - set _perfectSizeValid = false; after changed AttributedText

Setting the AttributedText causes GetDesiredSize to get called which sets _perfectSizeValid to true but at this point this frame still hasn't adjusted to any size change from *LayoutSubViews*. This resets _perfectSizeValid so after the AttributedText set the desiredsize can get pulled again

* Renamed PlainText to Text

* Fixed initial no HTML styling

13 files changed:
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/LabelTextType.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Controls/CoreGalleryPages/LabelCoreGalleryPage.cs
Xamarin.Forms.Core/Label.cs
Xamarin.Forms.Core/TextType.cs [new file with mode: 0644]
Xamarin.Forms.CustomAttributes/TestAttributes.cs
Xamarin.Forms.Platform.Android/FastRenderers/LabelRenderer.cs
Xamarin.Forms.Platform.Android/Forms.cs
Xamarin.Forms.Platform.Android/Renderers/LabelRenderer.cs
Xamarin.Forms.Platform.UAP/LabelHtmlHelper.cs [new file with mode: 0644]
Xamarin.Forms.Platform.UAP/LabelRenderer.cs
Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj
Xamarin.Forms.Platform.iOS/Renderers/LabelRenderer.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/LabelTextType.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/LabelTextType.cs
new file mode 100644 (file)
index 0000000..3590c08
--- /dev/null
@@ -0,0 +1,88 @@
+using System;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.Forms.Core.UITests;
+using Xamarin.UITest;
+using NUnit.Framework;
+using System.Linq;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+       [Category(UITestCategories.Label)]
+#endif
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.None, 0, "Implementation of Label TextType", PlatformAffected.All)]
+       public class LabelTextType : TestContentPage
+       {
+               protected override void Init()
+               {
+                       var label = new Label
+                       {
+                               AutomationId = "TextTypeLabel",
+                               Text = "<h1>Hello World!</h1>"
+                       };
+
+                       var button = new Button
+                       {
+                               AutomationId = "ToggleTextTypeButton",
+                               Text = "Toggle HTML/Plain"
+                       };
+
+                       button.Clicked += (s, a) =>
+                       {
+                               label.TextType = label.TextType == TextType.Html ? TextType.Text : TextType.Html;
+                       };
+
+
+                       Label htmlLabel = new Label() { TextType = TextType.Html };
+                       Label normalLabel = new Label();
+                       Label nullLabel = new Label() { TextType = TextType.Html };
+
+                       Button toggle = new Button()
+                       {
+                               Text = "Toggle some more things",
+                               Command = new Command(() =>
+                               {
+                                       htmlLabel.Text = $"<b>{DateTime.UtcNow}</b>";
+                                       normalLabel.Text = $"<b>{DateTime.UtcNow}</b>";
+
+                                       if (String.IsNullOrWhiteSpace(nullLabel.Text))
+                                               nullLabel.Text = "hi there";
+                                       else
+                                               nullLabel.Text = null;
+                               })
+                       };
+
+
+                       var stacklayout = new StackLayout();
+                       stacklayout.Children.Add(label);
+                       stacklayout.Children.Add(button);
+                       stacklayout.Children.Add(htmlLabel);
+                       stacklayout.Children.Add(normalLabel);
+                       stacklayout.Children.Add(nullLabel);
+                       stacklayout.Children.Add(toggle);
+
+                       Content = stacklayout;
+               }
+
+#if UITEST
+               [Test]
+               public void LabelToggleHtmlAndPlainTextTest() 
+               {
+                       RunningApp.WaitForElement ("TextTypeLabel");
+                       RunningApp.Screenshot ("I see plain text");
+
+                       Assert.IsTrue(RunningApp.Query("TextTypeLabel").FirstOrDefault()?.Text == "<h1>Hello World!</h1>");
+
+                       RunningApp.Tap("ToggleTextTypeButton");
+                       RunningApp.Screenshot ("I see HTML text");
+
+                       Assert.IsFalse(RunningApp.Query("TextTypeLabel").FirstOrDefault()?.Text.Contains("<h1>") ?? true);
+               }
+#endif
+       }
+}
index 1711c8f..2fb5d14 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)Issue6738.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)GitHub6926.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue5503.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)LabelTextType.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla22229.xaml">
index e7d0c67..9d7bbeb 100644 (file)
@@ -236,6 +236,40 @@ namespace Xamarin.Forms.Controls
                                        Padding = new Thickness(40, 20)
                                }
                        );
+                       
+                       var htmlLabelContainer = new ViewContainer<Label>(Test.Label.TextType,
+                               new Label
+                               {
+                                       Text = "<h1>Hello world!</h1>",
+                                       TextType = TextType.Html
+                               });
+
+                       var htmlLabelMultipleLinesContainer = new ViewContainer<Label>(Test.Label.TextType,
+                               new Label
+                               {
+                                       Text = "<h1>Hello world!</h1><p>Lorem <strong>ipsum</strong> bla di bla <i>blabla</i> blablabl&nbsp;ablabla & blablablablabl ablabl ablablabl ablablabla blablablablablablab lablablabla blablab lablablabla blablabl ablablablab lablabla blab lablablabla blablab lablabla blablablablab lablabla blablab lablablabl ablablabla blablablablablabla blablabla</p>",
+                                       TextType = TextType.Html,
+                                       MaxLines = 3
+                               });
+
+                       var toggleLabel = new Label
+                       {
+                               TextType = TextType.Html,
+                               Text = "<h1 style=\"color: red;\">Hello world!</h1><p>Lorem <strong>ipsum</strong></p>"
+                               
+                       };
+
+                       var gestureRecognizer = new TapGestureRecognizer();
+
+                       gestureRecognizer.Tapped += (s, a) =>
+                       {
+                               toggleLabel.TextType = toggleLabel.TextType == TextType.Html ? TextType.Text : TextType.Html;
+                       };
+
+                       toggleLabel.GestureRecognizers.Add(gestureRecognizer);
+
+                       var toggleHtmlPlainTextLabelContainer = new ViewContainer<Label>(Test.Label.TextType,
+                               toggleLabel);
 
                        Add (namedSizeMediumBoldContainer);
                        Add (namedSizeMediumItalicContainer);
@@ -272,6 +306,9 @@ namespace Xamarin.Forms.Controls
                        Add (maxlinesTailTruncContainer);
                        Add (maxlinesWordWrapContainer);
                        Add(paddingContainer);
+                       Add (htmlLabelContainer);
+                       Add (htmlLabelMultipleLinesContainer);
+                       Add (toggleHtmlPlainTextLabelContainer);
                }
        }
 }
\ No newline at end of file
index 7210624..9126181 100644 (file)
@@ -89,6 +89,9 @@ namespace Xamarin.Forms
                        });
 
                public static readonly BindableProperty PaddingProperty = PaddingElement.PaddingProperty;
+               
+               public static readonly BindableProperty TextTypeProperty = BindableProperty.Create(nameof(TextType), typeof(TextType), typeof(Label), TextType.Text,
+                       propertyChanged: (bindable, oldvalue, newvalue) => ((Label)bindable).InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged));
 
                readonly Lazy<PlatformConfigurationRegistry<Label>> _platformConfigurationRegistry;
 
@@ -212,6 +215,12 @@ namespace Xamarin.Forms
                        get { return (Thickness)GetValue(PaddingProperty); }
                        set { SetValue(PaddingProperty, value); }
                }
+               
+               public TextType TextType
+               {
+                       get => (TextType)GetValue(TextTypeProperty);
+                       set => SetValue(TextTypeProperty, value);
+               }
 
                double IFontElement.FontSizeDefaultValueCreator() =>
                        Device.GetNamedSize(NamedSize.Default, (Label)this);
diff --git a/Xamarin.Forms.Core/TextType.cs b/Xamarin.Forms.Core/TextType.cs
new file mode 100644 (file)
index 0000000..bbe0cf9
--- /dev/null
@@ -0,0 +1,8 @@
+namespace Xamarin.Forms
+{
+       public enum TextType
+       {
+               Text,
+               Html
+       }
+}
\ No newline at end of file
index 4ebeefa..8cf97b9 100644 (file)
@@ -635,7 +635,8 @@ namespace Xamarin.Forms.CustomAttributes
                        VerticalTextAlignmentStart,
                        VerticalTextAlignmentCenter,
                        VerticalTextAlignmentEnd,
-                       MaxLines
+                       MaxLines,
+                       TextType
                }
 
                public enum MasterDetailPage
index 7da8f9b..eadd780 100644 (file)
@@ -251,6 +251,7 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                                        UpdateGravity();
                                if (e.OldElement?.MaxLines != e.NewElement.MaxLines)
                                        UpdateMaxLines();
+
                                UpdatePadding();
 
                                ElevationHelper.SetElevation(this, e.NewElement);
@@ -263,7 +264,8 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
 
                        if (e.PropertyName == Label.HorizontalTextAlignmentProperty.PropertyName || e.PropertyName == Label.VerticalTextAlignmentProperty.PropertyName)
                                UpdateGravity();
-                       else if (e.PropertyName == Label.TextColorProperty.PropertyName)
+                       else if (e.PropertyName == Label.TextColorProperty.PropertyName ||
+                               e.PropertyName == Label.TextTypeProperty.PropertyName)
                                UpdateText();
                        else if (e.PropertyName == Label.FontProperty.PropertyName)
                                UpdateText();
@@ -380,7 +382,23 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                                        SetTextColor(_labelTextColorDefault);
                                        _lastUpdateColor = Color.Default;
                                }
-                               Text = Element.Text;
+
+                               switch (Element.TextType)
+                               {
+                                       case TextType.Html:
+                                               if (Forms.IsNougatOrNewer)
+                                                       Control.SetText(Html.FromHtml(Element.Text ?? string.Empty, FromHtmlOptions.ModeCompact), BufferType.Spannable);
+                                               else
+#pragma warning disable CS0618 // Type or member is obsolete
+                                                       Control.SetText(Html.FromHtml(Element.Text ?? string.Empty), BufferType.Spannable);
+#pragma warning restore CS0618 // Type or member is obsolete
+                                               break;
+
+                                       default:
+                                               Text = Element.Text;
+                                               break;
+                               }
+                               
                                UpdateColor();
                                UpdateFont();
 
@@ -416,4 +434,4 @@ namespace Xamarin.Forms.Platform.Android.FastRenderers
                        _lastSizeRequest = null;
                }
        }
-}
+}
\ No newline at end of file
index 20ca905..52a721c 100644 (file)
@@ -56,6 +56,7 @@ namespace Xamarin.Forms
                static BuildVersionCodes? s_sdkInt;
                static bool? s_isLollipopOrNewer;
                static bool? s_isMarshmallowOrNewer;
+               static bool? s_isNougatOrNewer;
 
                [Obsolete("Context is obsolete as of version 2.5. Please use a local context instead.")]
                [EditorBrowsable(EditorBrowsableState.Never)]
@@ -98,6 +99,16 @@ namespace Xamarin.Forms
                        }
                }
 
+               internal static bool IsNougatOrNewer
+               {
+                       get
+                       {
+                               if (!s_isNougatOrNewer.HasValue)
+                                       s_isNougatOrNewer = (int)Build.VERSION.SdkInt >= 24;
+                               return s_isNougatOrNewer.Value;
+                       }
+               }
+
                public static float GetFontSizeNormal(Context context)
                {
                        float size = 50;
index 2771697..d808ccc 100644 (file)
@@ -135,7 +135,6 @@ namespace Xamarin.Forms.Platform.Android
                                        UpdateMaxLines();
                                if (e.OldElement.CharacterSpacing != e.NewElement.CharacterSpacing)
                                        UpdateCharacterSpacing();
-
                        }
                        UpdateTextDecorations();
                        UpdatePadding();
@@ -158,13 +157,13 @@ namespace Xamarin.Forms.Platform.Android
                                UpdateLineBreakMode();
                        else if (e.PropertyName == Label.TextDecorationsProperty.PropertyName)
                                UpdateTextDecorations();
-                       else if (e.PropertyName == Label.TextProperty.PropertyName || e.PropertyName == Label.FormattedTextProperty.PropertyName)
+                       else if (e.IsOneOf(Label.TextProperty, Label.FormattedTextProperty, Label.TextTypeProperty))
                                UpdateText();
                        else if (e.PropertyName == Label.LineHeightProperty.PropertyName)
                                UpdateLineHeight();
                        else if (e.PropertyName == Label.MaxLinesProperty.PropertyName)
                                UpdateMaxLines();
-                       else if (e.PropertyName == ImageButton.PaddingProperty.PropertyName)
+                       else if (e.PropertyName == Label.PaddingProperty.PropertyName)
                                UpdatePadding();
                }
 
@@ -242,7 +241,6 @@ namespace Xamarin.Forms.Platform.Android
                        }
                }
 
-
                void UpdateLineHeight()
                {
                        _lastSizeRequest = null;
@@ -274,7 +272,25 @@ namespace Xamarin.Forms.Platform.Android
                                        _view.SetTextColor(_labelTextColorDefault);
                                        _lastUpdateColor = Color.Default;
                                }
-                               _view.Text = Element.Text;
+
+                               switch (Element.TextType)
+                               {
+
+                                       case TextType.Html:
+                                               if (Forms.IsNougatOrNewer)
+                                                       Control.SetText(Html.FromHtml(Element.Text ?? string.Empty, FromHtmlOptions.ModeCompact), TextView.BufferType.Spannable);
+                                               else
+#pragma warning disable CS0618 // Type or member is obsolete
+                                                       Control.SetText(Html.FromHtml(Element.Text ?? string.Empty), TextView.BufferType.Spannable);
+#pragma warning restore CS0618 // Type or member is obsolete
+                                               break;
+
+                                       default:
+                                               _view.Text = Element.Text;
+
+                                               break;
+                               }
+
                                UpdateColor();
                                UpdateFont();
 
diff --git a/Xamarin.Forms.Platform.UAP/LabelHtmlHelper.cs b/Xamarin.Forms.Platform.UAP/LabelHtmlHelper.cs
new file mode 100644 (file)
index 0000000..ba08a30
--- /dev/null
@@ -0,0 +1,112 @@
+using System.Xml.Linq;
+using Windows.UI.Xaml.Documents;
+
+namespace Xamarin.Forms.Platform.UAP
+{
+       internal static class LabelHtmlHelper
+       {
+               // All the supported HTML tags
+               internal const string ElementB = "B";
+               internal const string ElementBr = "BR";
+               internal const string ElementEm = "EM";
+               internal const string ElementI = "I";
+               internal const string ElementP = "P";
+               internal const string ElementStrong = "STRONG";
+               internal const string ElementU = "U";
+               internal const string ElementUl = "UL";
+               internal const string ElementLi = "LI";
+               internal const string ElementDiv = "DIV";
+
+               public static void ParseText(XElement element, InlineCollection inlines, Label label)
+               {
+                       if (element == null)
+                               return;
+
+                       var currentInlines = inlines;
+                       var elementName = element.Name.ToString().ToUpper();
+                       switch (elementName)
+                       {
+                               case ElementB:
+                               case ElementStrong:
+                                       var bold = new Bold();
+                                       inlines.Add(bold);
+                                       currentInlines = bold.Inlines;
+                                       break;
+                               case ElementI:
+                               case ElementEm:
+                                       var italic = new Italic();
+                                       inlines.Add(italic);
+                                       currentInlines = italic.Inlines;
+                                       break;
+                               case ElementU:
+                                       var underline = new Underline();
+                                       inlines.Add(underline);
+                                       currentInlines = underline.Inlines;
+                                       break;
+                               case ElementBr:
+                                       inlines.Add(new LineBreak());
+                                       break;
+                               case ElementP:
+                                       // Add two line breaks, one for the current text and the second for the gap.
+                                       if (AddLineBreakIfNeeded(inlines))
+                                       {
+                                               inlines.Add(new LineBreak());
+                                       }
+
+                                       var paragraphSpan = new Windows.UI.Xaml.Documents.Span();
+                                       inlines.Add(paragraphSpan);
+                                       currentInlines = paragraphSpan.Inlines;
+                                       break;
+                               case ElementLi:
+                                       inlines.Add(new LineBreak());
+                                       inlines.Add(new Run { Text = " • " });
+                                       break;
+                               case ElementUl:
+                               case ElementDiv:
+                                       AddLineBreakIfNeeded(inlines);
+                                       var divSpan = new Windows.UI.Xaml.Documents.Span();
+                                       inlines.Add(divSpan);
+                                       currentInlines = divSpan.Inlines;
+                                       break;
+                       }
+                       foreach (var node in element.Nodes())
+                       {
+                               if (node is XText textElement)
+                               {
+                                       currentInlines.Add(new Run { Text = textElement.Value });
+                               }
+                               else
+                               {
+                                       ParseText(node as XElement, currentInlines, label);
+                               }
+                       }
+                       // Add newlines for paragraph tags
+                       if (elementName == "ElementP")
+                       {
+                               currentInlines.Add(new LineBreak());
+                       }
+               }
+
+               static bool AddLineBreakIfNeeded(InlineCollection inlines)
+               {
+                       if (inlines.Count <= 0)
+                               return false;
+
+                       var lastInline = inlines[inlines.Count - 1];
+                       while ((lastInline is Windows.UI.Xaml.Documents.Span))
+                       {
+                               var span = (Windows.UI.Xaml.Documents.Span)lastInline;
+                               if (span.Inlines.Count > 0)
+                               {
+                                       lastInline = span.Inlines[span.Inlines.Count - 1];
+                               }
+                       }
+
+                       if (lastInline is LineBreak)
+                               return false;
+
+                       inlines.Add(new LineBreak());
+                       return true;
+               }
+       }
+}
\ No newline at end of file
index 3b9217f..f0c0253 100644 (file)
@@ -1,12 +1,15 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Text.RegularExpressions;
+using System.Xml.Linq;
 using Windows.Foundation;
 using Windows.UI.Text;
 using Windows.UI.Xaml;
 using Windows.UI.Xaml.Automation.Peers;
 using Windows.UI.Xaml.Controls;
 using Windows.UI.Xaml.Documents;
+using Xamarin.Forms.Platform.UAP;
 using Xamarin.Forms.PlatformConfiguration.WindowsSpecific;
 using Specifics = Xamarin.Forms.PlatformConfiguration.WindowsSpecific.Label;
 using WThickness = Windows.UI.Xaml.Thickness;
@@ -155,11 +158,8 @@ namespace Xamarin.Forms.Platform.UWP
 
                protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
                {
-                       if (e.PropertyName == Label.TextProperty.PropertyName ||
-                               e.PropertyName == Label.FormattedTextProperty.PropertyName)
-                       {
+                       if (e.IsOneOf(Label.TextProperty,  Label.FormattedTextProperty, Label.TextTypeProperty))
                                UpdateText(Control);
-                       }
                        else if (e.PropertyName == Label.TextColorProperty.PropertyName)
                                UpdateColor(Control);
                        else if (e.PropertyName == Label.HorizontalTextAlignmentProperty.PropertyName || e.PropertyName == Label.VerticalTextAlignmentProperty.PropertyName)
@@ -182,6 +182,7 @@ namespace Xamarin.Forms.Platform.UWP
                                UpdateMaxLines(Control);
                        else if (e.PropertyName == Label.PaddingProperty.PropertyName)
                                UpdatePadding(Control);
+                               
                        base.OnElementPropertyChanged(sender, e);
                }
 
@@ -327,10 +328,22 @@ namespace Xamarin.Forms.Platform.UWP
                        _perfectSizeValid = false;
 
                        if (textBlock == null)
-                       {
                                return;
+
+                       switch (Element.TextType)
+                       {
+                               case TextType.Html:
+                                       UpdateTextHtml(textBlock);
+                                       break;
+
+                               default:
+                                       UpdateTextPlainText(textBlock);
+                                       break;
                        }
+               }
 
+               void UpdateTextPlainText(TextBlock textBlock)
+               {
                        Label label = Element;
                        if (label != null)
                        {
@@ -360,6 +373,27 @@ namespace Xamarin.Forms.Platform.UWP
                        }
                }
 
+               void UpdateTextHtml(TextBlock textBlock)
+               {
+                       var text = Element.Text ?? String.Empty;
+
+                       // Just in case we are not given text with elements.
+                       var modifiedText = string.Format("<div>{0}</div>", text);
+                       modifiedText = Regex.Replace(modifiedText, "<br>", "<br></br>", RegexOptions.IgnoreCase);
+                       // reset the text because we will add to it.
+                       Control.Inlines.Clear();
+                       try
+                       {
+                               var element = XElement.Parse(modifiedText);
+                               LabelHtmlHelper.ParseText(element, Control.Inlines, Element);
+                       }
+                       catch (Exception)
+                       {
+                               // if anything goes wrong just show the html
+                               textBlock.Text = Windows.Data.Html.HtmlUtilities.ConvertToText(Element.Text);
+                       }
+               }
+
                void UpdateDetectReadingOrderFromContent(TextBlock textBlock)
                {
                        if (Element.IsSet(Specifics.DetectReadingOrderFromContentProperty))
@@ -407,4 +441,4 @@ namespace Xamarin.Forms.Platform.UWP
                                        Element.Padding.Bottom);
                }
        }
-}
+}
\ No newline at end of file
index 5193420..edea00d 100644 (file)
@@ -78,6 +78,7 @@
     <Compile Include="ITitleViewRendererController.cs" />
     <Compile Include="IToolbarProvider.cs" />
     <Compile Include="IVisualNativeElementRenderer.cs" />
+    <Compile Include="LabelHtmlHelper.cs" />
     <Compile Include="NativeBindingExtensions.cs" />
     <Compile Include="NativeEventWrapper.cs" />
     <Compile Include="NativePropertyListener.cs" />
index 5e3e5a3..4f3dd23 100644 (file)
@@ -39,7 +39,8 @@ namespace Xamarin.Forms.Platform.MacOS
                        Label.FormattedTextProperty.PropertyName,
                        Label.LineBreakModeProperty.PropertyName,
                        Label.LineHeightProperty.PropertyName,
-                       Label.PaddingProperty.PropertyName
+                       Label.PaddingProperty.PropertyName,
+                       Label.TextTypeProperty.PropertyName
                };
 
                public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
@@ -216,9 +217,10 @@ namespace Xamarin.Forms.Platform.MacOS
                                UpdateMaxLines();
                        else if (e.PropertyName == Label.PaddingProperty.PropertyName)
                                UpdatePadding();
+                       else if (e.PropertyName == Label.TextTypeProperty.PropertyName)
+                               UpdateText();
                }
 
-
                protected override NativeLabel CreateNativeControl()
                {
 #if __MOBILE__
@@ -236,6 +238,9 @@ namespace Xamarin.Forms.Platform.MacOS
 
                void UpdateTextDecorations()
                {
+                       if (Element?.TextType != TextType.Text)
+                               return;
+
                        if (!Element.IsSet(Label.TextDecorationsProperty))
                                return;
 
@@ -275,6 +280,7 @@ namespace Xamarin.Forms.Platform.MacOS
 #else
                        Control.AttributedStringValue = newAttributedText;
 #endif
+                       _perfectSizeValid = false;
                }
 
 #if __MOBILE__
@@ -372,15 +378,34 @@ namespace Xamarin.Forms.Platform.MacOS
                {
 #if __MOBILE__
 
+                       if (Element?.TextType != TextType.Text)
+                               return;
+
                        var textAttr = Control.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing);
 
                        if (textAttr != null)
                                Control.AttributedText = textAttr;
+                               
+                       _perfectSizeValid = false;
 #endif
                }
 
                void UpdateText()
                {
+                       switch (Element.TextType)
+                       {
+                               case TextType.Html:
+                                       UpdateTextHtml();
+                                       break;
+
+                               default:
+                                       UpdateTextPlainText();
+                                       break;
+                       }
+               }
+
+               void UpdateTextPlainText()
+               {
                        _formatted = Element.FormattedText;
                        if (_formatted == null && Element.LineHeight >= 0)
                                _formatted = Element.Text;
@@ -407,10 +432,42 @@ namespace Xamarin.Forms.Platform.MacOS
 #else
                        Control.AttributedStringValue = _formatted.ToAttributed(Element, Element.TextColor, Element.HorizontalTextAlignment, Element.LineHeight);
 #endif
+                       _perfectSizeValid = false;
+               }
+
+               void UpdateTextHtml()
+               {
+                       string text = Element.Text ?? string.Empty;
+
+#if __MOBILE__
+                       var attr = new NSAttributedStringDocumentAttributes
+                       {
+                               DocumentType = NSDocumentType.HTML
+                       };
+
+                       NSError nsError = null;
+
+                       Control.AttributedText = new NSAttributedString(text, attr, ref nsError);
+
+#else
+                       var attr = new NSAttributedStringDocumentAttributes
+                       {
+                               DocumentType = NSDocumentType.HTML
+                       };
+
+                       var htmlData = new NSMutableData();
+                       htmlData.SetData(text);
+
+                       Control.AttributedStringValue = new NSAttributedString(htmlData, attr, out _);
+#endif
+                       _perfectSizeValid = false;
                }
 
                void UpdateFont()
                {
+                       if (Element?.TextType != TextType.Text)
+                               return;
+
                        if (IsTextFormatted)
                        {
                                UpdateFormattedText();
@@ -427,6 +484,9 @@ namespace Xamarin.Forms.Platform.MacOS
 
                void UpdateTextColor()
                {
+                       if (Element?.TextType != TextType.Text)
+                               return;
+
                        if (IsTextFormatted)
                        {
                                UpdateFormattedText();
@@ -545,4 +605,4 @@ namespace Xamarin.Forms.Platform.MacOS
                }
 #endif
        }
-}
\ No newline at end of file
+}