Hello,
I wrote a custom renderer for my chat application to create a chat bubble using a path with series of arcs and lines. On android it works very well but on iOS, I do not see the path being renderered inside when the custom frame is being rendered in a listview. Outside a listview, it gives me what I want. I am starting to suspect that it has to do with the listview.
Can someone give me a hand with this?
some screenshots in android & ios
.cs Code:
public class ChatBubbleRenderer : VisualElementRenderer<ChatBubbleFrame>
{
public override void Draw(CGRect rect)
{
var currentContext = UIGraphics.GetCurrentContext();
var properRect = AdjustForThickness(rect);
HandleShapeDraw(currentContext, properRect);
}
protected RectangleF AdjustForThickness(CGRect rect)
{
var x = rect.X + Element.Padding.Left;
var y = rect.Y + Element.Padding.Top;
var width = rect.Width - ((Element.Padding.Left + (int)Element.StrokeWidth) * 2);
var height = rect.Height - ((Element.Padding.Top + (int)Element.StrokeWidth) * 2);
return new RectangleF((float)x, (float)y, (float)width, (float)height);
}
protected void HandleShapeDraw(CGContext currentContext, RectangleF rect)
{
this.ClipsToBounds = false;
var path = new CGPath();
var cr = Element.CornerRadius;
var hl = Element.HandleLength;
var x = rect.Left;
var y = rect.Top;
var startLeft = x;
var startTop = y;
var startRight = rect.Right;
var startBottom = rect.Bottom;
var centerX = x + cr;
var centerY = y + cr;
var width = (float)rect.Width - hl;
var height = (float)rect.Height;
if (Element.HandleIsBottomRight)
{
path.AddArc(centerX, centerY, cr, (float)Math.PI, (float)Math.PI * 3f / 2f, false);
x = startRight - cr;
//line to top right arc
path.AddLineToPoint(x, y);
//top right arc
centerX = x;
path.AddArc(centerX, centerY, cr, (float)Math.PI * 3f / 2f, 0, false);
x = x + cr;
//bring y down to top of triangle
y = y + height;
path.AddLineToPoint(x, y);
//chat handle
x = x + hl;
y = y + hl;
path.AddLineToPoint(x, y);
//to bottom left arc
x = startLeft + cr;
path.AddLineToPoint(x, y);
//bottom left arc
centerX = x;
centerY = y - cr;
path.AddArc(centerX, centerY, cr, (float)Math.PI / 2f, (float)Math.PI, false); //false to rotate clockwise
}
else
{
//line to top right arc
path.MoveToPoint(x, y);
x = startRight - cr;
path.AddLineToPoint(x, y);
//top right arc
centerX = x;
path.AddArc(centerX, centerY, cr, (float)Math.PI * 3f / 2f, 0, false);
//line to bottom right arc
x = x + cr;
y = y + height - cr;
path.AddLineToPoint(x, y);
//bottom right arc
centerX = x - cr;
centerY = y;
path.AddArc(centerX, centerY, cr, 0f, (float)Math.PI / 2f, false);
//line to bottom left arc
x = startLeft + cr;
y = y + cr;
path.AddLineToPoint(x, y);
//bottom left arc
centerX = x;
centerY = y - cr;
path.AddArc(centerX, centerY, cr, (float)Math.PI / 2f, (float)Math.PI, false); //false to rotate clockwise
//line to handle
x = startLeft;
y = startTop + hl;
path.AddLineToPoint(x, y);
x = x - hl;
y = startTop;
path.AddLineToPoint(x, y);
}
path.CloseSubpath();
HandleStandardDraw(currentContext, rect, () => currentContext.AddPath(path));
}
/// <summary>
/// A simple method for handling our drawing of the shape. This method is called differently for each type of shape
/// </summary>
/// <param name="currentContext">Current context.</param>
/// <param name="rect">Rect.</param>
/// <param name="createPathForShape">Create path for shape.</param>
/// <param name="lineWidth">Line width.</param>
protected virtual void HandleStandardDraw(CGContext currentContext, RectangleF rect, Action createPathForShape, float? lineWidth = null)
{
currentContext.SetLineWidth(lineWidth ?? Element.StrokeWidth);
currentContext.SetFillColor(Element.InnerColor.ToCGColor());
currentContext.SetStrokeColor(Element.StrokeColor.ToCGColor());
createPathForShape();
currentContext.DrawPath(CoreGraphics.CGPathDrawingMode.FillStroke);
}
Xaml
<DataTemplate x:Key="OtherUserCommentTemplate">
<ViewCell>
<ViewCell.View>
<controls:ChatBubbleFrame x:Name="OtherUserBubble"
HandleIsBottomRight="False"
StrokeColor="{StaticResource Color13}"
InnerColor="White"
StrokeWidth="1"
CornerRadius="10"
HandleLength="7"
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"
Padding="-1,10,10,10">
<StackLayout Margin="10,10,10,10">
<AbsoluteLayout>
<Label x:Name="OtherUserDisplayName"
Text="{Binding Entity.UserInfoData.DisplayName}"
Style="{StaticResource Typo21}"
Margin="0,0,5,0"
HorizontalOptions="Start"
LineBreakMode="TailTruncation"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0,0,-1,-1" />
<Label Text="{Binding Entity.CommentDateTime,Converter={StaticResource DateTimeToPrettyTime}}"
Style="{StaticResource Typo22}"
Grid.Column="1"
Margin="0,0,5,0"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.48,0,-1,-1" />
<controls:ContentButton Command="{Binding Parent[NavigateToIndividualMessageVisibilityPage]}"
CommandParameter="{Binding Entity}"
HeightRequest="13"
Grid.Column="2"
HorizontalOptions="EndAndExpand"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="1.3,0.01,-1,-1">
<StackLayout Orientation="Horizontal"
Spacing="4">
<Image Source="{Binding Entity.IsVisibleToClient, Converter={StaticResource VisibleToClientToImage}}"
WidthRequest="12" />
<Image Source="{Binding Entity.IsVisibleToPartner, Converter={StaticResource VisibleToPartnersToImage}}"
WidthRequest="12" />
</StackLayout>
</controls:ContentButton>
</AbsoluteLayout>
<Label Text="{Binding Entity.CommentText }"
Style="{StaticResource Typo23}"
LineBreakMode="WordWrap"
Margin="0,6,0,0" />
<controls:FileAttachmentStack Orientation="Vertical"
ItemsSource="{Binding Entity.FileAttachmentList}"
ItemTemplate="{StaticResource ReceivedMessageImageTemplate}"
ItemSelectedCommand="{Binding Parent[DisplayCarousel], Converter={StaticResource ActiveCommandToCommand}}"
Spacing="5" />
</StackLayout>
</controls:ChatBubbleFrame>
</ViewCell.View>
</ViewCell>
</DataTemplate>