[UPDATE]: I modified my custom renderer to handle this situation. See below!
While developing a Xamrin Forms (Android + iOS) app, I'm experiencing a strange behavour with labels: if I place a label with empty text, it is rendered fine on Android, while on iOS it is not rendered at all.
Here is a simple example:
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="MyApp.Views.Page1"
xmlns:ctrl="clr-namespace:MyApp.Controls">
<ContentPage.Content>
<StackLayout Spacing="0">
<Label x:Name="LabelOs" Margin="0,20" />
<Label x:Name="Label1" Text="Label1" BackgroundColor="Red" TextColor="White" Margin="0" Padding="0" />
<Label x:Name="Label2" Text="" BackgroundColor="Green" TextColor="White" Margin="0" Padding="0" />
<Label x:Name="Label3" Text="Label3" BackgroundColor="Blue" TextColor="White" Margin="0" Padding="0" />
<ctrl:RRLabel x:Name="Label1Custom" Text="Label1Custom" BackgroundColor="Yellow" Borders="Left, Right, Bottom" Margin="10,50,10,10" />
<ctrl:RRLabel x:Name="Label2Custom" Text="" Borders="Left, Right, Bottom" BackgroundColor="Yellow" Margin="10" />
<ctrl:RRLabel x:Name="Label3Custom" Text="Label3Custom" Borders="Left, Right, Bottom" BackgroundColor="Yellow" Margin="10" />
<Button x:Name="Button1" Text="Set Label2" Clicked="Button1_Clicked" Margin="20, 50, 20, 0" />
</StackLayout>
</ContentPage.Content>
Code behind:
private void Button1_Clicked(object sender, EventArgs e)
{
Label2.Text = "Label2";
Label2Custom.Text = "Label2Custom";
}
Label custom renderer (relevant part only):
public override void Draw(CGRect rect)
{
using (CGContext g = UIGraphics.GetCurrentContext())
{
base.Draw(rect);
g.SetLineWidth(2);
g.SetStrokeColor(lineColor);
var path = new CGPath();
if ((label.Borders & RRLabel.LabelBorders.Left) == RRLabel.LabelBorders.Left)
path.AddLines(new CGPoint[] { new CGPoint(rect.Left, rect.Top), new CGPoint(rect.Left, rect.Bottom) });
if ((label.Borders & RRLabel.LabelBorders.Bottom) == RRLabel.LabelBorders.Bottom)
path.AddLines(new CGPoint[] { new CGPoint(rect.Left, rect.Bottom), new CGPoint(rect.Right, rect.Bottom) });
if ((label.Borders & RRLabel.LabelBorders.Right) == RRLabel.LabelBorders.Right)
path.AddLines(new CGPoint[] { new CGPoint(rect.Right, rect.Bottom), new CGPoint(rect.Right, rect.Top) });
if ((label.Borders & RRLabel.LabelBorders.Top) == RRLabel.LabelBorders.Top)
path.AddLines(new CGPoint[] { new CGPoint(rect.Right, rect.Top), new CGPoint(rect.Left, rect.Top) });
g.AddPath(path);
g.DrawPath(CGPathDrawingMode.Stroke);
}
}
This is how the app looks like on Android:
And on iOS:
As you can see, both standard and custom labels have issues: they both have height=0, so their background is not displayed. When text is set, standard label il rendered correctly, while in the custom one there is no background and borders.
Of course everything is fine if I set label text to a space, but I would like to avoid this.
[SOLUTION]: I managed to handle this by modifying my custom renderer:
Shared control:
public class RRLabel : Label
{
/* ... */
public bool IosForceHeight { get; set; } = true;
}
Renderer:
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null && e.NewElement != null)
{
/* ... */
if (label.IosForceHeight && label.Text == "") Control.Text = " ";
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
/* ... */
if (label.IosForceHeight && e.PropertyName == nameof(RRLabel.Text) && label.Text == "") Control.Text = " ";
}
This way I have the label always rendered, while preserving the actual value of the Text property in the shared control.