Open MunteanuGabi opened 9 months ago
I got around this by loading the data and doing the binding to the map before setting it to the Content of the page. Hopefully this helps and following to see if this gets improved.
`
public GritBinsPage(GritBinsViewModel viewModel) : base(viewModel)
{
Title = "Grit Bins"
_viewModel = viewModel;
BindingContext = _viewModel;
Content = LoadingPageTemplate.CreateLoadingTemplate("Grit Bins page");
}
protected override async void OnNavigatedTo(NavigatedToEventArgs args)
{
await InitMap();
base.OnAppearing();
}
private async Task InitMap()
{
_viewModel.Map.ItemsSource = _viewModel.Positions;
_viewModel.Map.ItemTemplate = new DataTemplate(() =>
{
Pin pin = new Pin().Bind(Pin.LocationProperty, nameof(GritBin.LatLonLocation))
.Bind(Pin.LabelProperty, nameof(GritBin.Location))
.Bind(Pin.AddressProperty, nameof(GritBin.Street));
return pin;
});
await _viewModel.GetGritBins();
Content = _viewModel.Map;
}`
Same thing happening to me. I need to load the pins dynamically so I cannot add them before the map is created.
I am not able to display more than 20 items on iOS. Is there a limit? Doesn't look like it but more than 20 will display nothing...
I got the same problem: once I open the map page adding pins dynamically I get freezing throughout the app.
I can confirm that the workaround by @Beryl0 solves the issue. I have the map in an absolute layout named MainLayout. I create a map and add the map in code in the OnNavigatedTo method. After adding the pins i add the map to the absolute layout. The map performance increased significantly, i have no more freezing. I solved the issue of dynamically adding the pins by triggering the InitMap() method with the collectionchanged event of the pin collection using a weak reference messenger.
private Map? map;
MapSpan? currentMapSpan;
private async Task InitMap()
{
try
{
if (MainLayout.Contains(map))
MainLayout.Remove(map);
map = new()
{
IsShowingUser = true,
IsTrafficEnabled = true,
ItemTemplate = new DataTemplate(() =>
{
CustomPin pin = new()
{
ImageSource = "aerolocationpin.png"
};
pin.Bind(CustomPin.AddressProperty, getter: static (ObservableVenue venue) => venue.Address);
pin.Bind(CustomPin.LabelProperty, getter: static (ObservableVenue venue) => venue.Name);
pin.Bind(CustomPin.LocationProperty,
binding1: new Binding(nameof(ObservableVenue.Latitude)),
binding2: new Binding(nameof(ObservableVenue.Longitude)),
mode: BindingMode.OneWay,
convert: ((double Latitude, double Longitude) values) => new Location(values.Latitude, values.Longitude));
pin.InfoWindowClicked += Pin_InfoWindowClicked;
return pin;
})
};
map.Bind(Map.ItemsSourceProperty, getter: static (MapViewViewModel vm) => vm.VenueDataService.Venues);
AbsoluteLayout.SetLayoutBounds(map, new Rect(0, 0, 1, 1));
AbsoluteLayout.SetLayoutFlags(map, AbsoluteLayoutFlags.All);
MainLayout.Insert(0, map);
if (MainLayout.Contains(activityIndicator)) MainLayout.Remove(activityIndicator);
if (currentMapSpan is null) { MainThread.BeginInvokeOnMainThread(CenterMap); } else { MainThread.BeginInvokeOnMainThread(() => map.MoveToRegion(currentMapSpan)); }
}
catch (Exception)
{
// Unable to render map
await _viewModel.DialogService.ShowAlertAsync("Warning", "The map could not be rendered. If this problem persists please write a report.", "OK");
}
}
First time loading map with 512 pins seems to be okay, albeit could be faster. I do something like this with a switch to remove Pins then re-add them again when switch toggled on, same 512 pins to be readded:
[RelayCommand(CanExecute = nameof(CanToggleMapPinsVisibilityCommandExecute))]
private void ToggleMapPinsVisibility(ToggledEventArgs? toggledEventArgs)
{
if (toggledEventArgs?.Value == true)
{
StoresMap.Pins.Clear();
foreach (CustomStorePin customStorePin in StoreLocatorPageViewModel.StoreLocatorListTabViewModel?.AllStoreLocations?.Select(GenerateCustomStorePin) ?? [])
{
StoresMap.Pins.Add(customStorePin); <====== iterating here takes very long!
}
}
else
{
CustomStorePins.Clear();
if (StoreLocatorPageViewModel is not null)
{
StoreLocatorPageViewModel.SelectedCustomStorePin = null;
}
}
}
Repopulating the map with the stopwatch running took 37 seconds.
Description
Adding a large number of pins to the map control takes an unreasonable amount of time and also the map freezes while doing the rendering for both Android and iOS. For example placing 1000 pins takes around 1 min to render. Obs1. During the freeze there are a lot of log messages regarding garbage collection like: [ark.android.dev] Explicit concurrent copying GC freed 102687(3555KB) AllocSpace objects, 0(0B) LOS objects, 49% free, 6444KB/12MB, paused 12us,10us total 12.616ms Obs2. If the number of pins is small(several dozens) then the rendering is instant. Obs3. On Android if the pins are added sooner than around 200ms then the pins adding works fine even with more than a thousand of pins, but adding them later than that results in freezing and the rendering issues mentioned Obs4. The old map from Xamarin forms works without any issues for the same large number of pins
Steps to Reproduce
Run the project and observe the map freezing and taking a lot of time for the pins rendering.
Link to public reproduction project repository
https://github.com/MunteanuGabi/MauiSamples
Version with bug
8.0.3
Is this a regression from previous behavior?
Not sure, did not test other versions
Last version that worked well
Unknown/Other
Affected platforms
iOS, Android
Affected platform versions
No response
Did you find any workaround?
No response
Relevant log output
No response