Closed orwo1 closed 3 years ago
I've encountered, and recently worked around this issue.
Basically as far as I can tell the image load is async, so the view finishes loading without the image being ready; which is why if you were to use an element with a background colour, it would render correctly.
The workaround for me was to generate the image as a bitmap using SkiaSharp without using the view and cache the result.
I don't imagine there will be a solve for Xamarin.Forms.GoogleMaps until we can block the UI in Xamarin Forms until all images have loaded unfortunately.
@JordanBird I have tried skipping the middleman and use BitmapDescriptorFactory.FromStream(), but I was getting almost the same results. Are you using SkiaSharp to generate a bitmap from your xaml view? or other source? How do you pass it from the shared project into the map control?
It's a bit of a hack.. But I use FFImageLoading to pull in an SVG file, I've reworked the code from my project a little below:
var iconWidth = 100;
var iconHeight = 100;
var stream = await ImageService.Instance.LoadEmbeddedResource($"resource://REMOVED.png?Assembly=REMOVED")
.WithCache(FFImageLoading.Cache.CacheType.Memory)
.AsPNGStreamAsync();
var half = (float)iconWidth / 2f;
var iconBitmap = new SKBitmap(iconWidth, iconHeight);
using (SKCanvas canvas = new SKCanvas(iconBitmap))
{
canvas.Clear();
canvas.DrawCircle(half, half, half, new Paint() { Color = new SKColor(255, 0, 0) });
var iconSkBitmap= SKBitmap.FromImage(SKImage.FromEncodedData(part));
canvas.DrawBitmap(iconSkBitmap, ((float)iconWidth - iconSkBitmap.Width) / 2f, ((float)iconHeight - iconSkBitmap.Height) / 2f);
}
Icon = BitmapDescriptorFactory.FromStream(SKImage.FromBitmap(iconBitmap).Encode(SKEncodedImageFormat.Png, 100).AsStream());
So if you're able to get your icon into a stream, you should be able to just replace the ImageService stuff with your implementation.
As for setting the Icon from the shared project, you'll need your observable collection of pins/dtos; and then set your data template up like the below:
<maps:Map.ItemTemplate>
<DataTemplate>
<maps:Pin Position="{Binding Position}" Label="{Binding Label}" Icon="{Binding MapIcon}"/>
</DataTemplate>
</maps:Map.ItemTemplate>
I've roughly pulled this out of our project, so it might not work fully as expected, but there's hopefully enough there to get you on the right path; it's the best workaround I've found so far for this icon issue unfortunately.
@JordanBird This solved my issue.
I used:
`var iconSkBitmap = SKBitmap.Decode(embeddedResourceStream);
var density = DeviceDisplay.MainDisplayInfo.Density;
var width = (int)Math.Round(density * iconSkBitmap.Width);
var height = (int)Math.Round(density * iconSkBitmap.Height);`
To get the icons height and width scaled up per platform.
Thank you so much for sharing this.
I still don't understand why simply using:
BitmapDescriptor.FromStream(embedded_png_stream)
wasn't good enough?
VERSIONS
PLATFORMS
ACTUAL BEHAVIOR
Getting a BitmapDescriptor from xaml view of Image, is not rendered on the map every time. When it is not rendered on the pin, no error is displayed, and no exception is thrown. The pin renders the image one out of every couple of tries.
ACTUAL SCREENSHOTS/STACKTRACE
https://pasteboard.co/JyCVGyR.gif
EXPECTED BEHAVIOR
Each time I navigate to page with a map control, and I add pins through MapObjects property, To have them rendered with the images provided.
HOW TO REPRODUCE
Xaml:
PinIconConverter returns BitmapDescriptorFactory.FromView(PinTypeOneView/PinTypeTwoView);