Add MoveToRegion animation control, MapSpan.FromLocations, and fix initial map animation#33982
Add MoveToRegion animation control, MapSpan.FromLocations, and fix initial map animation#33982jfversluis merged 5 commits intonet11.0from
Conversation
There was a problem hiding this comment.
Pull request overview
This PR extends the .NET MAUI Maps control camera API to support non-animated region moves, adds a helper to compute a “best fit” MapSpan from multiple locations, and updates samples/tests to exercise the new behavior (including avoiding the initial “Maui island fly-in” animation).
Changes:
- Add
IMap.MoveToRegion(MapSpan region, bool animated)andMap.MoveToRegion(MapSpan mapSpan, bool animated), plus aMoveToRegionRequestcarrier for handler communication. - Add
MapSpan.FromLocations(IEnumerable<Location>)to compute a padded bounding span for a set of locations. - Update Android/iOS handlers, unit tests, and the Controls sample gallery with a new “Camera & Zoom” page.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Core/maps/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt | Declares new Core Maps public APIs (IMap overload, MoveToRegionRequest, MapSpan.FromLocations). |
| src/Core/maps/src/PublicAPI/net/PublicAPI.Unshipped.txt | Declares new Core Maps public APIs (net). |
| src/Core/maps/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt | Declares new Core Maps public APIs (net-windows). |
| src/Core/maps/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt | Declares new Core Maps public APIs (net-tizen). |
| src/Core/maps/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt | Declares new Core Maps public APIs (net-maccatalyst). |
| src/Core/maps/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt | Declares new Core Maps public APIs (net-ios). |
| src/Core/maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt | Declares new Core Maps public APIs (net-android). |
| src/Core/maps/src/Primitives/MoveToRegionRequest.cs | Adds request type to convey region + animation flag to handlers. |
| src/Core/maps/src/Primitives/MapSpan.cs | Adds MapSpan.FromLocations best-fit span computation. |
| src/Core/maps/src/Handlers/Map/MapHandler.iOS.cs | Updates MoveToRegion command handling to support MoveToRegionRequest + animated flag. |
| src/Core/maps/src/Handlers/Map/MapHandler.Android.cs | Updates MoveToRegion command handling to support MoveToRegionRequest + animated flag. |
| src/Core/maps/src/Core/IMap.cs | Adds MoveToRegion(MapSpan, bool animated) to the IMap contract. |
| src/Controls/Maps/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt | Declares new Controls Maps public API for Map.MoveToRegion(MapSpan, bool). |
| src/Controls/Maps/src/PublicAPI/net/PublicAPI.Unshipped.txt | Declares new Controls Maps public API (net). |
| src/Controls/Maps/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt | Declares new Controls Maps public API (net-windows). |
| src/Controls/Maps/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt | Declares new Controls Maps public API (net-tizen). |
| src/Controls/Maps/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt | Declares new Controls Maps public API (net-maccatalyst). |
| src/Controls/Maps/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt | Declares new Controls Maps public API (net-ios). |
| src/Controls/Maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt | Declares new Controls Maps public API (net-android). |
| src/Controls/Maps/src/Map.cs | Adds public MoveToRegion(MapSpan, bool) overload and adjusts handler invocation payload. |
| src/Controls/Maps/src/HandlerImpl/Map.Impl.cs | Implements new IMap overload and invokes initial region move with animated: false. |
| src/Controls/tests/Core.UnitTests/MapTests.cs | Adds unit tests for null handling and MoveToRegionRequest basics. |
| src/Controls/tests/Core.UnitTests/MapSpanTests.cs | Adds unit tests for MapSpan.FromLocations behavior and argument validation. |
| src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapsGallery.cs | Adds navigation entry for the new camera/zoom sample gallery page. |
| src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/CameraZoomGallery.xaml.cs | Implements sample interactions demonstrating best-fit and animated vs instant camera moves. |
| src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/CameraZoomGallery.xaml | Adds UI for “Fit All Pins”, “Animated”, and “Instant” behaviors. |
| double minLat = double.MaxValue, maxLat = double.MinValue; | ||
| double minLon = double.MaxValue, maxLon = double.MinValue; | ||
|
|
||
| foreach (var loc in locationList) | ||
| { | ||
| minLat = Math.Min(minLat, loc.Latitude); | ||
| maxLat = Math.Max(maxLat, loc.Latitude); | ||
| minLon = Math.Min(minLon, loc.Longitude); | ||
| maxLon = Math.Max(maxLon, loc.Longitude); | ||
| } | ||
|
|
||
| double centerLat = (minLat + maxLat) / 2; | ||
| double centerLon = (minLon + maxLon) / 2; | ||
| double latDegrees = (maxLat - minLat) * 1.1; // 10% padding | ||
| double lonDegrees = (maxLon - minLon) * 1.1; | ||
|
|
||
| // Ensure minimum span | ||
| latDegrees = Math.Max(latDegrees, MinimumRangeDegrees); | ||
| lonDegrees = Math.Max(lonDegrees, MinimumRangeDegrees); | ||
|
|
||
| return new MapSpan(new Location(centerLat, centerLon), latDegrees, lonDegrees); | ||
| } |
There was a problem hiding this comment.
MapSpan.FromLocations computes the longitude bounds using simple min/max. This fails for points that cross the antimeridian (e.g., 179° and -179°): maxLon - minLon becomes ~358°, then the MapSpan constructor clamps LongitudeDegrees to 180°, producing a span that may not include either point. The implementation needs to account for longitude wraparound (choose the smaller arc across ±180° and compute the correct center/span) and should have a unit test covering the dateline case.
60244f3 to
bff695f
Compare
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
Independent Code ReviewSummary: Adds ✅ What Looks Good
🔴 Issues1. void MoveToRegion(MapSpan region, bool animated) => MoveToRegion(region);2. 3. Code duplication public void MoveToRegion(MapSpan mapSpan) => MoveToRegion(mapSpan, true);🟡 Nits
|
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 33982Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 33982" |
|
Thanks for the review @kubaflo! Addressed the items: Fixed in latest commit:
Responses to other points: 1. Breaking IMap interface change — \IMap\ is an internal contract between the MAUI control and its handler, not intended for external implementation. These are new .NET 11 APIs. A default interface implementation isn't needed since there are no external consumers. 2. MoveToRegionRequest allows null region — This is intentional. The handler checks for null and no-ops, which is the correct behavior when the map is first created and no region has been set yet. The initial \OnHandlerChanged\ call passes _lastMoveToRegion\ which starts as null. 3. FromLocations ToList() allocation — For typical use cases (< 100 pins), this is negligible. Using \ToList()\ enables \Count\ check and random access for the single-element optimization. A future enhancement could use \TryGetNonEnumeratedCount\ but the benefit doesn't justify the complexity for now. |
🔍 Round 2 Review — PR #33982 (MoveToRegion animated)Updated with author response analysis ✅ Fixed Since Round 1
|
|
/azp run maui-pr |
|
Azure Pipelines successfully started running 1 pipeline(s). |
📋 Round 3 — PR #33982 (MoveToRegion animated) — Final RecommendationNo outstanding issues. All Round 2 concerns were satisfactorily addressed. The Recommendation: Merge when CI passes. Clean and ready. The |
594a755 to
854a3b4
Compare
…itial map animation - Add MoveToRegion(MapSpan, bool animated) overload to IMap and Map - Add MoveToRegionRequest class for handler communication - Android: AnimateCamera vs MoveCamera based on animated flag - iOS: MKMapView.SetRegion with animated parameter - Add MapSpan.FromLocations() to compute best-fit span for pins - Fix OnHandlerChanged to use animated:false (no Maui island fly-in) - Add 9 unit tests for new functionality - Add CameraZoomGallery sample page Fixes #21046 Fixes #10718 Fixes #11332
- Handle longitude wraparound when locations span the antimeridian - Choose the shorter arc across ±180° for correct center/span calculation - MoveToRegion handler already supports both MapSpan and MoveToRegionRequest
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Reduce code duplication: MoveToRegion(MapSpan) now delegates to MoveToRegion(MapSpan, bool) with animated=true - Add antimeridian crossing unit test to verify MapSpan.FromLocations correctly handles points near ±180° longitude Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
854a3b4 to
1cbd4f0
Compare
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description
This PR adds camera and zoom control improvements to the Maps control:
1. MoveToRegion Animation Control (Fixes #21046)
MoveToRegion(MapSpan region, bool animated)overload toIMapandMapAnimateCamerawhen animated=true,MoveCamerawhen animated=falseMKMapView.SetRegionOnHandlerChangednow usesanimated: false, so when you set an initial region, it appears instantly without the jarring animation from the Maui island default position2. MapSpan.FromLocations - Best Fit Zoom (Fixes #10718)
MapSpan.FromLocations(IEnumerable<Location>)static method3. MoveToRegionRequest
MoveToRegionRequestclass wrappingMapSpan+bool Animatedfor handler communicationMoveToRegionRequestand rawMapSpanRelated: Zoom Level Property (#11332)
MoveToRegion(animated)+MapSpan.FromLocations()+ existingVisibleRegionprovides comprehensive camera/zoom controlZoomLevelproperty may be added as a future enhancementNew Public API
Testing
Part of Epic: Maps Control Improvements for .NET 11
Fixes #21046
Fixes #10718
Fixes #11332