From 22cc736f52e64a2d725547aa58856d778149857b Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Fri, 2 Aug 2024 15:04:11 +0300 Subject: [PATCH 1/8] [google_maps_flutter_ios] Add support for cluster markering --- .../google_maps_flutter_ios/CHANGELOG.md | 4 + .../integration_test/google_maps_test.dart | 102 +++++++ .../ios/Runner.xcodeproj/project.pbxproj | 10 +- .../FLTClusterManagersControllerTests.m | 128 ++++++++ .../example/ios14/lib/main.dart | 2 + .../example/ios15/lib/main.dart | 2 + .../maps_example_dart/lib/clustering.dart | 278 ++++++++++++++++++ .../lib/example_google_map.dart | 32 ++ .../fake_google_maps_flutter_platform.dart | 18 ++ .../Classes/FLTClusterManagersController.h | 69 +++++ .../Classes/FLTClusterManagersController.m | 143 +++++++++ .../ios/Classes/FLTGoogleMapJSONConversions.h | 4 + .../ios/Classes/FLTGoogleMapJSONConversions.m | 18 ++ .../ios/Classes/FLTGoogleMapsPlugin.h | 1 + .../ios/Classes/GoogleMapController.h | 1 + .../ios/Classes/GoogleMapController.m | 69 ++++- .../ios/Classes/GoogleMapMarkerController.h | 17 +- .../ios/Classes/GoogleMapMarkerController.m | 133 ++++++--- .../ios/Classes/GoogleMarkerUserData.h | 17 ++ .../ios/Classes/GoogleMarkerUserData.m | 9 + .../ios/Classes/GoogleMarkerUtilities.h | 38 +++ .../ios/Classes/GoogleMarkerUtilities.m | 38 +++ .../google_maps_flutter_ios-umbrella.h | 2 + .../ios/Classes/messages.g.h | 35 +++ .../ios/Classes/messages.g.m | 194 ++++++++++-- .../lib/src/google_map_inspector_ios.dart | 14 + .../lib/src/google_maps_flutter_ios.dart | 53 ++++ .../lib/src/messages.g.dart | 198 +++++++++++-- .../lib/src/utils/cluster_manager.dart | 34 +++ .../pigeons/messages.dart | 41 +++ .../google_maps_flutter_ios/pubspec.yaml | 2 +- .../test/cluster_manager_utils_test.dart | 66 +++++ .../google_maps_flutter_ios_test.mocks.dart | 17 ++ 33 files changed, 1687 insertions(+), 102 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/clustering.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.h create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.m create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.h create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.m create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/lib/src/utils/cluster_manager.dart create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/test/cluster_manager_utils_test.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index cb038750c13..381ebf6f5d4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.12.0 + +* Adds support for marker clustering. + ## 2.11.0 * Adds support for heatmap layers. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart index db8b3956763..d3ea025d99c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart @@ -1030,6 +1030,85 @@ void main() { }, ); + testWidgets('marker clustering', (WidgetTester tester) async { + final Key key = GlobalKey(); + const int clusterManagersAmount = 2; + const int markersPerClusterManager = 5; + final Map markers = {}; + final Set clusterManagers = {}; + + for (int i = 0; i < clusterManagersAmount; i++) { + final ClusterManagerId clusterManagerId = + ClusterManagerId('cluster_manager_$i'); + final ClusterManager clusterManager = + ClusterManager(clusterManagerId: clusterManagerId); + clusterManagers.add(clusterManager); + } + + for (final ClusterManager cm in clusterManagers) { + for (int i = 0; i < markersPerClusterManager; i++) { + final MarkerId markerId = + MarkerId('${cm.clusterManagerId.value}_marker_$i'); + final Marker marker = Marker( + markerId: markerId, + clusterManagerId: cm.clusterManagerId, + position: LatLng( + _kInitialMapCenter.latitude + i, _kInitialMapCenter.longitude)); + markers[markerId] = marker; + } + } + + final Completer controllerCompleter = + Completer(); + + final GoogleMapsInspectorPlatform inspector = + GoogleMapsInspectorPlatform.instance!; + + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + clusterManagers: clusterManagers, + markers: Set.of(markers.values), + onMapCreated: (ExampleGoogleMapController googleMapController) { + controllerCompleter.complete(googleMapController); + }, + ), + )); + + final ExampleGoogleMapController controller = + await controllerCompleter.future; + + for (final ClusterManager cm in clusterManagers) { + final List clusters = await inspector.getClusters( + mapId: controller.mapId, clusterManagerId: cm.clusterManagerId); + final int markersAmountForClusterManager = clusters + .map((Cluster cluster) => cluster.count) + .reduce((int value, int element) => value + element); + expect(markersAmountForClusterManager, markersPerClusterManager); + } + + // Remove markers from clusterManagers and test that clusterManagers are empty. + for (final MapEntry entry in markers.entries) { + markers[entry.key] = _copyMarkerWithClusterManagerId(entry.value, null); + } + await tester.pumpWidget(Directionality( + textDirection: TextDirection.ltr, + child: ExampleGoogleMap( + key: key, + initialCameraPosition: _kInitialCameraPosition, + clusterManagers: clusterManagers, + markers: Set.of(markers.values)), + )); + + for (final ClusterManager cm in clusterManagers) { + final List clusters = await inspector.getClusters( + mapId: controller.mapId, clusterManagerId: cm.clusterManagerId); + expect(clusters.length, 0); + } + }); + testWidgets('testSetStyleMapId', (WidgetTester tester) async { final Key key = GlobalKey(); @@ -1254,3 +1333,26 @@ class _DebugTileProvider implements TileProvider { return Tile(width, height, byteData); } } + +Marker _copyMarkerWithClusterManagerId( + Marker marker, ClusterManagerId? clusterManagerId) { + return Marker( + markerId: marker.markerId, + alpha: marker.alpha, + anchor: marker.anchor, + consumeTapEvents: marker.consumeTapEvents, + draggable: marker.draggable, + flat: marker.flat, + icon: marker.icon, + infoWindow: marker.infoWindow, + position: marker.position, + rotation: marker.rotation, + visible: marker.visible, + zIndex: marker.zIndex, + onTap: marker.onTap, + onDragStart: marker.onDragStart, + onDrag: marker.onDrag, + onDragEnd: marker.onDragEnd, + clusterManagerId: clusterManagerId, + ); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj index cb8922d30b6..5ef22647478 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */; }; 521AB0032B876A76005F460D /* ExtractIconFromDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 521AB0022B876A76005F460D /* ExtractIconFromDataTests.m */; }; + 52E9C2AE2C5CEBD600078060 /* FLTClusterManagersControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 52E9C2AD2C5CEBD600078060 /* FLTClusterManagersControllerTests.m */; }; 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; }; 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E472692836FF0C00BDDDAC /* MapKit.framework */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; @@ -66,6 +67,7 @@ 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsPolylinesControllerTests.m; sourceTree = ""; }; 521AB0022B876A76005F460D /* ExtractIconFromDataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExtractIconFromDataTests.m; sourceTree = ""; }; 61A9A8623F5CA9BBC813DC6B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 52E9C2AD2C5CEBD600078060 /* FLTClusterManagersControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTClusterManagersControllerTests.m; sourceTree = ""; }; 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = ""; }; 68E472692836FF0C00BDDDAC /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/iOSSupport/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; }; 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; @@ -205,14 +207,15 @@ isa = PBXGroup; children = ( F269303A2BB389BF00BF17C4 /* assets */, + 52E9C2AF2C5CECD100078060 /* ExtractIconFromDataTests.m */, + 52E9C2AD2C5CEBD600078060 /* FLTClusterManagersControllerTests.m */, 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */, - 521AB0022B876A76005F460D /* ExtractIconFromDataTests.m */, + 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */, F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */, 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */, 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */, F7151F14265D7ED70028CB91 /* Info.plist */, - 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */, ); path = RunnerTests; sourceTree = ""; @@ -509,11 +512,12 @@ buildActionMask = 2147483647; files = ( F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, + 52E9C2AE2C5CEBD600078060 /* FLTClusterManagersControllerTests.m in Sources */, 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */, 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */, 0DD7B6C32B744EEF00E857FD /* FLTTileProviderControllerTests.m in Sources */, - 521AB0032B876A76005F460D /* ExtractIconFromDataTests.m in Sources */, + 52E9C2B02C5CECD100078060 /* ExtractIconFromDataTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m new file mode 100644 index 00000000000..754abf1f6da --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m @@ -0,0 +1,128 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +@import google_maps_flutter_ios; +@import google_maps_flutter_ios.Test; +@import XCTest; +@import GoogleMaps; + +#import +#import +#import "PartiallyMockedMapView.h" + +@interface FLTClusterManagersControllerTests : XCTestCase +@end + +@implementation FLTClusterManagersControllerTests + +- (void)testClustering { + NSObject *registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); + CGRect frame = CGRectMake(0, 0, 100, 100); + + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + id handler = OCMClassMock([FGMMapsCallbackApi class]); + + FLTClusterManagersController *clusterManagersController = + [[FLTClusterManagersController alloc] initWithCallbackHandler:handler mapView:mapView]; + FLTMarkersController *markersController = + [[FLTMarkersController alloc] initWithClusterManagersController:clusterManagersController + callbackHandler:handler + mapView:mapView + registrar:registrar]; + + // Add cluster managers. + NSString *clusterManagerId = @"cm"; + FGMPlatformClusterManager *clusterManagerToAdd = + [FGMPlatformClusterManager makeWithIdentifier:clusterManagerId]; + [clusterManagersController addClusterManagers:@[ clusterManagerToAdd ]]; + + // Add cluster managers in JSON format. + NSString *JSONClusterManagerId = @"json_cm"; + NSDictionary *JSONclusterManagerToAdd = @{@"clusterManagerId" : JSONClusterManagerId}; + [clusterManagersController addJSONClusterManagers:@[ JSONclusterManagerToAdd ]]; + + // Verify that cluster managers are available + GMUClusterManager *clusterManager = + [clusterManagersController clusterManagerWithIdentifier:clusterManagerId]; + XCTAssertNotNil(clusterManager, @"Cluster Manager should not be nil"); + GMUClusterManager *JSONClusterManager = + [clusterManagersController clusterManagerWithIdentifier:JSONClusterManagerId]; + XCTAssertNotNil(JSONClusterManager, @"Cluster Manager should not be nil"); + + // Add markers + NSString *markerId1 = @"m1"; + NSString *markerId2 = @"m2"; + + FGMPlatformMarker *marker1 = [FGMPlatformMarker makeWithJson:@{ + @"markerId" : markerId1, + @"position" : @[ @0, @0 ], + @"clusterManagerId" : clusterManagerId + }]; + NSDictionary *marker2 = + @{@"markerId" : markerId2, @"position" : @[ @0, @0 ], @"clusterManagerId" : clusterManagerId}; + + [markersController addMarkers:@[ marker1 ]]; + [markersController addJSONMarkers:@[ marker2 ]]; + + FlutterError *error = nil; + + // Invoke clustering + [clusterManagersController invokeClusteringForEachClusterManager]; + + // Verify that the markers were added to the cluster manager + NSArray *clusters1 = + [clusterManagersController getClustersWithIdentifier:clusterManagerId error:&error]; + XCTAssertNil(error, @"Error should be nil"); + for (FGMPlatformCluster *cluster in clusters1) { + NSString *cmId = cluster.clusterManagerId; + XCTAssertNotNil(cmId, @"Cluster Manager Identifier should not be nil"); + if ([cmId isEqualToString:clusterManagerId]) { + NSArray *markerIds = cluster.markerIds; + XCTAssertEqual(markerIds.count, 2, @"Cluster should contain two marker"); + XCTAssertTrue([markerIds containsObject:markerId1], @"Cluster should contain markerId1"); + XCTAssertTrue([markerIds containsObject:markerId2], @"Cluster should contain markerId2"); + return; + } + } + + [markersController removeMarkersWithIdentifiers:@[ markerId2 ]]; + + // Verify that the marker2 is removed from the clusterManager + NSArray *clusters2 = + [clusterManagersController getClustersWithIdentifier:clusterManagerId error:&error]; + XCTAssertNil(error, @"Error should be nil"); + + for (FGMPlatformCluster *cluster in clusters2) { + NSString *cmId = cluster.clusterManagerId; + XCTAssertNotNil(cmId, @"Cluster Manager ID should not be nil"); + if ([cmId isEqualToString:clusterManagerId]) { + NSArray *markerIds = cluster.markerIds; + XCTAssertEqual(markerIds.count, 1, @"Cluster should contain one marker"); + XCTAssertTrue([markerIds containsObject:markerId1], @"Cluster should contain markerId1"); + return; + } + } + + [markersController removeMarkersWithIdentifiers:@[ markerId1 ]]; + + // Verify that all markers are removed from clusterManager + NSArray *clusters3 = + [clusterManagersController getClustersWithIdentifier:clusterManagerId error:&error]; + XCTAssertNil(error, @"Error should be nil"); + XCTAssertEqual(clusters3.count, 0, @"Cluster Manager should not contain any clusters"); + + // Remove cluster manager + [clusterManagersController removeClusterManagersWithIdentifiers:@[ clusterManagerId ]]; + + // Verify that the cluster manager is removed + clusterManager = [clusterManagersController clusterManagerWithIdentifier:clusterManagerId]; + XCTAssertNil(clusterManager, @"Cluster Manager should be nil"); +} + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/lib/main.dart index 09fa814fdcf..3144c2aff5e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:maps_example_dart/animate_camera.dart'; +import 'package:maps_example_dart/clustering.dart'; import 'package:maps_example_dart/lite_mode.dart'; import 'package:maps_example_dart/map_click.dart'; import 'package:maps_example_dart/map_coordinates.dart'; @@ -40,6 +41,7 @@ void main() { SnapshotPage(), LiteModePage(), TileOverlayPage(), + ClusteringPage(), MapIdPage(), ]))); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/lib/main.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/lib/main.dart index 09fa814fdcf..3144c2aff5e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/lib/main.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios15/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:maps_example_dart/animate_camera.dart'; +import 'package:maps_example_dart/clustering.dart'; import 'package:maps_example_dart/lite_mode.dart'; import 'package:maps_example_dart/map_click.dart'; import 'package:maps_example_dart/map_coordinates.dart'; @@ -40,6 +41,7 @@ void main() { SnapshotPage(), LiteModePage(), TileOverlayPage(), + ClusteringPage(), MapIdPage(), ]))); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/clustering.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/clustering.dart new file mode 100644 index 00000000000..f6860f7bf42 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/clustering.dart @@ -0,0 +1,278 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +import 'example_google_map.dart'; +import 'page.dart'; + +/// Page for demonstrating marker clustering support. +class ClusteringPage extends GoogleMapExampleAppPage { + /// Default Constructor. + const ClusteringPage({Key? key}) + : super(const Icon(Icons.place), 'Manage clustering', key: key); + + @override + Widget build(BuildContext context) { + return const ClusteringBody(); + } +} + +/// Body of the clustering page. +class ClusteringBody extends StatefulWidget { + /// Default Constructor. + const ClusteringBody({super.key}); + + @override + State createState() => ClusteringBodyState(); +} + +/// State of the clustering page. +class ClusteringBodyState extends State { + /// Default Constructor. + ClusteringBodyState(); + + /// Starting point from where markers are added. + static const LatLng center = LatLng(-33.86, 151.1547171); + + /// Marker offset factor for randomizing marker placing. + static const double _markerOffsetFactor = 0.05; + + /// Offset for longitude when placing markers to different cluster managers. + static const double _clusterManagerLongitudeOffset = 0.1; + + /// Maximum amount of cluster managers. + static const int _clusterManagerMaxCount = 3; + + /// Amount of markers to be added to the cluster manager at once. + static const int _markersToAddToClusterManagerCount = 10; + + /// Fully visible alpha value. + static const double _fullyVisibleAlpha = 1.0; + + /// Half visible alpha value. + static const double _halfVisibleAlpha = 0.5; + + /// Google map controller. + ExampleGoogleMapController? controller; + + /// Map of clusterManagers with identifier as the key. + Map clusterManagers = + {}; + + /// Map of markers with identifier as the key. + Map markers = {}; + + /// Id of the currently selected marker. + MarkerId? selectedMarker; + + /// Counter for added cluster manager ids. + int _clusterManagerIdCounter = 1; + + /// Counter for added markers ids. + int _markerIdCounter = 1; + + /// Cluster that was tapped most recently. + Cluster? lastCluster; + + // ignore: use_setters_to_change_properties + void _onMapCreated(ExampleGoogleMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + void _onMarkerTapped(MarkerId markerId) { + final Marker? tappedMarker = markers[markerId]; + if (tappedMarker != null) { + setState(() { + final MarkerId? previousMarkerId = selectedMarker; + if (previousMarkerId != null && markers.containsKey(previousMarkerId)) { + final Marker resetOld = markers[previousMarkerId]! + .copyWith(iconParam: BitmapDescriptor.defaultMarker); + markers[previousMarkerId] = resetOld; + } + selectedMarker = markerId; + final Marker newMarker = tappedMarker.copyWith( + iconParam: BitmapDescriptor.defaultMarkerWithHue( + BitmapDescriptor.hueGreen, + ), + ); + markers[markerId] = newMarker; + }); + } + } + + void _addClusterManager() { + if (clusterManagers.length == _clusterManagerMaxCount) { + return; + } + + final String clusterManagerIdVal = + 'cluster_manager_id_$_clusterManagerIdCounter'; + _clusterManagerIdCounter++; + final ClusterManagerId clusterManagerId = + ClusterManagerId(clusterManagerIdVal); + + final ClusterManager clusterManager = ClusterManager( + clusterManagerId: clusterManagerId, + onClusterTap: (Cluster cluster) => setState(() { + lastCluster = cluster; + }), + ); + + setState(() { + clusterManagers[clusterManagerId] = clusterManager; + }); + _addMarkersToCluster(clusterManager); + } + + void _removeClusterManager(ClusterManager clusterManager) { + setState(() { + // Remove markers managed by cluster manager to be removed. + markers.removeWhere((MarkerId key, Marker marker) => + marker.clusterManagerId == clusterManager.clusterManagerId); + // Remove cluster manager. + clusterManagers.remove(clusterManager.clusterManagerId); + }); + } + + void _addMarkersToCluster(ClusterManager clusterManager) { + for (int i = 0; i < _markersToAddToClusterManagerCount; i++) { + final String markerIdVal = + '${clusterManager.clusterManagerId.value}_marker_id_$_markerIdCounter'; + _markerIdCounter++; + final MarkerId markerId = MarkerId(markerIdVal); + + final int clusterManagerIndex = + clusterManagers.values.toList().indexOf(clusterManager); + + // Add additional offset to longitude for each cluster manager to space + // out markers in different cluster managers. + final double clusterManagerLongitudeOffset = + clusterManagerIndex * _clusterManagerLongitudeOffset; + + final Marker marker = Marker( + clusterManagerId: clusterManager.clusterManagerId, + markerId: markerId, + position: LatLng( + center.latitude + _getRandomOffset(), + center.longitude + _getRandomOffset() + clusterManagerLongitudeOffset, + ), + infoWindow: InfoWindow(title: markerIdVal, snippet: '*'), + onTap: () => _onMarkerTapped(markerId), + ); + markers[markerId] = marker; + } + setState(() {}); + } + + double _getRandomOffset() { + return (Random().nextDouble() - 0.5) * _markerOffsetFactor; + } + + void _remove(MarkerId markerId) { + setState(() { + if (markers.containsKey(markerId)) { + markers.remove(markerId); + } + }); + } + + void _changeMarkersAlpha() { + for (final MarkerId markerId in markers.keys) { + final Marker marker = markers[markerId]!; + final double current = marker.alpha; + markers[markerId] = marker.copyWith( + alphaParam: current == _fullyVisibleAlpha + ? _halfVisibleAlpha + : _fullyVisibleAlpha, + ); + } + setState(() {}); + } + + @override + Widget build(BuildContext context) { + final MarkerId? selectedId = selectedMarker; + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + SizedBox( + height: 300.0, + child: ExampleGoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition( + target: LatLng(-33.852, 151.25), + zoom: 11.0, + ), + markers: Set.of(markers.values), + clusterManagers: Set.of(clusterManagers.values), + ), + ), + Column(children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: clusterManagers.length >= _clusterManagerMaxCount + ? null + : () => _addClusterManager(), + child: const Text('Add cluster manager'), + ), + TextButton( + onPressed: clusterManagers.isEmpty + ? null + : () => _removeClusterManager(clusterManagers.values.last), + child: const Text('Remove cluster manager'), + ), + ], + ), + Wrap( + alignment: WrapAlignment.spaceEvenly, + children: [ + for (final MapEntry clusterEntry + in clusterManagers.entries) + TextButton( + onPressed: () => _addMarkersToCluster(clusterEntry.value), + child: Text('Add markers to ${clusterEntry.key.value}'), + ), + ], + ), + Wrap( + alignment: WrapAlignment.spaceEvenly, + children: [ + TextButton( + onPressed: selectedId == null + ? null + : () { + _remove(selectedId); + setState(() { + selectedMarker = null; + }); + }, + child: const Text('Remove selected marker'), + ), + TextButton( + onPressed: markers.isEmpty ? null : () => _changeMarkersAlpha(), + child: const Text('Change all markers alpha'), + ), + ], + ), + if (lastCluster != null) + Padding( + padding: const EdgeInsets.all(10), + child: Text( + 'Cluster with ${lastCluster!.count} markers clicked at ${lastCluster!.position}')), + ]), + ], + ); + } +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/example_google_map.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/example_google_map.dart index 0734731af7c..fcf24452c87 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/example_google_map.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/lib/example_google_map.dart @@ -87,6 +87,9 @@ class ExampleGoogleMapController { .listen((MapTapEvent e) => _googleMapState.onTap(e.position)); GoogleMapsFlutterPlatform.instance.onLongPress(mapId: mapId).listen( (MapLongPressEvent e) => _googleMapState.onLongPress(e.position)); + GoogleMapsFlutterPlatform.instance + .onClusterTap(mapId: mapId) + .listen((ClusterTapEvent e) => _googleMapState.onClusterTap(e.value)); } /// Updates configuration options of the map user interface. @@ -101,6 +104,13 @@ class ExampleGoogleMapController { .updateMarkers(markerUpdates, mapId: mapId); } + /// Updates cluster manager configuration. + Future _updateClusterManagers( + ClusterManagerUpdates clusterManagerUpdates) { + return GoogleMapsFlutterPlatform.instance + .updateClusterManagers(clusterManagerUpdates, mapId: mapId); + } + /// Updates polygon configuration. Future _updatePolygons(PolygonUpdates polygonUpdates) { return GoogleMapsFlutterPlatform.instance @@ -237,6 +247,7 @@ class ExampleGoogleMap extends StatefulWidget { this.polygons = const {}, this.polylines = const {}, this.circles = const {}, + this.clusterManagers = const {}, this.onCameraMoveStarted, this.tileOverlays = const {}, this.onCameraMove, @@ -312,6 +323,9 @@ class ExampleGoogleMap extends StatefulWidget { /// Tile overlays to be placed on the map. final Set tileOverlays; + /// Cluster Managers to be placed for the map. + final Set clusterManagers; + /// Called when the camera starts moving. final VoidCallback? onCameraMoveStarted; @@ -371,6 +385,8 @@ class _ExampleGoogleMapState extends State { Map _polygons = {}; Map _polylines = {}; Map _circles = {}; + Map _clusterManagers = + {}; late MapConfiguration _mapConfiguration; @override @@ -390,6 +406,7 @@ class _ExampleGoogleMapState extends State { polygons: widget.polygons, polylines: widget.polylines, circles: widget.circles, + clusterManagers: widget.clusterManagers, ), mapConfiguration: _mapConfiguration, ); @@ -399,6 +416,7 @@ class _ExampleGoogleMapState extends State { void initState() { super.initState(); _mapConfiguration = _configurationFromMapWidget(widget); + _clusterManagers = keyByClusterManagerId(widget.clusterManagers); _markers = keyByMarkerId(widget.markers); _polygons = keyByPolygonId(widget.polygons); _polylines = keyByPolylineId(widget.polylines); @@ -416,6 +434,7 @@ class _ExampleGoogleMapState extends State { void didUpdateWidget(ExampleGoogleMap oldWidget) { super.didUpdateWidget(oldWidget); _updateOptions(); + _updateClusterManagers(); _updateMarkers(); _updatePolygons(); _updatePolylines(); @@ -441,6 +460,13 @@ class _ExampleGoogleMapState extends State { _markers = keyByMarkerId(widget.markers); } + Future _updateClusterManagers() async { + final ExampleGoogleMapController controller = await _controller.future; + unawaited(controller._updateClusterManagers(ClusterManagerUpdates.from( + _clusterManagers.values.toSet(), widget.clusterManagers))); + _clusterManagers = keyByClusterManagerId(widget.clusterManagers); + } + Future _updatePolygons() async { final ExampleGoogleMapController controller = await _controller.future; unawaited(controller._updatePolygons( @@ -518,6 +544,12 @@ class _ExampleGoogleMapState extends State { void onLongPress(LatLng position) { widget.onLongPress?.call(position); } + + void onClusterTap(Cluster cluster) { + final ClusterManager? clusterManager = + _clusterManagers[cluster.clusterManagerId]; + clusterManager?.onClusterTap?.call(cluster); + } } /// Builds a [MapConfiguration] from the given [map]. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/test/fake_google_maps_flutter_platform.dart b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/test/fake_google_maps_flutter_platform.dart index 22447ba5eca..9ac70ab760f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/test/fake_google_maps_flutter_platform.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/shared/maps_example_dart/test/fake_google_maps_flutter_platform.dart @@ -94,6 +94,15 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { await _fakeDelay(); } + @override + Future updateClusterManagers( + ClusterManagerUpdates clusterManagerUpdates, { + required int mapId, + }) async { + mapInstances[mapId]?.clusterManagerUpdates.add(clusterManagerUpdates); + await _fakeDelay(); + } + @override Future clearTileCache( TileOverlayId tileOverlayId, { @@ -241,6 +250,11 @@ class FakeGoogleMapsFlutterPlatform extends GoogleMapsFlutterPlatform { return mapEventStreamController.stream.whereType(); } + @override + Stream onClusterTap({required int mapId}) { + return mapEventStreamController.stream.whereType(); + } + @override void dispose({required int mapId}) { disposed = true; @@ -282,6 +296,8 @@ class PlatformMapStateRecorder { this.mapObjects = const MapObjects(), this.mapConfiguration = const MapConfiguration(), }) { + clusterManagerUpdates.add(ClusterManagerUpdates.from( + const {}, mapObjects.clusterManagers)); markerUpdates.add(MarkerUpdates.from(const {}, mapObjects.markers)); polygonUpdates .add(PolygonUpdates.from(const {}, mapObjects.polygons)); @@ -300,4 +316,6 @@ class PlatformMapStateRecorder { final List polylineUpdates = []; final List circleUpdates = []; final List> tileOverlaySets = >[]; + final List clusterManagerUpdates = + []; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h new file mode 100644 index 00000000000..dcde3d09409 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h @@ -0,0 +1,69 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +@import GoogleMapsUtils; + +#import "messages.g.h" + +NS_ASSUME_NONNULL_BEGIN + +// Defines cluster managers controller interface which +// is responsible for adding/removing/returning one or more cluster managers. +@interface FLTClusterManagersController : NSObject + +/// Initializes FLTClusterManagersController. +/// +/// @param callbackHandler A callback handler. +/// @param mapView A map view that will be used to display clustered markers. +- (instancetype)initWithCallbackHandler:(FGMMapsCallbackApi *)callbackHandler + mapView:(GMSMapView *)mapView; + +/// Creates ClusterManagers and initializes them form JSON data. +/// +/// @param clusterManagersToAdd List of clustermanager object data. +- (void)addJSONClusterManagers:(NSArray *)clusterManagersToAdd; + +/// Creates ClusterManagers and initializes them. +/// +/// @param clusterManagersToAdd List of clustermanager object data. +- (void)addClusterManagers:(NSArray *)clusterManagersToAdd; + +/// Removes requested ClusterManagers from the controller. +/// +/// @param identifiers List of clusterManagerIds to remove. + +- (void)removeClusterManagersWithIdentifiers:(NSArray *)identifiers; + +/// Returns the ClusterManager for the given identifier. +/// +/// @param identifier identifier of the ClusterManager. +/// @return GMUClusterManager if found; otherwise, nil. +- (nullable GMUClusterManager *)clusterManagerWithIdentifier:(NSString *)identifier; + +/// Converts clusters managed by the specified ClusterManager to +/// a serializable array of clusters. +/// +/// This method fetches and serializes clusters at the current zoom +/// level from the ClusterManager identified by the given identifier. +/// If the specified ClusterManager identifier does not exist, an empty +/// array is returned. +/// +/// @param identifier The identifier of the ClusterManager to serialize. +/// @return An array of FGMPlatformCluster objects representing the clusters. +- (nullable NSArray *) + getClustersWithIdentifier:(NSString *)identifier + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error; + +/// Called when cluster marker is tapped on the map. +/// +/// @param cluster GMUStaticCluster object. +- (void)didTapOnCluster:(GMUStaticCluster *)cluster; + +/// Calls cluster method of all ClusterManagers. +- (void)invokeClusteringForEachClusterManager; +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m new file mode 100644 index 00000000000..bac87fd53b9 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m @@ -0,0 +1,143 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTClusterManagersController.h" + +#import "FLTGoogleMapJSONConversions.h" +#import "GoogleMarkerUtilities.h" + +@interface FLTClusterManagersController () + +/// A dictionary that cluster managers unique identifiers to GMUClusterManager instances. +@property(strong, nonatomic) + NSMutableDictionary *clusterManagerIdentifierToManagers; + +/// The method channel that is used to communicate with the Flutter implementation. +@property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; + +/// The current GMSMapView instance on which the cluster managers are operating. +@property(strong, nonatomic) GMSMapView *mapView; + +@end + +@implementation FLTClusterManagersController +- (instancetype)initWithCallbackHandler:(FGMMapsCallbackApi *)callbackHandler + mapView:(GMSMapView *)mapView { + self = [super init]; + if (self) { + _callbackHandler = callbackHandler; + _mapView = mapView; + _clusterManagerIdentifierToManagers = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)addJSONClusterManagers:(NSArray *)clusterManagersToAdd { + for (NSDictionary *clusterDict in clusterManagersToAdd) { + NSString *identifier = clusterDict[@"clusterManagerId"]; + [self addClusterManager:identifier]; + } +} + +- (void)addClusterManagers:(NSArray *)clusterManagersToAdd { + for (FGMPlatformClusterManager *clusterManager in clusterManagersToAdd) { + NSString *identifier = clusterManager.identifier; + [self addClusterManager:identifier]; + } +} + +- (void)addClusterManager:(NSString *)identifier { + id algorithm = [[GMUNonHierarchicalDistanceBasedAlgorithm alloc] init]; + id iconGenerator = [[GMUDefaultClusterIconGenerator alloc] init]; + id renderer = + [[GMUDefaultClusterRenderer alloc] initWithMapView:self.mapView + clusterIconGenerator:iconGenerator]; + GMUClusterManager *clusterManager = [[GMUClusterManager alloc] initWithMap:self.mapView + algorithm:algorithm + renderer:renderer]; + self.clusterManagerIdentifierToManagers[identifier] = clusterManager; +} + +- (void)removeClusterManagersWithIdentifiers:(NSArray *)identifiers { + for (NSString *identifier in identifiers) { + GMUClusterManager *clusterManager = + [self.clusterManagerIdentifierToManagers objectForKey:identifier]; + if (!clusterManager) { + continue; + } + [clusterManager clearItems]; + [self.clusterManagerIdentifierToManagers removeObjectForKey:identifier]; + } +} + +- (nullable GMUClusterManager *)clusterManagerWithIdentifier:(NSString *)identifier { + return [self.clusterManagerIdentifierToManagers objectForKey:identifier]; +} + +- (void)invokeClusteringForEachClusterManager { + for (GMUClusterManager *clusterManager in [self.clusterManagerIdentifierToManagers allValues]) { + [clusterManager cluster]; + } +} + +- (nullable NSArray *) + getClustersWithIdentifier:(NSString *)identifier + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + GMUClusterManager *clusterManager = + [self.clusterManagerIdentifierToManagers objectForKey:identifier]; + + if (!clusterManager) { + *error = [FlutterError + errorWithCode:@"Invalid clusterManagerId" + message:@"getClusters called with invalid clusterManagerId" + details:[NSString stringWithFormat:@"clusterManagerId was: '%@'", identifier]]; + return nil; + } + + NSMutableArray *response = [[NSMutableArray alloc] init]; + + // Ref: + // https://github.com/googlemaps/google-maps-ios-utils/blob/0e7ed81f1bbd9d29e4529c40ae39b0791b0a0eb8/src/Clustering/GMUClusterManager.m#L94. + NSUInteger integralZoom = (NSUInteger)floorf(_mapView.camera.zoom + 0.5f); + NSArray> *clusters = [clusterManager.algorithm clustersAtZoom:integralZoom]; + for (id cluster in clusters) { + FGMPlatformCluster *platFormCluster = FGMGetPigeonCluster(cluster, identifier); + [response addObject:platFormCluster]; + } + return response; +} + +- (void)didTapOnCluster:(GMUStaticCluster *)cluster { + NSString *clusterManagerId = [self clusterManagerIdentifierForCluster:cluster]; + if (!clusterManagerId) { + return; + } + if (cluster) { + FGMPlatformCluster *platFormCluster = FGMGetPigeonCluster(cluster, clusterManagerId); + [self.callbackHandler onClusterTapCluster:platFormCluster + completion:^(FlutterError *_Nullable _){ + }]; + } +} + +#pragma mark - Private methods + +/// Returns the cluster manager id for given cluster. +/// +/// @param cluster identifier of the ClusterManager. +/// @return id NSString if found; otherwise, nil. +- (nullable NSString *)clusterManagerIdentifierForCluster:(GMUStaticCluster *)cluster { + if ([cluster.items count] == 0) { + return nil; + } + + if ([cluster.items.firstObject isKindOfClass:[GMSMarker class]]) { + GMSMarker *firstMarker = (GMSMarker *)cluster.items.firstObject; + return [GoogleMarkerUtilities getClusterManagerIdentifierFrom:firstMarker]; + } + + return nil; +} + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h index 403e93af859..d6a7992e979 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h @@ -34,6 +34,10 @@ extern FGMPlatformLatLngBounds *FGMGetPigeonLatLngBoundsForCoordinateBounds( extern FGMPlatformCameraPosition *FGMGetPigeonCameraPositionForPosition( GMSCameraPosition *position); +/// Converts a GMUStaticCluster to its Pigeon representation. +extern FGMPlatformCluster *FGMGetPigeonCluster(GMUStaticCluster *cluster, + NSString *clusterManagerIdentifier); + @interface FLTGoogleMapJSONConversions : NSObject extern NSString *const kHeatmapsToAddKey; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m index 69ceb73620e..567bfe0cd4d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "FLTGoogleMapJSONConversions.h" +#import "GoogleMarkerUtilities.h" /// Returns dict[key], or nil if dict[key] is NSNull. id FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key) { @@ -39,6 +40,23 @@ CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng zoom:position.zoom]; } +FGMPlatformCluster *FGMGetPigeonCluster(GMUStaticCluster *cluster, + NSString *clusterManagerIdentifier) { + NSMutableArray *markerIds = [[NSMutableArray alloc] init]; + GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] init]; + + for (GMSMarker *marker in cluster.items) { + [markerIds addObject:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker]]; + bounds = [bounds includingCoordinate:marker.position]; + } + + return [FGMPlatformCluster + makeWithClusterManagerId:clusterManagerIdentifier + position:FGMGetPigeonLatLngForCoordinate(cluster.position) + bounds:FGMGetPigeonLatLngBoundsForCoordinateBounds(bounds) + markerIds:markerIds]; +} + @implementation FLTGoogleMapJSONConversions // These constants must match the corresponding constants in serialization.dart diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h index 26f69eaf388..7bb693fb700 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h @@ -4,6 +4,7 @@ #import #import +#import "FLTClusterManagersController.h" #import "GoogleMapCircleController.h" #import "GoogleMapController.h" #import "GoogleMapMarkerController.h" diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h index 4fdc08721d3..a4ba7db158c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h @@ -4,6 +4,7 @@ #import #import +#import "FLTClusterManagersController.h" #import "GoogleMapCircleController.h" #import "GoogleMapMarkerController.h" #import "GoogleMapPolygonController.h" diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 0f1ca2e3ad9..b4ed8251bcc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +@import GoogleMapsUtils; + #import "GoogleMapController.h" #import "FLTGoogleMapHeatmapController.h" #import "FLTGoogleMapJSONConversions.h" #import "FLTGoogleMapTileOverlayController.h" +#import "GoogleMarkerUtilities.h" #import "messages.g.h" #pragma mark - Conversion of JSON-like values sent via platform channels. Forward declarations. @@ -116,6 +119,7 @@ @interface FLTGoogleMapController () @property(nonatomic, strong) FGMMapsCallbackApi *dartCallbackHandler; @property(nonatomic, assign) BOOL trackCameraPosition; @property(nonatomic, weak) NSObject *registrar; +@property(nonatomic, strong) FLTClusterManagersController *clusterManagersController; @property(nonatomic, strong) FLTMarkersController *markersController; @property(nonatomic, strong) FLTPolygonsController *polygonsController; @property(nonatomic, strong) FLTPolylinesController *polylinesController; @@ -175,9 +179,14 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView _mapView.delegate = self; _mapView.paddingAdjustmentBehavior = kGMSMapViewPaddingAdjustmentBehaviorNever; _registrar = registrar; - _markersController = [[FLTMarkersController alloc] initWithMapView:_mapView - callbackHandler:_dartCallbackHandler - registrar:registrar]; + _clusterManagersController = + [[FLTClusterManagersController alloc] initWithCallbackHandler:_dartCallbackHandler + mapView:_mapView]; + _markersController = + [[FLTMarkersController alloc] initWithClusterManagersController:_clusterManagersController + callbackHandler:_dartCallbackHandler + mapView:_mapView + registrar:registrar]; _polygonsController = [[FLTPolygonsController alloc] initWithMapView:_mapView callbackHandler:_dartCallbackHandler registrar:registrar]; @@ -192,10 +201,19 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView [[FLTTileOverlaysController alloc] initWithMapView:_mapView callbackHandler:_dartCallbackHandler registrar:registrar]; + + id clusterManagersToAdd = args[@"clusterManagersToAdd"]; + if ([clusterManagersToAdd isKindOfClass:[NSArray class]]) { + [_clusterManagersController addJSONClusterManagers:clusterManagersToAdd]; + } id markersToAdd = args[@"markersToAdd"]; if ([markersToAdd isKindOfClass:[NSArray class]]) { [_markersController addJSONMarkers:markersToAdd]; } + + // Invoke clustering after markers are added. + [_clusterManagersController invokeClusteringForEachClusterManager]; + id polygonsToAdd = args[@"polygonsToAdd"]; if ([polygonsToAdd isKindOfClass:[NSArray class]]) { [_polygonsController addJSONPolygons:polygonsToAdd]; @@ -386,28 +404,38 @@ - (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *) } - (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { - NSString *markerId = marker.userData[0]; - return [self.markersController didTapMarkerWithIdentifier:markerId]; + if ([marker.userData isKindOfClass:[GMUStaticCluster class]]) { + GMUStaticCluster *cluster = marker.userData; + [self.clusterManagersController didTapOnCluster:cluster]; + // When NO is returned, the map will focus on the cluster. + return NO; + } + return [self.markersController + didTapMarkerWithIdentifier:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker]]; } - (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker { - NSString *markerId = marker.userData[0]; - [self.markersController didEndDraggingMarkerWithIdentifier:markerId location:marker.position]; + [self.markersController + didEndDraggingMarkerWithIdentifier:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker] + location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didBeginDraggingMarker:(GMSMarker *)marker { - NSString *markerId = marker.userData[0]; - [self.markersController didStartDraggingMarkerWithIdentifier:markerId location:marker.position]; + [self.markersController + didStartDraggingMarkerWithIdentifier:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker] + location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didDragMarker:(GMSMarker *)marker { - NSString *markerId = marker.userData[0]; - [self.markersController didDragMarkerWithIdentifier:markerId location:marker.position]; + [self.markersController + didDragMarkerWithIdentifier:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker] + location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker { - NSString *markerId = marker.userData[0]; - [self.markersController didTapInfoWindowOfMarkerWithIdentifier:markerId]; + [self.markersController + didTapInfoWindowOfMarkerWithIdentifier:[GoogleMarkerUtilities + getMarkerIdentifierFrom:marker]]; } - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { NSString *overlayId = overlay.userData[0]; @@ -562,6 +590,14 @@ - (void)updateMarkersByAdding:(nonnull NSArray *)toAdd [self.controller.markersController addMarkers:toAdd]; [self.controller.markersController changeMarkers:toChange]; [self.controller.markersController removeMarkersWithIdentifiers:idsToRemove]; + [self.controller.clusterManagersController invokeClusteringForEachClusterManager]; +} + +- (void)updateClusterManagersByAdding:(nonnull NSArray *)toAdd + removing:(nonnull NSArray *)idsToRemove + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [self.controller.clusterManagersController addClusterManagers:toAdd]; + [self.controller.clusterManagersController removeClusterManagersWithIdentifiers:idsToRemove]; } - (void)updatePolygonsByAdding:(nonnull NSArray *)toAdd @@ -783,6 +819,13 @@ - (nullable NSNumber *)areZoomGesturesEnabledWithError: return [FGMPlatformHeatmap makeWithJson:heatmapInfo]; } +- (nullable NSArray *) + getClustersWithIdentifier:(NSString *)clusterManagerId + error:(FlutterError *_Nullable *_Nonnull)error { + return [self.controller.clusterManagersController getClustersWithIdentifier:clusterManagerId + error:error]; +} + - (nullable NSNumber *)isCompassEnabledWithError: (FlutterError *_Nullable __autoreleasing *_Nonnull)error { return @(self.controller.mapView.settings.compassButton); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h index 1b87ab1bb94..481ac5e32cb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h @@ -4,7 +4,7 @@ #import #import - +#import "FLTClusterManagersController.h" #import "GoogleMapController.h" #import "messages.g.h" @@ -13,9 +13,10 @@ NS_ASSUME_NONNULL_BEGIN // Defines marker controllable by Flutter. @interface FLTGoogleMapMarkerController : NSObject @property(assign, nonatomic, readonly) BOOL consumeTapEvents; -- (instancetype)initWithPosition:(CLLocationCoordinate2D)position - identifier:(NSString *)identifier - mapView:(GMSMapView *)mapView; +- (instancetype)initWithMarker:(GMSMarker *)marker + markerIdentifier:(NSString *)markerIdentifier + clusterManagerIdentifier:(NSString *)clusterManagerIdentifier + mapView:(GMSMapView *)mapView; - (void)showInfoWindow; - (void)hideInfoWindow; - (BOOL)isInfoWindowShown; @@ -23,9 +24,11 @@ NS_ASSUME_NONNULL_BEGIN @end @interface FLTMarkersController : NSObject -- (instancetype)initWithMapView:(GMSMapView *)mapView - callbackHandler:(FGMMapsCallbackApi *)callbackHandler - registrar:(NSObject *)registrar; +- (instancetype)initWithClusterManagersController: + (nullable FLTClusterManagersController *)clusterManagers + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar; - (void)addJSONMarkers:(NSArray *> *)markersToAdd; - (void)addMarkers:(NSArray *)markersToAdd; - (void)changeMarkers:(NSArray *)markersToChange; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m index 46ac7b24ee5..8122e5c611b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m @@ -4,25 +4,33 @@ #import "GoogleMapMarkerController.h" #import "FLTGoogleMapJSONConversions.h" +#import "GoogleMarkerUtilities.h" @interface FLTGoogleMapMarkerController () @property(strong, nonatomic) GMSMarker *marker; @property(weak, nonatomic) GMSMapView *mapView; @property(assign, nonatomic, readwrite) BOOL consumeTapEvents; +/// The unique identifier for the cluster manager. +@property(copy, nonatomic) NSString *clusterManagerIdentifier; +/// The unique identifier for the marker. +@property(copy, nonatomic) NSString *markerIdentifier; @end @implementation FLTGoogleMapMarkerController -- (instancetype)initWithPosition:(CLLocationCoordinate2D)position - identifier:(NSString *)identifier - mapView:(GMSMapView *)mapView { +- (instancetype)initWithMarker:(GMSMarker *)marker + markerIdentifier:(NSString *)markerIdentifier + clusterManagerIdentifier:(NSString *)clusterManagerIdentifier + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { - _marker = [GMSMarker markerWithPosition:position]; + _marker = marker; + _markerIdentifier = [markerIdentifier copy]; + _clusterManagerIdentifier = [clusterManagerIdentifier copy]; _mapView = mapView; - _marker.userData = @[ identifier ]; + [self updateMarkerUserData]; } return self; } @@ -83,13 +91,28 @@ - (void)setRotation:(CLLocationDegrees)rotation { } - (void)setVisible:(BOOL)visible { - self.marker.map = visible ? self.mapView : nil; + // If marker belongs the cluster manager, visibility need to be controlled with the opacity + // as the cluster manager controls when marker is on the map and when not. + // Alpha value for marker must always be interpreted before visibility value. + if (self.clusterManagerIdentifier) { + self.marker.opacity = visible ? self.marker.opacity : 0.0f; + } else { + self.marker.map = visible ? self.mapView : nil; + } } - (void)setZIndex:(int)zIndex { self.marker.zIndex = zIndex; } +- (void)updateMarkerUserData { + if (self.clusterManagerIdentifier) { + [GoogleMarkerUtilities setMarkerIdentifier:self.markerIdentifier andClusterManagerIdentifier:self.clusterManagerIdentifier for:self.marker]; + } else { + [GoogleMarkerUtilities setMarkerIdentifier:self.markerIdentifier for:self.marker]; + } +} + - (void)interpretMarkerOptions:(NSDictionary *)data registrar:(NSObject *)registrar screenScale:(CGFloat)screenScale { @@ -432,6 +455,8 @@ @interface FLTMarkersController () @property(strong, nonatomic) NSMutableDictionary *markerIdentifierToController; @property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; +/// Controller for adding/removing/fetching cluster managers +@property(weak, nonatomic, nullable) FLTClusterManagersController *clusterManagersController; @property(weak, nonatomic) NSObject *registrar; @property(weak, nonatomic) GMSMapView *mapView; @@ -439,13 +464,16 @@ @interface FLTMarkersController () @implementation FLTMarkersController -- (instancetype)initWithMapView:(GMSMapView *)mapView - callbackHandler:(FGMMapsCallbackApi *)callbackHandler - registrar:(NSObject *)registrar { +- (instancetype)initWithClusterManagersController: + (nullable FLTClusterManagersController *)clusterManagers + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + mapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar { self = [super init]; if (self) { _callbackHandler = callbackHandler; _mapView = mapView; + _clusterManagersController = clusterManagers; _markerIdentifierToController = [[NSMutableDictionary alloc] init]; _registrar = registrar; } @@ -454,39 +482,59 @@ - (instancetype)initWithMapView:(GMSMapView *)mapView - (void)addJSONMarkers:(NSArray *> *)markersToAdd { for (NSDictionary *marker in markersToAdd) { - CLLocationCoordinate2D position = [FLTMarkersController getPosition:marker]; - NSString *identifier = marker[@"markerId"]; - FLTGoogleMapMarkerController *controller = - [[FLTGoogleMapMarkerController alloc] initWithPosition:position - identifier:identifier - mapView:self.mapView]; - [controller interpretMarkerOptions:marker - registrar:self.registrar - screenScale:[self getScreenScale]]; - self.markerIdentifierToController[identifier] = controller; + [self addJSONMarker:marker]; } } - (void)addMarkers:(NSArray *)markersToAdd { for (FGMPlatformMarker *marker in markersToAdd) { - CLLocationCoordinate2D position = [FLTMarkersController getPosition:marker.json]; - NSString *identifier = marker.json[@"markerId"]; - FLTGoogleMapMarkerController *controller = - [[FLTGoogleMapMarkerController alloc] initWithPosition:position - identifier:identifier - mapView:self.mapView]; - [controller interpretMarkerOptions:marker.json - registrar:self.registrar - screenScale:[self getScreenScale]]; - self.markerIdentifierToController[identifier] = controller; + [self addJSONMarker:marker.json]; + } +} + +- (void)addJSONMarker:(NSDictionary *)markerToAdd { + CLLocationCoordinate2D position = [FLTMarkersController getPosition:markerToAdd]; + NSString *markerIdentifier = markerToAdd[@"markerId"]; + NSString *clusterManagerIdentifier = markerToAdd[@"clusterManagerId"]; + GMSMarker *marker = [GMSMarker markerWithPosition:position]; + FLTGoogleMapMarkerController *controller = + [[FLTGoogleMapMarkerController alloc] initWithMarker:marker + markerIdentifier:markerIdentifier + clusterManagerIdentifier:clusterManagerIdentifier + mapView:self.mapView]; + [controller interpretMarkerOptions:markerToAdd + registrar:self.registrar + screenScale:[self getScreenScale]]; + if (clusterManagerIdentifier && [clusterManagerIdentifier isKindOfClass:[NSString class]]) { + GMUClusterManager *clusterManager = + [_clusterManagersController clusterManagerWithIdentifier:clusterManagerIdentifier]; + if (marker && clusterManager) { + [clusterManager addItem:(id)marker]; + } } + self.markerIdentifierToController[markerIdentifier] = controller; } - (void)changeMarkers:(NSArray *)markersToChange { for (FGMPlatformMarker *marker in markersToChange) { - NSString *identifier = marker.json[@"markerId"]; - FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; - [controller interpretMarkerOptions:marker.json + [self changeMarker:marker]; + } +} + +- (void)changeMarker:(FGMPlatformMarker *)markerToChange { + NSString *markerIdentifier = markerToChange.json[@"markerId"]; + NSString *clusterManagerIdentifier = markerToChange.json[@"clusterManagerId"]; + + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[markerIdentifier]; + if (!controller) { + return; + } + NSString *previousClusterManagerIdentifier = [controller clusterManagerIdentifier]; + if (![previousClusterManagerIdentifier isEqualToString:clusterManagerIdentifier]) { + [self removeMarker:markerIdentifier]; + [self addJSONMarker:markerToChange.json]; + } else { + [controller interpretMarkerOptions:markerToChange.json registrar:self.registrar screenScale:[self getScreenScale]]; } @@ -494,13 +542,26 @@ - (void)changeMarkers:(NSArray *)markersToChange { - (void)removeMarkersWithIdentifiers:(NSArray *)identifiers { for (NSString *identifier in identifiers) { - FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; - if (!controller) { - continue; + [self removeMarker:identifier]; + } +} + +- (void)removeMarker:(NSString *)identifier { + FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; + if (!controller) { + return; + } + id clusterManagerIdentifier = [controller clusterManagerIdentifier]; + if ([clusterManagerIdentifier isKindOfClass:[NSString class]]) { + GMUClusterManager *clusterManager = + [_clusterManagersController clusterManagerWithIdentifier:clusterManagerIdentifier]; + if (controller.marker && clusterManager) { + [clusterManager removeItem:(id)controller.marker]; } + } else { [controller removeMarker]; - [self.markerIdentifierToController removeObjectForKey:identifier]; } + [self.markerIdentifierToController removeObjectForKey:identifier]; } - (BOOL)didTapMarkerWithIdentifier:(NSString *)identifier { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.h new file mode 100644 index 00000000000..305f75bfaad --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +/// Defines user data object for markers. +@interface GoogleMarkerUserData : NSObject + +/// The identifier of the marker. +@property(nonatomic, copy) NSString *markerIdentifier; + +/// The identifier of the cluster manager. +/// This property is set only if the marker is managed by a cluster manager. +@property(nonatomic, copy) NSString *clusterManagerIdentifier; + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.m new file mode 100644 index 00000000000..7632799577b --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.m @@ -0,0 +1,9 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "GoogleMarkerUserData.h" + +@implementation GoogleMarkerUserData + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.h new file mode 100644 index 00000000000..2502fb4fd8c --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.h @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GoogleMarkerUtilities : NSObject + +/// Sets MarkerId to GMSMarker UserData. +/// +/// @param markerIdentifier Identifier of the marker. +/// @param marker GMSMarker object. ++ (void)setMarkerIdentifier:(NSString *)markerIdentifier for:(GMSMarker*)marker; + +/// Sets MarkerId and ClusterManagerId to GMSMarker UserData. +/// +/// @param markerIdentifier Identifier of marker. +/// @param clusterManagerIdentifier Identifier of cluster manager. +/// @param marker GMSMarker object. ++ (void)setMarkerIdentifier:(NSString *)markerIdentifier andClusterManagerIdentifier:(NSString *)clusterManagerIdentifier for:(GMSMarker*)marker; + +/// Get MarkerIdentifier from GMSMarker UserData. +/// +/// @param marker GMSMarker object. +/// @return NSString if found; otherwise, nil. ++ (nullable NSString *)getMarkerIdentifierFrom:(GMSMarker *)marker; + +/// Get ClusterManagerIdentifier from GMSMarker UserData. +/// +/// @param marker GMSMarker object. +/// @return NSString if found; otherwise, nil. ++ (nullable NSString *)getClusterManagerIdentifierFrom:(GMSMarker *)marker; + +@end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.m new file mode 100644 index 00000000000..d0d183417b8 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.m @@ -0,0 +1,38 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "GoogleMarkerUtilities.h" +#import "GoogleMarkerUserData.h" + +@implementation GoogleMarkerUtilities + ++ (void)setMarkerIdentifier:(NSString *)markerIdentifier for:(GMSMarker*)marker { + GoogleMarkerUserData *userData = [[GoogleMarkerUserData alloc] init]; + userData.markerIdentifier = markerIdentifier; + marker.userData = userData; +} + ++ (void)setMarkerIdentifier:(NSString *)markerIdentifier andClusterManagerIdentifier:(NSString *)clusterManagerIdentifier for:(GMSMarker*)marker { + GoogleMarkerUserData *userData = [[GoogleMarkerUserData alloc] init]; + userData.markerIdentifier = markerIdentifier; + userData.clusterManagerIdentifier = clusterManagerIdentifier; + marker.userData = userData; +} + ++ (nullable NSString *)getMarkerIdentifierFrom:(GMSMarker *)marker { + if ([marker.userData isKindOfClass:[GoogleMarkerUserData class]]) { + GoogleMarkerUserData *userData = (GoogleMarkerUserData *)marker.userData; + return userData.markerIdentifier; + } + return nil; +} + ++ (nullable NSString *)getClusterManagerIdentifierFrom:(GMSMarker *)marker { + if ([marker.userData isKindOfClass:[GoogleMarkerUserData class]]) { + GoogleMarkerUserData *userData = (GoogleMarkerUserData *)marker.userData; + return userData.clusterManagerIdentifier; + } + return nil; +} +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h index de245e43bd3..d749a5ab85d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h @@ -8,6 +8,8 @@ #import #import #import +#import +#import FOUNDATION_EXPORT double google_maps_flutterVersionNumber; FOUNDATION_EXPORT const unsigned char google_maps_flutterVersionString[]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h index 7752ff0f4ae..c1057c7bdda 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h @@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN @class FGMPlatformCameraUpdate; @class FGMPlatformCircle; @class FGMPlatformHeatmap; +@class FGMPlatformClusterManager; @class FGMPlatformMarker; @class FGMPlatformPolygon; @class FGMPlatformPolyline; @@ -24,6 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @class FGMPlatformTileOverlay; @class FGMPlatformLatLng; @class FGMPlatformLatLngBounds; +@class FGMPlatformCluster; @class FGMPlatformMapConfiguration; @class FGMPlatformPoint; @class FGMPlatformTileLayer; @@ -76,6 +78,14 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) id json; @end +/// Pigeon equivalent of the ClusterManager class. +@interface FGMPlatformClusterManager : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithIdentifier:(NSString *)identifier; +@property(nonatomic, copy) NSString *identifier; +@end + /// Pigeon equivalent of the Marker class. @interface FGMPlatformMarker : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. @@ -151,6 +161,20 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) FGMPlatformLatLng *southwest; @end +/// Pigeon equivalent of Cluster. +@interface FGMPlatformCluster : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithClusterManagerId:(NSString *)clusterManagerId + position:(FGMPlatformLatLng *)position + bounds:(FGMPlatformLatLngBounds *)bounds + markerIds:(NSArray *)markerIds; +@property(nonatomic, copy) NSString *clusterManagerId; +@property(nonatomic, strong) FGMPlatformLatLng *position; +@property(nonatomic, strong) FGMPlatformLatLngBounds *bounds; +@property(nonatomic, copy) NSArray *markerIds; +@end + /// Pigeon equivalent of MapConfiguration. @interface FGMPlatformMapConfiguration : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. @@ -219,6 +243,10 @@ NSObject *FGMGetMessagesCodec(void); changing:(NSArray *)toChange removing:(NSArray *)idsToRemove error:(FlutterError *_Nullable *_Nonnull)error; +/// Updates the set of custer managers for clusters on the map. +- (void)updateClusterManagersByAdding:(NSArray *)toAdd + removing:(NSArray *)idsToRemove + error:(FlutterError *_Nullable *_Nonnull)error; /// Updates the set of markers on the map. - (void)updateMarkersByAdding:(NSArray *)toAdd changing:(NSArray *)toChange @@ -343,6 +371,9 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger /// Called when a circle is tapped. - (void)didTapCircleWithIdentifier:(NSString *)circleId completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a marker cluster is tapped. +- (void)onClusterTapCluster:(FGMPlatformCluster *)cluster + completion:(void (^)(FlutterError *_Nullable))completion; /// Called when a polygon is tapped. - (void)didTapPolygonWithIdentifier:(NSString *)polygonId completion:(void (^)(FlutterError *_Nullable))completion; @@ -382,6 +413,10 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger error:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable FGMPlatformZoomRange *)zoomRange:(FlutterError *_Nullable *_Nonnull)error; +/// @return `nil` only when `error != nil`. +- (nullable NSArray *) + getClustersWithIdentifier:(NSString *)clusterManagerId + error:(FlutterError *_Nullable *_Nonnull)error; @end extern void SetUpFGMMapsInspectorApi(id binaryMessenger, diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m index d541e4c8ade..3b2aa3c01a1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m @@ -63,6 +63,12 @@ + (nullable FGMPlatformHeatmap *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformClusterManager () ++ (FGMPlatformClusterManager *)fromList:(NSArray *)list; ++ (nullable FGMPlatformClusterManager *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformMarker () + (FGMPlatformMarker *)fromList:(NSArray *)list; + (nullable FGMPlatformMarker *)nullableFromList:(NSArray *)list; @@ -105,6 +111,12 @@ + (nullable FGMPlatformLatLngBounds *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformCluster () ++ (FGMPlatformCluster *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCluster *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformMapConfiguration () + (FGMPlatformMapConfiguration *)fromList:(NSArray *)list; + (nullable FGMPlatformMapConfiguration *)nullableFromList:(NSArray *)list; @@ -225,6 +237,27 @@ + (nullable FGMPlatformHeatmap *)nullableFromList:(NSArray *)list { } @end +@implementation FGMPlatformClusterManager ++ (instancetype)makeWithIdentifier:(NSString *)identifier { + FGMPlatformClusterManager *pigeonResult = [[FGMPlatformClusterManager alloc] init]; + pigeonResult.identifier = identifier; + return pigeonResult; +} ++ (FGMPlatformClusterManager *)fromList:(NSArray *)list { + FGMPlatformClusterManager *pigeonResult = [[FGMPlatformClusterManager alloc] init]; + pigeonResult.identifier = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformClusterManager *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformClusterManager fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.identifier ?: [NSNull null], + ]; +} +@end + @implementation FGMPlatformMarker + (instancetype)makeWithJson:(id)json { FGMPlatformMarker *pigeonResult = [[FGMPlatformMarker alloc] init]; @@ -387,6 +420,39 @@ + (nullable FGMPlatformLatLngBounds *)nullableFromList:(NSArray *)list { } @end +@implementation FGMPlatformCluster ++ (instancetype)makeWithClusterManagerId:(NSString *)clusterManagerId + position:(FGMPlatformLatLng *)position + bounds:(FGMPlatformLatLngBounds *)bounds + markerIds:(NSArray *)markerIds { + FGMPlatformCluster *pigeonResult = [[FGMPlatformCluster alloc] init]; + pigeonResult.clusterManagerId = clusterManagerId; + pigeonResult.position = position; + pigeonResult.bounds = bounds; + pigeonResult.markerIds = markerIds; + return pigeonResult; +} ++ (FGMPlatformCluster *)fromList:(NSArray *)list { + FGMPlatformCluster *pigeonResult = [[FGMPlatformCluster alloc] init]; + pigeonResult.clusterManagerId = GetNullableObjectAtIndex(list, 0); + pigeonResult.position = GetNullableObjectAtIndex(list, 1); + pigeonResult.bounds = GetNullableObjectAtIndex(list, 2); + pigeonResult.markerIds = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable FGMPlatformCluster *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCluster fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.clusterManagerId ?: [NSNull null], + self.position ?: [NSNull null], + self.bounds ?: [NSNull null], + self.markerIds ?: [NSNull null], + ]; +} +@end + @implementation FGMPlatformMapConfiguration + (instancetype)makeWithJson:(id)json { FGMPlatformMapConfiguration *pigeonResult = [[FGMPlatformMapConfiguration alloc] init]; @@ -503,26 +569,30 @@ - (nullable id)readValueOfType:(UInt8)type { case 132: return [FGMPlatformHeatmap fromList:[self readValue]]; case 133: - return [FGMPlatformMarker fromList:[self readValue]]; + return [FGMPlatformClusterManager fromList:[self readValue]]; case 134: - return [FGMPlatformPolygon fromList:[self readValue]]; + return [FGMPlatformMarker fromList:[self readValue]]; case 135: - return [FGMPlatformPolyline fromList:[self readValue]]; + return [FGMPlatformPolygon fromList:[self readValue]]; case 136: - return [FGMPlatformTile fromList:[self readValue]]; + return [FGMPlatformPolyline fromList:[self readValue]]; case 137: - return [FGMPlatformTileOverlay fromList:[self readValue]]; + return [FGMPlatformTile fromList:[self readValue]]; case 138: - return [FGMPlatformLatLng fromList:[self readValue]]; + return [FGMPlatformTileOverlay fromList:[self readValue]]; case 139: - return [FGMPlatformLatLngBounds fromList:[self readValue]]; + return [FGMPlatformLatLng fromList:[self readValue]]; case 140: - return [FGMPlatformMapConfiguration fromList:[self readValue]]; + return [FGMPlatformLatLngBounds fromList:[self readValue]]; case 141: - return [FGMPlatformPoint fromList:[self readValue]]; + return [FGMPlatformCluster fromList:[self readValue]]; case 142: - return [FGMPlatformTileLayer fromList:[self readValue]]; + return [FGMPlatformMapConfiguration fromList:[self readValue]]; case 143: + return [FGMPlatformPoint fromList:[self readValue]]; + case 144: + return [FGMPlatformTileLayer fromList:[self readValue]]; + case 145: return [FGMPlatformZoomRange fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -546,39 +616,45 @@ - (void)writeValue:(id)value { } else if ([value isKindOfClass:[FGMPlatformHeatmap class]]) { [self writeByte:132]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { + } else if ([value isKindOfClass:[FGMPlatformClusterManager class]]) { [self writeByte:133]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { + } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { [self writeByte:134]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { + } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { [self writeByte:135]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTile class]]) { + } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { [self writeByte:136]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { + } else if ([value isKindOfClass:[FGMPlatformTile class]]) { [self writeByte:137]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { + } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { [self writeByte:138]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { + } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { [self writeByte:139]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { + } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { [self writeByte:140]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { + } else if ([value isKindOfClass:[FGMPlatformCluster class]]) { [self writeByte:141]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { + } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { [self writeByte:142]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { [self writeByte:143]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { + [self writeByte:144]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + [self writeByte:145]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -726,6 +802,32 @@ void SetUpFGMMapsApiWithSuffix(id binaryMessenger, [channel setMessageHandler:nil]; } } + /// Updates the set of custer managers for clusters on the map. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.updateClusterManagers", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updateClusterManagersByAdding:removing:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(updateClusterManagersByAdding:removing:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSArray *arg_toAdd = GetNullableObjectAtIndex(args, 0); + NSArray *arg_idsToRemove = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api updateClusterManagersByAdding:arg_toAdd removing:arg_idsToRemove error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } /// Updates the set of markers on the map. { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] @@ -1471,6 +1573,31 @@ - (void)didTapCircleWithIdentifier:(NSString *)arg_circleId } }]; } +- (void)onClusterTapCluster:(FGMPlatformCluster *)arg_cluster + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onClusterTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_cluster ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} - (void)didTapPolygonWithIdentifier:(NSString *)arg_polygonId completion:(void (^)(FlutterError *_Nullable))completion { NSString *channelName = [NSString @@ -1810,4 +1937,29 @@ void SetUpFGMMapsInspectorApiWithSuffix(id binaryMesseng [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsInspectorApi.getClusters", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(getClustersWithIdentifier:error:)], + @"FGMMapsInspectorApi api (%@) doesn't respond to " + @"@selector(getClustersWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_clusterManagerId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSArray *output = [api getClustersWithIdentifier:arg_clusterManagerId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart index 35a5e73c30f..9d8ddadf192 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_map_inspector_ios.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'google_maps_flutter_ios.dart'; import 'messages.g.dart'; import 'serialization.dart'; @@ -128,4 +129,17 @@ class GoogleMapsInspectorIOS extends GoogleMapsInspectorPlatform { Future isTrafficEnabled({required int mapId}) async { return _inspectorProvider(mapId)!.isTrafficEnabled(); } + + @override + Future> getClusters({ + required int mapId, + required ClusterManagerId clusterManagerId, + }) async { + return (await _inspectorProvider(mapId)! + .getClusters(clusterManagerId.value)) + // See comment in messages.dart for why the force unwrap is okay. + .map((PlatformCluster? cluster) => + GoogleMapsFlutterIOS.clusterFromPlatformCluster(cluster!)) + .toList(); + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart index 483b1e7b19d..a296a7642de 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart @@ -14,6 +14,7 @@ import 'package:stream_transform/stream_transform.dart'; import 'google_map_inspector_ios.dart'; import 'messages.g.dart'; import 'serialization.dart'; +import 'utils/cluster_manager.dart'; // TODO(stuartmorgan): Remove the dependency on platform interface toJson // methods. Channel serialization details should all be package-internal. @@ -200,6 +201,11 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } + @override + Stream onClusterTap({required int mapId}) { + return _events(mapId).whereType(); + } + @override Future updateMapConfiguration( MapConfiguration configuration, { @@ -314,6 +320,21 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { ); } + @override + Future updateClusterManagers( + ClusterManagerUpdates clusterManagerUpdates, { + required int mapId, + }) { + return _hostApi(mapId).updateClusterManagers( + clusterManagerUpdates.clusterManagersToAdd + .map(_platformClusterManagerFromClusterManager) + .toList(), + clusterManagerUpdates.clusterManagerIdsToRemove + .map((ClusterManagerId id) => id.value) + .toList(), + ); + } + @override Future clearTileCache( TileOverlayId tileOverlayId, { @@ -438,6 +459,8 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { 'circlesToAdd': serializeCircleSet(mapObjects.circles), 'heatmapsToAdd': mapObjects.heatmaps.map(serializeHeatmap).toList(), 'tileOverlaysToAdd': serializeTileOverlaySet(mapObjects.tileOverlays), + 'clusterManagersToAdd': + serializeClusterManagerSet(mapObjects.clusterManagers), }; return UiKitView( @@ -477,6 +500,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { Set polylines = const {}, Set circles = const {}, Set tileOverlays = const {}, + Set clusterManagers = const {}, Set>? gestureRecognizers, Map mapOptions = const {}, }) { @@ -491,6 +515,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { polygons: polygons, polylines: polylines, circles: circles, + clusterManagers: clusterManagers, tileOverlays: tileOverlays), mapOptions: mapOptions, ); @@ -506,6 +531,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { Set polylines = const {}, Set circles = const {}, Set tileOverlays = const {}, + Set clusterManagers = const {}, Set>? gestureRecognizers, Map mapOptions = const {}, }) { @@ -519,6 +545,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { polylines: polylines, circles: circles, tileOverlays: tileOverlays, + clusterManagers: clusterManagers, gestureRecognizers: gestureRecognizers, mapOptions: mapOptions, ); @@ -531,6 +558,18 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { MapsInspectorApi(messageChannelSuffix: mapId.toString())); } + /// Converts a Pigeon [PlatformCluster] to the corresponding [Cluster]. + static Cluster clusterFromPlatformCluster(PlatformCluster cluster) { + return Cluster( + ClusterManagerId(cluster.clusterManagerId), + cluster.markerIds + // See comment in messages.dart for why the force unwrap is okay. + .map((String? markerId) => MarkerId(markerId!)) + .toList(), + position: _latLngFromPlatformLatLng(cluster.position), + bounds: _latLngBoundsFromPlatformLatLngBounds(cluster.bounds)); + } + static PlatformLatLng _platformLatLngFromLatLng(LatLng latLng) { return PlatformLatLng( latitude: latLng.latitude, longitude: latLng.longitude); @@ -571,6 +610,12 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { TileOverlay tileOverlay) { return PlatformTileOverlay(json: tileOverlay.toJson()); } + + static PlatformClusterManager _platformClusterManagerFromClusterManager( + ClusterManager clusterManager) { + return PlatformClusterManager( + identifier: clusterManager.clusterManagerId.value); + } } /// Callback handler for map events from the platform host. @@ -645,6 +690,14 @@ class HostMapMessageHandler implements MapsCallbackApi { streamController.add(CircleTapEvent(mapId, CircleId(circleId))); } + @override + void onClusterTap(PlatformCluster cluster) { + streamController.add(ClusterTapEvent( + mapId, + GoogleMapsFlutterIOS.clusterFromPlatformCluster(cluster), + )); + } + @override void onInfoWindowTap(String markerId) { streamController.add(InfoWindowTapEvent(mapId, MarkerId(markerId))); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart index e5041ef24e1..3e164bf2d88 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart @@ -141,6 +141,28 @@ class PlatformHeatmap { } } +/// Pigeon equivalent of the ClusterManager class. +class PlatformClusterManager { + PlatformClusterManager({ + required this.identifier, + }); + + String identifier; + + Object encode() { + return [ + identifier, + ]; + } + + static PlatformClusterManager decode(Object result) { + result as List; + return PlatformClusterManager( + identifier: result[0]! as String, + ); + } +} + /// Pigeon equivalent of the Marker class. class PlatformMarker { PlatformMarker({ @@ -327,6 +349,43 @@ class PlatformLatLngBounds { } } +/// Pigeon equivalent of Cluster. +class PlatformCluster { + PlatformCluster({ + required this.clusterManagerId, + required this.position, + required this.bounds, + required this.markerIds, + }); + + String clusterManagerId; + + PlatformLatLng position; + + PlatformLatLngBounds bounds; + + List markerIds; + + Object encode() { + return [ + clusterManagerId, + position, + bounds, + markerIds, + ]; + } + + static PlatformCluster decode(Object result) { + result as List; + return PlatformCluster( + clusterManagerId: result[0]! as String, + position: result[1]! as PlatformLatLng, + bounds: result[2]! as PlatformLatLngBounds, + markerIds: (result[3] as List?)!.cast(), + ); + } +} + /// Pigeon equivalent of MapConfiguration. class PlatformMapConfiguration { PlatformMapConfiguration({ @@ -459,39 +518,45 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is PlatformHeatmap) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is PlatformMarker) { + } else if (value is PlatformClusterManager) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is PlatformPolygon) { + } else if (value is PlatformMarker) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is PlatformPolyline) { + } else if (value is PlatformPolygon) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is PlatformTile) { + } else if (value is PlatformPolyline) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is PlatformTileOverlay) { + } else if (value is PlatformTile) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLng) { + } else if (value is PlatformTileOverlay) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLngBounds) { + } else if (value is PlatformLatLng) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PlatformMapConfiguration) { + } else if (value is PlatformLatLngBounds) { buffer.putUint8(140); writeValue(buffer, value.encode()); - } else if (value is PlatformPoint) { + } else if (value is PlatformCluster) { buffer.putUint8(141); writeValue(buffer, value.encode()); - } else if (value is PlatformTileLayer) { + } else if (value is PlatformMapConfiguration) { buffer.putUint8(142); writeValue(buffer, value.encode()); - } else if (value is PlatformZoomRange) { + } else if (value is PlatformPoint) { buffer.putUint8(143); writeValue(buffer, value.encode()); + } else if (value is PlatformTileLayer) { + buffer.putUint8(144); + writeValue(buffer, value.encode()); + } else if (value is PlatformZoomRange) { + buffer.putUint8(145); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -509,26 +574,30 @@ class _PigeonCodec extends StandardMessageCodec { case 132: return PlatformHeatmap.decode(readValue(buffer)!); case 133: - return PlatformMarker.decode(readValue(buffer)!); + return PlatformClusterManager.decode(readValue(buffer)!); case 134: - return PlatformPolygon.decode(readValue(buffer)!); + return PlatformMarker.decode(readValue(buffer)!); case 135: - return PlatformPolyline.decode(readValue(buffer)!); + return PlatformPolygon.decode(readValue(buffer)!); case 136: - return PlatformTile.decode(readValue(buffer)!); + return PlatformPolyline.decode(readValue(buffer)!); case 137: - return PlatformTileOverlay.decode(readValue(buffer)!); + return PlatformTile.decode(readValue(buffer)!); case 138: - return PlatformLatLng.decode(readValue(buffer)!); + return PlatformTileOverlay.decode(readValue(buffer)!); case 139: - return PlatformLatLngBounds.decode(readValue(buffer)!); + return PlatformLatLng.decode(readValue(buffer)!); case 140: - return PlatformMapConfiguration.decode(readValue(buffer)!); + return PlatformLatLngBounds.decode(readValue(buffer)!); case 141: - return PlatformPoint.decode(readValue(buffer)!); + return PlatformCluster.decode(readValue(buffer)!); case 142: - return PlatformTileLayer.decode(readValue(buffer)!); + return PlatformMapConfiguration.decode(readValue(buffer)!); case 143: + return PlatformPoint.decode(readValue(buffer)!); + case 144: + return PlatformTileLayer.decode(readValue(buffer)!); + case 145: return PlatformZoomRange.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -659,6 +728,32 @@ class MapsApi { } } + /// Updates the set of custer managers for clusters on the map. + Future updateClusterManagers( + List toAdd, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updateClusterManagers$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + /// Updates the set of markers on the map. Future updateMarkers(List toAdd, List toChange, List idsToRemove) async { @@ -1161,6 +1256,9 @@ abstract class MapsCallbackApi { /// Called when a circle is tapped. void onCircleTap(String circleId); + /// Called when a marker cluster is tapped. + void onClusterTap(PlatformCluster cluster); + /// Called when a polygon is tapped. void onPolygonTap(String polygonId); @@ -1484,6 +1582,34 @@ abstract class MapsCallbackApi { }); } } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onClusterTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onClusterTap was null.'); + final List args = (message as List?)!; + final PlatformCluster? arg_cluster = (args[0] as PlatformCluster?); + assert(arg_cluster != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onClusterTap was null, expected non-null PlatformCluster.'); + try { + api.onClusterTap(arg_cluster!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( @@ -1902,4 +2028,34 @@ class MapsInspectorApi { return (__pigeon_replyList[0] as PlatformZoomRange?)!; } } + + Future> getClusters(String clusterManagerId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsInspectorApi.getClusters$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([clusterManagerId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)! + .cast(); + } + } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/utils/cluster_manager.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/utils/cluster_manager.dart new file mode 100644 index 00000000000..854690206a2 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/utils/cluster_manager.dart @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +/// Converts a Set of Cluster Managers into object serializable in JSON. +Object serializeClusterManagerSet(Set clusterManagers) { + return clusterManagers + .map((ClusterManager cm) => serializeClusterManager(cm)) + .toList(); +} + +/// Converts a Cluster Manager into object serializable in JSON. +Object serializeClusterManager(ClusterManager clusterManager) { + final Map json = {}; + json['clusterManagerId'] = clusterManager.clusterManagerId.value; + return json; +} + +/// Converts a Cluster Manager updates into object serializable in JSON. +Object serializeClusterManagerUpdates( + ClusterManagerUpdates clusterManagerUpdates) { + final Map updateMap = {}; + + updateMap['clusterManagersToAdd'] = + serializeClusterManagerSet(clusterManagerUpdates.objectsToAdd); + updateMap['clusterManagerIdsToRemove'] = clusterManagerUpdates + .objectIdsToRemove + .map((MapsObjectId id) => id.value) + .toList(); + + return updateMap; +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart index cd6cab64c91..af05fe01cf9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart @@ -66,6 +66,13 @@ class PlatformHeatmap { final Object json; } +/// Pigeon equivalent of the ClusterManager class. +class PlatformClusterManager { + PlatformClusterManager({required this.identifier}); + + final String identifier; +} + /// Pigeon equivalent of the Marker class. class PlatformMarker { PlatformMarker(this.json); @@ -139,6 +146,24 @@ class PlatformLatLngBounds { final PlatformLatLng southwest; } +/// Pigeon equivalent of Cluster. +class PlatformCluster { + PlatformCluster({ + required this.clusterManagerId, + required this.position, + required this.bounds, + required this.markerIds, + }); + + final String clusterManagerId; + final PlatformLatLng position; + final PlatformLatLngBounds bounds; + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + final List markerIds; +} + /// Pigeon equivalent of MapConfiguration. class PlatformMapConfiguration { PlatformMapConfiguration({required this.json}); @@ -213,6 +238,14 @@ abstract class MapsApi { void updateHeatmaps(List toAdd, List toChange, List idsToRemove); + /// Updates the set of custer managers for clusters on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + @ObjCSelector('updateClusterManagersByAdding:removing:') + void updateClusterManagers( + List toAdd, List idsToRemove); + /// Updates the set of markers on the map. // TODO(stuartmorgan): Make the generic type non-nullable once supported. // https://github.com/flutter/flutter/issues/97848 @@ -354,6 +387,9 @@ abstract class MapsCallbackApi { @ObjCSelector('didTapCircleWithIdentifier:') void onCircleTap(String circleId); + /// Called when a marker cluster is tapped. + void onClusterTap(PlatformCluster cluster); + /// Called when a polygon is tapped. @ObjCSelector('didTapPolygonWithIdentifier:') void onPolygonTap(String polygonId); @@ -386,4 +422,9 @@ abstract class MapsInspectorApi { PlatformHeatmap? getHeatmapInfo(String heatmapId); @ObjCSelector('zoomRange') PlatformZoomRange getZoomRange(); + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + @ObjCSelector('getClustersWithIdentifier:') + List getClusters(String clusterManagerId); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index 2ba981a262b..273cd24a475 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.11.0 +version: 2.12.0 environment: sdk: ^3.2.3 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/cluster_manager_utils_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/cluster_manager_utils_test.dart new file mode 100644 index 00000000000..55d2cc98137 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/cluster_manager_utils_test.dart @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:google_maps_flutter_ios/src/utils/cluster_manager.dart'; +import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + test('serializeClusterManager', () async { + const ClusterManager manager = + ClusterManager(clusterManagerId: ClusterManagerId('1234')); + final Object json = serializeClusterManager(manager); + + expect(json, { + 'clusterManagerId': '1234', + }); + }); + + test('serializeClusterManagerSet', () async { + const ClusterManager manager = + ClusterManager(clusterManagerId: ClusterManagerId('1234')); + const ClusterManager manager2 = + ClusterManager(clusterManagerId: ClusterManagerId('5678')); + const ClusterManager manager3 = + ClusterManager(clusterManagerId: ClusterManagerId('9012')); + final Object json = serializeClusterManagerSet( + {manager, manager2, manager3}); + + expect(json, [ + { + 'clusterManagerId': '1234', + }, + { + 'clusterManagerId': '5678', + }, + { + 'clusterManagerId': '9012', + } + ]); + }); + + test('serializeClusterManagerUpdates', () async { + final ClusterManagerUpdates updates = ClusterManagerUpdates.from( + { + const ClusterManager(clusterManagerId: ClusterManagerId('1234')) + }, + { + const ClusterManager(clusterManagerId: ClusterManagerId('5678')) + }, + ); + + final Object json = serializeClusterManagerUpdates(updates); + + expect(json, { + 'clusterManagerIdsToRemove': ['1234'], + 'clusterManagersToAdd': [ + { + 'clusterManagerId': '5678', + } + ], + }); + }); +} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart index c8f9ca752fa..c82012987a0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart @@ -118,6 +118,23 @@ class MockMapsApi extends _i1.Mock implements _i2.MapsApi { returnValueForMissingStub: _i3.Future.value(), ) as _i3.Future); + @override + _i3.Future updateClusterManagers( + List<_i2.PlatformClusterManager?>? toAdd, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateClusterManagers, + [ + toAdd, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + @override _i3.Future updateMarkers( List<_i2.PlatformMarker?>? toAdd, From e3ca225f0ef359abf6c52c8cdbedc9a1c2b03054 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Sat, 3 Aug 2024 14:20:25 +0300 Subject: [PATCH 2/8] [google_maps_flutter_ios] Quality improvements to clustering support --- .../Classes/FLTClusterManagersController.h | 13 +++-- .../Classes/FLTClusterManagersController.m | 34 +++++------- .../ios/Classes/FLTGoogleMapJSONConversions.m | 8 +-- .../ios/Classes/FLTGoogleMarkerUserData.h | 42 ++++++++++++++ .../ios/Classes/FLTGoogleMarkerUserData.m | 34 ++++++++++++ .../ios/Classes/GoogleMapController.m | 55 +++++++++---------- .../ios/Classes/GoogleMapMarkerController.h | 11 ++-- .../ios/Classes/GoogleMapMarkerController.m | 39 +++++-------- .../ios/Classes/GoogleMarkerUserData.h | 17 ------ .../ios/Classes/GoogleMarkerUserData.m | 9 --- .../ios/Classes/GoogleMarkerUtilities.h | 38 ------------- .../ios/Classes/GoogleMarkerUtilities.m | 38 ------------- .../ios/Classes/messages.g.h | 8 +-- .../ios/Classes/messages.g.m | 12 ++-- .../lib/src/utils/cluster_manager.dart | 15 ----- .../pigeons/messages.dart | 3 +- .../test/cluster_manager_utils_test.dart | 22 -------- 17 files changed, 157 insertions(+), 241 deletions(-) create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.h create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.m delete mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.h delete mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.m delete mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.h delete mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.m diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h index dcde3d09409..813b937ca92 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h @@ -18,8 +18,8 @@ NS_ASSUME_NONNULL_BEGIN /// /// @param callbackHandler A callback handler. /// @param mapView A map view that will be used to display clustered markers. -- (instancetype)initWithCallbackHandler:(FGMMapsCallbackApi *)callbackHandler - mapView:(GMSMapView *)mapView; +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler; /// Creates ClusterManagers and initializes them form JSON data. /// @@ -52,15 +52,16 @@ NS_ASSUME_NONNULL_BEGIN /// array is returned. /// /// @param identifier The identifier of the ClusterManager to serialize. -/// @return An array of FGMPlatformCluster objects representing the clusters. +/// @return An array of FGMPlatformCluster objects representing the clusters. `nil` is returned only +/// when `error != nil`. - (nullable NSArray *) - getClustersWithIdentifier:(NSString *)identifier - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error; + clustersWithIdentifier:(NSString *)identifier + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error; /// Called when cluster marker is tapped on the map. /// /// @param cluster GMUStaticCluster object. -- (void)didTapOnCluster:(GMUStaticCluster *)cluster; +- (void)didTapCluster:(GMUStaticCluster *)cluster; /// Calls cluster method of all ClusterManagers. - (void)invokeClusteringForEachClusterManager; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m index bac87fd53b9..f0262da9ffd 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m @@ -5,7 +5,7 @@ #import "FLTClusterManagersController.h" #import "FLTGoogleMapJSONConversions.h" -#import "GoogleMarkerUtilities.h" +#import "FLTGoogleMarkerUserData.h" @interface FLTClusterManagersController () @@ -13,7 +13,7 @@ @interface FLTClusterManagersController () @property(strong, nonatomic) NSMutableDictionary *clusterManagerIdentifierToManagers; -/// The method channel that is used to communicate with the Flutter implementation. +/// The callback handler interface for calls to Flutter. @property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; /// The current GMSMapView instance on which the cluster managers are operating. @@ -22,8 +22,8 @@ @interface FLTClusterManagersController () @end @implementation FLTClusterManagersController -- (instancetype)initWithCallbackHandler:(FGMMapsCallbackApi *)callbackHandler - mapView:(GMSMapView *)mapView { +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler { self = [super init]; if (self) { _callbackHandler = callbackHandler; @@ -82,8 +82,8 @@ - (void)invokeClusteringForEachClusterManager { } - (nullable NSArray *) - getClustersWithIdentifier:(NSString *)identifier - error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + clustersWithIdentifier:(NSString *)identifier + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { GMUClusterManager *clusterManager = [self.clusterManagerIdentifierToManagers objectForKey:identifier]; @@ -95,12 +95,12 @@ - (void)invokeClusteringForEachClusterManager { return nil; } - NSMutableArray *response = [[NSMutableArray alloc] init]; - // Ref: // https://github.com/googlemaps/google-maps-ios-utils/blob/0e7ed81f1bbd9d29e4529c40ae39b0791b0a0eb8/src/Clustering/GMUClusterManager.m#L94. NSUInteger integralZoom = (NSUInteger)floorf(_mapView.camera.zoom + 0.5f); NSArray> *clusters = [clusterManager.algorithm clustersAtZoom:integralZoom]; + NSMutableArray *response = + [[NSMutableArray alloc] initWithCapacity:clusters.count]; for (id cluster in clusters) { FGMPlatformCluster *platFormCluster = FGMGetPigeonCluster(cluster, identifier); [response addObject:platFormCluster]; @@ -108,17 +108,15 @@ - (void)invokeClusteringForEachClusterManager { return response; } -- (void)didTapOnCluster:(GMUStaticCluster *)cluster { +- (void)didTapCluster:(GMUStaticCluster *)cluster { NSString *clusterManagerId = [self clusterManagerIdentifierForCluster:cluster]; if (!clusterManagerId) { return; } - if (cluster) { - FGMPlatformCluster *platFormCluster = FGMGetPigeonCluster(cluster, clusterManagerId); - [self.callbackHandler onClusterTapCluster:platFormCluster - completion:^(FlutterError *_Nullable _){ - }]; - } + FGMPlatformCluster *platFormCluster = FGMGetPigeonCluster(cluster, clusterManagerId); + [self.callbackHandler didTapCluster:platFormCluster + completion:^(FlutterError *_Nullable _){ + }]; } #pragma mark - Private methods @@ -128,13 +126,9 @@ - (void)didTapOnCluster:(GMUStaticCluster *)cluster { /// @param cluster identifier of the ClusterManager. /// @return id NSString if found; otherwise, nil. - (nullable NSString *)clusterManagerIdentifierForCluster:(GMUStaticCluster *)cluster { - if ([cluster.items count] == 0) { - return nil; - } - if ([cluster.items.firstObject isKindOfClass:[GMSMarker class]]) { GMSMarker *firstMarker = (GMSMarker *)cluster.items.firstObject; - return [GoogleMarkerUtilities getClusterManagerIdentifierFrom:firstMarker]; + return FLTGetClusterManagerIdentifierFrom(firstMarker); } return nil; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m index 567bfe0cd4d..152e1c6061d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m @@ -3,7 +3,7 @@ // found in the LICENSE file. #import "FLTGoogleMapJSONConversions.h" -#import "GoogleMarkerUtilities.h" +#import "FLTGoogleMarkerUserData.h" /// Returns dict[key], or nil if dict[key] is NSNull. id FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key) { @@ -42,11 +42,11 @@ CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng FGMPlatformCluster *FGMGetPigeonCluster(GMUStaticCluster *cluster, NSString *clusterManagerIdentifier) { - NSMutableArray *markerIds = [[NSMutableArray alloc] init]; + NSMutableArray *markerIDs = [[NSMutableArray alloc] initWithCapacity:cluster.items.count]; GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] init]; for (GMSMarker *marker in cluster.items) { - [markerIds addObject:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker]]; + [markerIDs addObject:FLTGetMarkerIdentifierFrom(marker)]; bounds = [bounds includingCoordinate:marker.position]; } @@ -54,7 +54,7 @@ CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng makeWithClusterManagerId:clusterManagerIdentifier position:FGMGetPigeonLatLngForCoordinate(cluster.position) bounds:FGMGetPigeonLatLngBoundsForCoordinateBounds(bounds) - markerIds:markerIds]; + markerIds:markerIDs]; } @implementation FLTGoogleMapJSONConversions diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.h new file mode 100644 index 00000000000..df35904b278 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +NS_ASSUME_NONNULL_BEGIN + +/// Defines user data object for markers. +@interface FLTGoogleMarkerUserData : NSObject + +/// The identifier of the marker. +@property(nonatomic, copy) NSString *markerIdentifier; + +/// The identifier of the cluster manager. +/// This property is set only if the marker is managed by a cluster manager. +@property(nonatomic, copy, nullable) NSString *clusterManagerIdentifier; + +@end + +/// Sets MarkerId and optionally ClusterManagerId to GMSMarker UserData. +/// +/// @param markerIdentifier Identifier of marker. +/// @param clusterManagerIdentifier Optional identifier of cluster manager. +/// @param marker GMSMarker object. +extern void FLTSetIdentifiersToMarkerUserData(NSString *markerIdentifier, + NSString *_Nullable clusterManagerIdentifier, + GMSMarker *marker); + +/// Get MarkerIdentifier from GMSMarker UserData. +/// +/// @param marker GMSMarker object. +/// @return NSString if found; otherwise, nil. +extern NSString *_Nullable FLTGetMarkerIdentifierFrom(GMSMarker *marker); + +/// Get ClusterManagerIdentifier from GMSMarker UserData. +/// +/// @param marker GMSMarker object. +/// @return NSString if found; otherwise, nil. +extern NSString *_Nullable FLTGetClusterManagerIdentifierFrom(GMSMarker *marker); + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.m new file mode 100644 index 00000000000..4df5f075cd0 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.m @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTGoogleMarkerUserData.h" + +@implementation FLTGoogleMarkerUserData + +@end + +void FLTSetIdentifiersToMarkerUserData(NSString *markerIdentifier, + NSString *_Nullable clusterManagerIdentifier, + GMSMarker *marker) { + FLTGoogleMarkerUserData *userData = [[FLTGoogleMarkerUserData alloc] init]; + userData.markerIdentifier = markerIdentifier; + userData.clusterManagerIdentifier = clusterManagerIdentifier; + marker.userData = userData; +}; + +NSString *_Nullable FLTGetMarkerIdentifierFrom(GMSMarker *marker) { + if ([marker.userData isKindOfClass:[FLTGoogleMarkerUserData class]]) { + FLTGoogleMarkerUserData *userData = (FLTGoogleMarkerUserData *)marker.userData; + return userData.markerIdentifier; + } + return nil; +}; + +NSString *_Nullable FLTGetClusterManagerIdentifierFrom(GMSMarker *marker) { + if ([marker.userData isKindOfClass:[FLTGoogleMarkerUserData class]]) { + FLTGoogleMarkerUserData *userData = (FLTGoogleMarkerUserData *)marker.userData; + return userData.clusterManagerIdentifier; + } + return nil; +}; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index b4ed8251bcc..718276229b4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -8,7 +8,7 @@ #import "FLTGoogleMapHeatmapController.h" #import "FLTGoogleMapJSONConversions.h" #import "FLTGoogleMapTileOverlayController.h" -#import "GoogleMarkerUtilities.h" +#import "FLTGoogleMarkerUserData.h" #import "messages.g.h" #pragma mark - Conversion of JSON-like values sent via platform channels. Forward declarations. @@ -180,13 +180,12 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView _mapView.paddingAdjustmentBehavior = kGMSMapViewPaddingAdjustmentBehaviorNever; _registrar = registrar; _clusterManagersController = - [[FLTClusterManagersController alloc] initWithCallbackHandler:_dartCallbackHandler - mapView:_mapView]; - _markersController = - [[FLTMarkersController alloc] initWithClusterManagersController:_clusterManagersController - callbackHandler:_dartCallbackHandler - mapView:_mapView - registrar:registrar]; + [[FLTClusterManagersController alloc] initWithMapView:_mapView + callbackHandler:_dartCallbackHandler]; + _markersController = [[FLTMarkersController alloc] initWithMapView:_mapView + callbackHandler:_dartCallbackHandler + clusterManagersController:_clusterManagersController + registrar:registrar]; _polygonsController = [[FLTPolygonsController alloc] initWithMapView:_mapView callbackHandler:_dartCallbackHandler registrar:registrar]; @@ -210,10 +209,6 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView if ([markersToAdd isKindOfClass:[NSArray class]]) { [_markersController addJSONMarkers:markersToAdd]; } - - // Invoke clustering after markers are added. - [_clusterManagersController invokeClusteringForEachClusterManager]; - id polygonsToAdd = args[@"polygonsToAdd"]; if ([polygonsToAdd isKindOfClass:[NSArray class]]) { [_polygonsController addJSONPolygons:polygonsToAdd]; @@ -235,6 +230,9 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView [_tileOverlaysController addJSONTileOverlays:tileOverlaysToAdd]; } + // Invoke clustering after markers are added. + [_clusterManagersController invokeClusteringForEachClusterManager]; + [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil]; _callHandler = [[FGMMapCallHandler alloc] initWithMapController:self @@ -406,36 +404,31 @@ - (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *) - (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { if ([marker.userData isKindOfClass:[GMUStaticCluster class]]) { GMUStaticCluster *cluster = marker.userData; - [self.clusterManagersController didTapOnCluster:cluster]; + [self.clusterManagersController didTapCluster:cluster]; // When NO is returned, the map will focus on the cluster. return NO; } - return [self.markersController - didTapMarkerWithIdentifier:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker]]; + return [self.markersController didTapMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker)]; } - (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker { - [self.markersController - didEndDraggingMarkerWithIdentifier:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker] - location:marker.position]; + [self.markersController didEndDraggingMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker) + location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didBeginDraggingMarker:(GMSMarker *)marker { - [self.markersController - didStartDraggingMarkerWithIdentifier:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker] - location:marker.position]; + [self.markersController didStartDraggingMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker) + location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didDragMarker:(GMSMarker *)marker { - [self.markersController - didDragMarkerWithIdentifier:[GoogleMarkerUtilities getMarkerIdentifierFrom:marker] - location:marker.position]; + [self.markersController didDragMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker) + location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker { [self.markersController - didTapInfoWindowOfMarkerWithIdentifier:[GoogleMarkerUtilities - getMarkerIdentifierFrom:marker]]; + didTapInfoWindowOfMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker)]; } - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { NSString *overlayId = overlay.userData[0]; @@ -590,6 +583,8 @@ - (void)updateMarkersByAdding:(nonnull NSArray *)toAdd [self.controller.markersController addMarkers:toAdd]; [self.controller.markersController changeMarkers:toChange]; [self.controller.markersController removeMarkersWithIdentifiers:idsToRemove]; + + // Invoke clustering after markers are added. [self.controller.clusterManagersController invokeClusteringForEachClusterManager]; } @@ -820,10 +815,10 @@ - (nullable NSNumber *)areZoomGesturesEnabledWithError: } - (nullable NSArray *) - getClustersWithIdentifier:(NSString *)clusterManagerId - error:(FlutterError *_Nullable *_Nonnull)error { - return [self.controller.clusterManagersController getClustersWithIdentifier:clusterManagerId - error:error]; + clustersWithIdentifier:(NSString *)clusterManagerId + error:(FlutterError *_Nullable *_Nonnull)error { + return [self.controller.clusterManagersController clustersWithIdentifier:clusterManagerId + error:error]; } - (nullable NSNumber *)isCompassEnabledWithError: diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h index 481ac5e32cb..20669fead87 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN @property(assign, nonatomic, readonly) BOOL consumeTapEvents; - (instancetype)initWithMarker:(GMSMarker *)marker markerIdentifier:(NSString *)markerIdentifier - clusterManagerIdentifier:(NSString *)clusterManagerIdentifier + clusterManagerIdentifier:(nullable NSString *)clusterManagerIdentifier mapView:(GMSMapView *)mapView; - (void)showInfoWindow; - (void)hideInfoWindow; @@ -24,11 +24,10 @@ NS_ASSUME_NONNULL_BEGIN @end @interface FLTMarkersController : NSObject -- (instancetype)initWithClusterManagersController: - (nullable FLTClusterManagersController *)clusterManagers - callbackHandler:(FGMMapsCallbackApi *)callbackHandler - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar; +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + clusterManagersController:(nullable FLTClusterManagersController *)clusterManagersController + registrar:(NSObject *)registrar; - (void)addJSONMarkers:(NSArray *> *)markersToAdd; - (void)addMarkers:(NSArray *)markersToAdd; - (void)changeMarkers:(NSArray *)markersToChange; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m index 8122e5c611b..811878a4da3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m @@ -4,7 +4,7 @@ #import "GoogleMapMarkerController.h" #import "FLTGoogleMapJSONConversions.h" -#import "GoogleMarkerUtilities.h" +#import "FLTGoogleMarkerUserData.h" @interface FLTGoogleMapMarkerController () @@ -12,7 +12,7 @@ @interface FLTGoogleMapMarkerController () @property(weak, nonatomic) GMSMapView *mapView; @property(assign, nonatomic, readwrite) BOOL consumeTapEvents; /// The unique identifier for the cluster manager. -@property(copy, nonatomic) NSString *clusterManagerIdentifier; +@property(copy, nonatomic, nullable) NSString *clusterManagerIdentifier; /// The unique identifier for the marker. @property(copy, nonatomic) NSString *markerIdentifier; @@ -22,7 +22,7 @@ @implementation FLTGoogleMapMarkerController - (instancetype)initWithMarker:(GMSMarker *)marker markerIdentifier:(NSString *)markerIdentifier - clusterManagerIdentifier:(NSString *)clusterManagerIdentifier + clusterManagerIdentifier:(nullable NSString *)clusterManagerIdentifier mapView:(GMSMapView *)mapView { self = [super init]; if (self) { @@ -30,7 +30,7 @@ - (instancetype)initWithMarker:(GMSMarker *)marker _markerIdentifier = [markerIdentifier copy]; _clusterManagerIdentifier = [clusterManagerIdentifier copy]; _mapView = mapView; - [self updateMarkerUserData]; + FLTSetIdentifiersToMarkerUserData(_markerIdentifier, _clusterManagerIdentifier, _marker); } return self; } @@ -105,14 +105,6 @@ - (void)setZIndex:(int)zIndex { self.marker.zIndex = zIndex; } -- (void)updateMarkerUserData { - if (self.clusterManagerIdentifier) { - [GoogleMarkerUtilities setMarkerIdentifier:self.markerIdentifier andClusterManagerIdentifier:self.clusterManagerIdentifier for:self.marker]; - } else { - [GoogleMarkerUtilities setMarkerIdentifier:self.markerIdentifier for:self.marker]; - } -} - - (void)interpretMarkerOptions:(NSDictionary *)data registrar:(NSObject *)registrar screenScale:(CGFloat)screenScale { @@ -464,16 +456,15 @@ @interface FLTMarkersController () @implementation FLTMarkersController -- (instancetype)initWithClusterManagersController: - (nullable FLTClusterManagersController *)clusterManagers - callbackHandler:(FGMMapsCallbackApi *)callbackHandler - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar { +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + clusterManagersController:(nullable FLTClusterManagersController *)clusterManagersController + registrar:(NSObject *)registrar { self = [super init]; if (self) { _callbackHandler = callbackHandler; _mapView = mapView; - _clusterManagersController = clusterManagers; + _clusterManagersController = clusterManagersController; _markerIdentifierToController = [[NSMutableDictionary alloc] init]; _registrar = registrar; } @@ -505,10 +496,10 @@ - (void)addJSONMarker:(NSDictionary *)markerToAdd { [controller interpretMarkerOptions:markerToAdd registrar:self.registrar screenScale:[self getScreenScale]]; - if (clusterManagerIdentifier && [clusterManagerIdentifier isKindOfClass:[NSString class]]) { + if (clusterManagerIdentifier) { GMUClusterManager *clusterManager = [_clusterManagersController clusterManagerWithIdentifier:clusterManagerIdentifier]; - if (marker && clusterManager) { + if ([marker conformsToProtocol:@protocol(GMUClusterItem)]) { [clusterManager addItem:(id)marker]; } } @@ -551,13 +542,11 @@ - (void)removeMarker:(NSString *)identifier { if (!controller) { return; } - id clusterManagerIdentifier = [controller clusterManagerIdentifier]; - if ([clusterManagerIdentifier isKindOfClass:[NSString class]]) { + NSString *clusterManagerIdentifier = [controller clusterManagerIdentifier]; + if (clusterManagerIdentifier) { GMUClusterManager *clusterManager = [_clusterManagersController clusterManagerWithIdentifier:clusterManagerIdentifier]; - if (controller.marker && clusterManager) { - [clusterManager removeItem:(id)controller.marker]; - } + [clusterManager removeItem:(id)controller.marker]; } else { [controller removeMarker]; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.h deleted file mode 100644 index 305f75bfaad..00000000000 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -/// Defines user data object for markers. -@interface GoogleMarkerUserData : NSObject - -/// The identifier of the marker. -@property(nonatomic, copy) NSString *markerIdentifier; - -/// The identifier of the cluster manager. -/// This property is set only if the marker is managed by a cluster manager. -@property(nonatomic, copy) NSString *clusterManagerIdentifier; - -@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.m deleted file mode 100644 index 7632799577b..00000000000 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUserData.m +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "GoogleMarkerUserData.h" - -@implementation GoogleMarkerUserData - -@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.h deleted file mode 100644 index 2502fb4fd8c..00000000000 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface GoogleMarkerUtilities : NSObject - -/// Sets MarkerId to GMSMarker UserData. -/// -/// @param markerIdentifier Identifier of the marker. -/// @param marker GMSMarker object. -+ (void)setMarkerIdentifier:(NSString *)markerIdentifier for:(GMSMarker*)marker; - -/// Sets MarkerId and ClusterManagerId to GMSMarker UserData. -/// -/// @param markerIdentifier Identifier of marker. -/// @param clusterManagerIdentifier Identifier of cluster manager. -/// @param marker GMSMarker object. -+ (void)setMarkerIdentifier:(NSString *)markerIdentifier andClusterManagerIdentifier:(NSString *)clusterManagerIdentifier for:(GMSMarker*)marker; - -/// Get MarkerIdentifier from GMSMarker UserData. -/// -/// @param marker GMSMarker object. -/// @return NSString if found; otherwise, nil. -+ (nullable NSString *)getMarkerIdentifierFrom:(GMSMarker *)marker; - -/// Get ClusterManagerIdentifier from GMSMarker UserData. -/// -/// @param marker GMSMarker object. -/// @return NSString if found; otherwise, nil. -+ (nullable NSString *)getClusterManagerIdentifierFrom:(GMSMarker *)marker; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.m deleted file mode 100644 index d0d183417b8..00000000000 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMarkerUtilities.m +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "GoogleMarkerUtilities.h" -#import "GoogleMarkerUserData.h" - -@implementation GoogleMarkerUtilities - -+ (void)setMarkerIdentifier:(NSString *)markerIdentifier for:(GMSMarker*)marker { - GoogleMarkerUserData *userData = [[GoogleMarkerUserData alloc] init]; - userData.markerIdentifier = markerIdentifier; - marker.userData = userData; -} - -+ (void)setMarkerIdentifier:(NSString *)markerIdentifier andClusterManagerIdentifier:(NSString *)clusterManagerIdentifier for:(GMSMarker*)marker { - GoogleMarkerUserData *userData = [[GoogleMarkerUserData alloc] init]; - userData.markerIdentifier = markerIdentifier; - userData.clusterManagerIdentifier = clusterManagerIdentifier; - marker.userData = userData; -} - -+ (nullable NSString *)getMarkerIdentifierFrom:(GMSMarker *)marker { - if ([marker.userData isKindOfClass:[GoogleMarkerUserData class]]) { - GoogleMarkerUserData *userData = (GoogleMarkerUserData *)marker.userData; - return userData.markerIdentifier; - } - return nil; -} - -+ (nullable NSString *)getClusterManagerIdentifierFrom:(GMSMarker *)marker { - if ([marker.userData isKindOfClass:[GoogleMarkerUserData class]]) { - GoogleMarkerUserData *userData = (GoogleMarkerUserData *)marker.userData; - return userData.clusterManagerIdentifier; - } - return nil; -} -@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h index c1057c7bdda..070c24e90e5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h @@ -372,8 +372,8 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger - (void)didTapCircleWithIdentifier:(NSString *)circleId completion:(void (^)(FlutterError *_Nullable))completion; /// Called when a marker cluster is tapped. -- (void)onClusterTapCluster:(FGMPlatformCluster *)cluster - completion:(void (^)(FlutterError *_Nullable))completion; +- (void)didTapCluster:(FGMPlatformCluster *)cluster + completion:(void (^)(FlutterError *_Nullable))completion; /// Called when a polygon is tapped. - (void)didTapPolygonWithIdentifier:(NSString *)polygonId completion:(void (^)(FlutterError *_Nullable))completion; @@ -415,8 +415,8 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger - (nullable FGMPlatformZoomRange *)zoomRange:(FlutterError *_Nullable *_Nonnull)error; /// @return `nil` only when `error != nil`. - (nullable NSArray *) - getClustersWithIdentifier:(NSString *)clusterManagerId - error:(FlutterError *_Nullable *_Nonnull)error; + clustersWithIdentifier:(NSString *)clusterManagerId + error:(FlutterError *_Nullable *_Nonnull)error; @end extern void SetUpFGMMapsInspectorApi(id binaryMessenger, diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m index 3b2aa3c01a1..27c32b8b4ca 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m @@ -1573,8 +1573,8 @@ - (void)didTapCircleWithIdentifier:(NSString *)arg_circleId } }]; } -- (void)onClusterTapCluster:(FGMPlatformCluster *)arg_cluster - completion:(void (^)(FlutterError *_Nullable))completion { +- (void)didTapCluster:(FGMPlatformCluster *)arg_cluster + completion:(void (^)(FlutterError *_Nullable))completion { NSString *channelName = [NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onClusterTap", @@ -1946,16 +1946,16 @@ void SetUpFGMMapsInspectorApiWithSuffix(id binaryMesseng binaryMessenger:binaryMessenger codec:FGMGetMessagesCodec()]; if (api) { - NSCAssert([api respondsToSelector:@selector(getClustersWithIdentifier:error:)], + NSCAssert([api respondsToSelector:@selector(clustersWithIdentifier:error:)], @"FGMMapsInspectorApi api (%@) doesn't respond to " - @"@selector(getClustersWithIdentifier:error:)", + @"@selector(clustersWithIdentifier:error:)", api); [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { NSArray *args = message; NSString *arg_clusterManagerId = GetNullableObjectAtIndex(args, 0); FlutterError *error; - NSArray *output = [api getClustersWithIdentifier:arg_clusterManagerId - error:&error]; + NSArray *output = [api clustersWithIdentifier:arg_clusterManagerId + error:&error]; callback(wrapResult(output, error)); }]; } else { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/utils/cluster_manager.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/utils/cluster_manager.dart index 854690206a2..d644bfd8c0e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/utils/cluster_manager.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/utils/cluster_manager.dart @@ -17,18 +17,3 @@ Object serializeClusterManager(ClusterManager clusterManager) { json['clusterManagerId'] = clusterManager.clusterManagerId.value; return json; } - -/// Converts a Cluster Manager updates into object serializable in JSON. -Object serializeClusterManagerUpdates( - ClusterManagerUpdates clusterManagerUpdates) { - final Map updateMap = {}; - - updateMap['clusterManagersToAdd'] = - serializeClusterManagerSet(clusterManagerUpdates.objectsToAdd); - updateMap['clusterManagerIdsToRemove'] = clusterManagerUpdates - .objectIdsToRemove - .map((MapsObjectId id) => id.value) - .toList(); - - return updateMap; -} diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart index af05fe01cf9..e5c11bed645 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart @@ -388,6 +388,7 @@ abstract class MapsCallbackApi { void onCircleTap(String circleId); /// Called when a marker cluster is tapped. + @ObjCSelector('didTapCluster:') void onClusterTap(PlatformCluster cluster); /// Called when a polygon is tapped. @@ -425,6 +426,6 @@ abstract class MapsInspectorApi { // TODO(stuartmorgan): Make the generic type non-nullable once supported. // https://github.com/flutter/flutter/issues/97848 // The consuming code treats the entries as non-nullable. - @ObjCSelector('getClustersWithIdentifier:') + @ObjCSelector('clustersWithIdentifier:') List getClusters(String clusterManagerId); } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/cluster_manager_utils_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/cluster_manager_utils_test.dart index 55d2cc98137..d7ea7c0379f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/test/cluster_manager_utils_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/cluster_manager_utils_test.dart @@ -41,26 +41,4 @@ void main() { } ]); }); - - test('serializeClusterManagerUpdates', () async { - final ClusterManagerUpdates updates = ClusterManagerUpdates.from( - { - const ClusterManager(clusterManagerId: ClusterManagerId('1234')) - }, - { - const ClusterManager(clusterManagerId: ClusterManagerId('5678')) - }, - ); - - final Object json = serializeClusterManagerUpdates(updates); - - expect(json, { - 'clusterManagerIdsToRemove': ['1234'], - 'clusterManagersToAdd': [ - { - 'clusterManagerId': '5678', - } - ], - }); - }); } From f91197bf5ec98c1748d835be66b01990804eaece Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Sat, 3 Aug 2024 14:37:34 +0300 Subject: [PATCH 3/8] [google_maps_flutter_ios] Update swift_version to match the version in Google-Maps-iOS-Utils --- .../google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec index 2715a7dcb15..f744daa6853 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/google_maps_flutter_ios.podspec @@ -32,7 +32,7 @@ Downloaded by pub (not CocoaPods). s.platform = :ios, '14.0' # "Google-Maps-iOS-Utils" is static and contains Swift classes. # Find the Swift runtime when these plugins are built as libraries without `use_frameworks!` - s.swift_version = '5.0' + s.swift_version = '5.9' s.xcconfig = { 'LIBRARY_SEARCH_PATHS' => '$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift', 'LD_RUNPATH_SEARCH_PATHS' => '$(inherited) /usr/lib/swift', From 77c78c153430890a54a5ac41deba2a8e6a215b4b Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Sat, 3 Aug 2024 14:55:18 +0300 Subject: [PATCH 4/8] [google_maps_flutter_ios] Fix clustering tests --- .../FLTClusterManagersControllerTests.m | 17 +++++++++-------- .../Classes/google_maps_flutter_ios-umbrella.h | 3 +-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m index 754abf1f6da..f2fd209dde4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m @@ -29,12 +29,13 @@ - (void)testClustering { id handler = OCMClassMock([FGMMapsCallbackApi class]); FLTClusterManagersController *clusterManagersController = - [[FLTClusterManagersController alloc] initWithCallbackHandler:handler mapView:mapView]; + [[FLTClusterManagersController alloc] initWithMapView:mapView callbackHandler:handler]; + FLTMarkersController *markersController = - [[FLTMarkersController alloc] initWithClusterManagersController:clusterManagersController - callbackHandler:handler - mapView:mapView - registrar:registrar]; + [[FLTMarkersController alloc] initWithMapView:mapView + callbackHandler:handler + clusterManagersController:clusterManagersController + registrar:registrar]; // Add cluster managers. NSString *clusterManagerId = @"cm"; @@ -77,7 +78,7 @@ - (void)testClustering { // Verify that the markers were added to the cluster manager NSArray *clusters1 = - [clusterManagersController getClustersWithIdentifier:clusterManagerId error:&error]; + [clusterManagersController clustersWithIdentifier:clusterManagerId error:&error]; XCTAssertNil(error, @"Error should be nil"); for (FGMPlatformCluster *cluster in clusters1) { NSString *cmId = cluster.clusterManagerId; @@ -95,7 +96,7 @@ - (void)testClustering { // Verify that the marker2 is removed from the clusterManager NSArray *clusters2 = - [clusterManagersController getClustersWithIdentifier:clusterManagerId error:&error]; + [clusterManagersController clustersWithIdentifier:clusterManagerId error:&error]; XCTAssertNil(error, @"Error should be nil"); for (FGMPlatformCluster *cluster in clusters2) { @@ -113,7 +114,7 @@ - (void)testClustering { // Verify that all markers are removed from clusterManager NSArray *clusters3 = - [clusterManagersController getClustersWithIdentifier:clusterManagerId error:&error]; + [clusterManagersController clustersWithIdentifier:clusterManagerId error:&error]; XCTAssertNil(error, @"Error should be nil"); XCTAssertEqual(clusters3.count, 0, @"Cluster Manager should not contain any clusters"); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h index d749a5ab85d..ef380aca498 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h @@ -7,9 +7,8 @@ #import #import #import +#import #import -#import -#import FOUNDATION_EXPORT double google_maps_flutterVersionNumber; FOUNDATION_EXPORT const unsigned char google_maps_flutterVersionString[]; From b78f1189be7aaed2432a96b5cc4b08c5a38f3af6 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Sat, 3 Aug 2024 15:36:57 +0300 Subject: [PATCH 5/8] [google_maps_flutter_ios] Rename new classes to use FGM prefix --- .../ios/Runner.xcodeproj/project.pbxproj | 4 +-- ....m => FGMClusterManagersControllerTests.m} | 8 ++--- ...oller.h => FGMClusterManagersController.h} | 2 +- ...oller.m => FGMClusterManagersController.m} | 10 +++--- ...leMarkerUserData.h => FGMMarkerUserData.h} | 8 ++--- .../ios/Classes/FGMMarkerUserData.m | 34 +++++++++++++++++++ .../ios/Classes/FLTGoogleMapJSONConversions.m | 4 +-- .../ios/Classes/FLTGoogleMapsPlugin.h | 2 +- .../ios/Classes/FLTGoogleMarkerUserData.m | 34 ------------------- .../ios/Classes/GoogleMapController.h | 2 +- .../ios/Classes/GoogleMapController.m | 24 +++++++------ .../ios/Classes/GoogleMapMarkerController.h | 4 +-- .../ios/Classes/GoogleMapMarkerController.m | 8 ++--- .../google_maps_flutter_ios-umbrella.h | 2 +- 14 files changed, 75 insertions(+), 71 deletions(-) rename packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/{FLTClusterManagersControllerTests.m => FGMClusterManagersControllerTests.m} (95%) rename packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/{FLTClusterManagersController.h => FGMClusterManagersController.h} (98%) rename packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/{FLTClusterManagersController.m => FGMClusterManagersController.m} (95%) rename packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/{FLTGoogleMarkerUserData.h => FGMMarkerUserData.h} (82%) create mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.m delete mode 100644 packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.m diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj index 5ef22647478..07befd30b82 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj @@ -208,7 +208,7 @@ children = ( F269303A2BB389BF00BF17C4 /* assets */, 52E9C2AF2C5CECD100078060 /* ExtractIconFromDataTests.m */, - 52E9C2AD2C5CEBD600078060 /* FLTClusterManagersControllerTests.m */, + 52E9C2AD2C5CEBD600078060 /* FGMClusterManagersControllerTests.m */, 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */, 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */, F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, @@ -512,7 +512,7 @@ buildActionMask = 2147483647; files = ( F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, - 52E9C2AE2C5CEBD600078060 /* FLTClusterManagersControllerTests.m in Sources */, + 52E9C2AE2C5CEBD600078060 /* FGMClusterManagersControllerTests.m in Sources */, 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */, 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */, diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FGMClusterManagersControllerTests.m similarity index 95% rename from packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m rename to packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FGMClusterManagersControllerTests.m index f2fd209dde4..97607bf63ca 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTClusterManagersControllerTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FGMClusterManagersControllerTests.m @@ -11,10 +11,10 @@ #import #import "PartiallyMockedMapView.h" -@interface FLTClusterManagersControllerTests : XCTestCase +@interface FGMClusterManagersControllerTests : XCTestCase @end -@implementation FLTClusterManagersControllerTests +@implementation FGMClusterManagersControllerTests - (void)testClustering { NSObject *registrar = OCMProtocolMock(@protocol(FlutterPluginRegistrar)); @@ -28,8 +28,8 @@ - (void)testClustering { id handler = OCMClassMock([FGMMapsCallbackApi class]); - FLTClusterManagersController *clusterManagersController = - [[FLTClusterManagersController alloc] initWithMapView:mapView callbackHandler:handler]; + FGMClusterManagersController *clusterManagersController = + [[FGMClusterManagersController alloc] initWithMapView:mapView callbackHandler:handler]; FLTMarkersController *markersController = [[FLTMarkersController alloc] initWithMapView:mapView diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.h similarity index 98% rename from packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.h index 813b937ca92..c77c4127b0d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.h @@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN // Defines cluster managers controller interface which // is responsible for adding/removing/returning one or more cluster managers. -@interface FLTClusterManagersController : NSObject +@interface FGMClusterManagersController : NSObject /// Initializes FLTClusterManagersController. /// diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.m similarity index 95% rename from packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.m index f0262da9ffd..7114bc532bf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTClusterManagersController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.m @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "FLTClusterManagersController.h" +#import "FGMClusterManagersController.h" +#import "FGMMarkerUserData.h" #import "FLTGoogleMapJSONConversions.h" -#import "FLTGoogleMarkerUserData.h" -@interface FLTClusterManagersController () +@interface FGMClusterManagersController () /// A dictionary that cluster managers unique identifiers to GMUClusterManager instances. @property(strong, nonatomic) @@ -21,7 +21,7 @@ @interface FLTClusterManagersController () @end -@implementation FLTClusterManagersController +@implementation FGMClusterManagersController - (instancetype)initWithMapView:(GMSMapView *)mapView callbackHandler:(FGMMapsCallbackApi *)callbackHandler { self = [super init]; @@ -128,7 +128,7 @@ - (void)didTapCluster:(GMUStaticCluster *)cluster { - (nullable NSString *)clusterManagerIdentifierForCluster:(GMUStaticCluster *)cluster { if ([cluster.items.firstObject isKindOfClass:[GMSMarker class]]) { GMSMarker *firstMarker = (GMSMarker *)cluster.items.firstObject; - return FLTGetClusterManagerIdentifierFrom(firstMarker); + return FGMGetClusterManagerIdentifierFromMarker(firstMarker); } return nil; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.h similarity index 82% rename from packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.h rename to packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.h index df35904b278..a4d881635ff 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.h @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN /// Defines user data object for markers. -@interface FLTGoogleMarkerUserData : NSObject +@interface FGMMarkerUserData : NSObject /// The identifier of the marker. @property(nonatomic, copy) NSString *markerIdentifier; @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN /// @param markerIdentifier Identifier of marker. /// @param clusterManagerIdentifier Optional identifier of cluster manager. /// @param marker GMSMarker object. -extern void FLTSetIdentifiersToMarkerUserData(NSString *markerIdentifier, +extern void FGMSetIdentifiersToMarkerUserData(NSString *markerIdentifier, NSString *_Nullable clusterManagerIdentifier, GMSMarker *marker); @@ -31,12 +31,12 @@ extern void FLTSetIdentifiersToMarkerUserData(NSString *markerIdentifier, /// /// @param marker GMSMarker object. /// @return NSString if found; otherwise, nil. -extern NSString *_Nullable FLTGetMarkerIdentifierFrom(GMSMarker *marker); +extern NSString *_Nullable FGMGetMarkerIdentifierFromMarker(GMSMarker *marker); /// Get ClusterManagerIdentifier from GMSMarker UserData. /// /// @param marker GMSMarker object. /// @return NSString if found; otherwise, nil. -extern NSString *_Nullable FLTGetClusterManagerIdentifierFrom(GMSMarker *marker); +extern NSString *_Nullable FGMGetClusterManagerIdentifierFromMarker(GMSMarker *marker); NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.m new file mode 100644 index 00000000000..4e8e365c1ec --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.m @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FGMMarkerUserData.h" + +@implementation FGMMarkerUserData + +@end + +void FGMSetIdentifiersToMarkerUserData(NSString *markerIdentifier, + NSString *_Nullable clusterManagerIdentifier, + GMSMarker *marker) { + FGMMarkerUserData *userData = [[FGMMarkerUserData alloc] init]; + userData.markerIdentifier = markerIdentifier; + userData.clusterManagerIdentifier = clusterManagerIdentifier; + marker.userData = userData; +}; + +NSString *_Nullable FGMGetMarkerIdentifierFromMarker(GMSMarker *marker) { + if ([marker.userData isKindOfClass:[FGMMarkerUserData class]]) { + FGMMarkerUserData *userData = (FGMMarkerUserData *)marker.userData; + return userData.markerIdentifier; + } + return nil; +}; + +NSString *_Nullable FGMGetClusterManagerIdentifierFromMarker(GMSMarker *marker) { + if ([marker.userData isKindOfClass:[FGMMarkerUserData class]]) { + FGMMarkerUserData *userData = (FGMMarkerUserData *)marker.userData; + return userData.clusterManagerIdentifier; + } + return nil; +}; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m index 152e1c6061d..6542c998c83 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m @@ -3,7 +3,7 @@ // found in the LICENSE file. #import "FLTGoogleMapJSONConversions.h" -#import "FLTGoogleMarkerUserData.h" +#import "FGMMarkerUserData.h" /// Returns dict[key], or nil if dict[key] is NSNull. id FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key) { @@ -46,7 +46,7 @@ CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] init]; for (GMSMarker *marker in cluster.items) { - [markerIDs addObject:FLTGetMarkerIdentifierFrom(marker)]; + [markerIDs addObject:FGMGetMarkerIdentifierFromMarker(marker)]; bounds = [bounds includingCoordinate:marker.position]; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h index 7bb693fb700..b21ef16dcf8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h @@ -4,7 +4,7 @@ #import #import -#import "FLTClusterManagersController.h" +#import "FGMClusterManagersController.h" #import "GoogleMapCircleController.h" #import "GoogleMapController.h" #import "GoogleMapMarkerController.h" diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.m deleted file mode 100644 index 4df5f075cd0..00000000000 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMarkerUserData.m +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "FLTGoogleMarkerUserData.h" - -@implementation FLTGoogleMarkerUserData - -@end - -void FLTSetIdentifiersToMarkerUserData(NSString *markerIdentifier, - NSString *_Nullable clusterManagerIdentifier, - GMSMarker *marker) { - FLTGoogleMarkerUserData *userData = [[FLTGoogleMarkerUserData alloc] init]; - userData.markerIdentifier = markerIdentifier; - userData.clusterManagerIdentifier = clusterManagerIdentifier; - marker.userData = userData; -}; - -NSString *_Nullable FLTGetMarkerIdentifierFrom(GMSMarker *marker) { - if ([marker.userData isKindOfClass:[FLTGoogleMarkerUserData class]]) { - FLTGoogleMarkerUserData *userData = (FLTGoogleMarkerUserData *)marker.userData; - return userData.markerIdentifier; - } - return nil; -}; - -NSString *_Nullable FLTGetClusterManagerIdentifierFrom(GMSMarker *marker) { - if ([marker.userData isKindOfClass:[FLTGoogleMarkerUserData class]]) { - FLTGoogleMarkerUserData *userData = (FLTGoogleMarkerUserData *)marker.userData; - return userData.clusterManagerIdentifier; - } - return nil; -}; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h index a4ba7db158c..45f0a68cae2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h @@ -4,7 +4,7 @@ #import #import -#import "FLTClusterManagersController.h" +#import "FGMClusterManagersController.h" #import "GoogleMapCircleController.h" #import "GoogleMapMarkerController.h" #import "GoogleMapPolygonController.h" diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 718276229b4..df49a63902f 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -5,10 +5,11 @@ @import GoogleMapsUtils; #import "GoogleMapController.h" + +#import "FGMMarkerUserData.h" #import "FLTGoogleMapHeatmapController.h" #import "FLTGoogleMapJSONConversions.h" #import "FLTGoogleMapTileOverlayController.h" -#import "FLTGoogleMarkerUserData.h" #import "messages.g.h" #pragma mark - Conversion of JSON-like values sent via platform channels. Forward declarations. @@ -119,7 +120,7 @@ @interface FLTGoogleMapController () @property(nonatomic, strong) FGMMapsCallbackApi *dartCallbackHandler; @property(nonatomic, assign) BOOL trackCameraPosition; @property(nonatomic, weak) NSObject *registrar; -@property(nonatomic, strong) FLTClusterManagersController *clusterManagersController; +@property(nonatomic, strong) FGMClusterManagersController *clusterManagersController; @property(nonatomic, strong) FLTMarkersController *markersController; @property(nonatomic, strong) FLTPolygonsController *polygonsController; @property(nonatomic, strong) FLTPolylinesController *polylinesController; @@ -180,7 +181,7 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView _mapView.paddingAdjustmentBehavior = kGMSMapViewPaddingAdjustmentBehaviorNever; _registrar = registrar; _clusterManagersController = - [[FLTClusterManagersController alloc] initWithMapView:_mapView + [[FGMClusterManagersController alloc] initWithMapView:_mapView callbackHandler:_dartCallbackHandler]; _markersController = [[FLTMarkersController alloc] initWithMapView:_mapView callbackHandler:_dartCallbackHandler @@ -408,27 +409,30 @@ - (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { // When NO is returned, the map will focus on the cluster. return NO; } - return [self.markersController didTapMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker)]; + return + [self.markersController didTapMarkerWithIdentifier:FGMGetMarkerIdentifierFromMarker(marker)]; } - (void)mapView:(GMSMapView *)mapView didEndDraggingMarker:(GMSMarker *)marker { - [self.markersController didEndDraggingMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker) - location:marker.position]; + [self.markersController + didEndDraggingMarkerWithIdentifier:FGMGetMarkerIdentifierFromMarker(marker) + location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didBeginDraggingMarker:(GMSMarker *)marker { - [self.markersController didStartDraggingMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker) - location:marker.position]; + [self.markersController + didStartDraggingMarkerWithIdentifier:FGMGetMarkerIdentifierFromMarker(marker) + location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didDragMarker:(GMSMarker *)marker { - [self.markersController didDragMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker) + [self.markersController didDragMarkerWithIdentifier:FGMGetMarkerIdentifierFromMarker(marker) location:marker.position]; } - (void)mapView:(GMSMapView *)mapView didTapInfoWindowOfMarker:(GMSMarker *)marker { [self.markersController - didTapInfoWindowOfMarkerWithIdentifier:FLTGetMarkerIdentifierFrom(marker)]; + didTapInfoWindowOfMarkerWithIdentifier:FGMGetMarkerIdentifierFromMarker(marker)]; } - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { NSString *overlayId = overlay.userData[0]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h index 20669fead87..a46e6fd5e97 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h @@ -4,7 +4,7 @@ #import #import -#import "FLTClusterManagersController.h" +#import "FGMClusterManagersController.h" #import "GoogleMapController.h" #import "messages.g.h" @@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTMarkersController : NSObject - (instancetype)initWithMapView:(GMSMapView *)mapView callbackHandler:(FGMMapsCallbackApi *)callbackHandler - clusterManagersController:(nullable FLTClusterManagersController *)clusterManagersController + clusterManagersController:(nullable FGMClusterManagersController *)clusterManagersController registrar:(NSObject *)registrar; - (void)addJSONMarkers:(NSArray *> *)markersToAdd; - (void)addMarkers:(NSArray *)markersToAdd; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m index 811878a4da3..6c6b6610118 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m @@ -3,8 +3,8 @@ // found in the LICENSE file. #import "GoogleMapMarkerController.h" +#import "FGMMarkerUserData.h" #import "FLTGoogleMapJSONConversions.h" -#import "FLTGoogleMarkerUserData.h" @interface FLTGoogleMapMarkerController () @@ -30,7 +30,7 @@ - (instancetype)initWithMarker:(GMSMarker *)marker _markerIdentifier = [markerIdentifier copy]; _clusterManagerIdentifier = [clusterManagerIdentifier copy]; _mapView = mapView; - FLTSetIdentifiersToMarkerUserData(_markerIdentifier, _clusterManagerIdentifier, _marker); + FGMSetIdentifiersToMarkerUserData(_markerIdentifier, _clusterManagerIdentifier, _marker); } return self; } @@ -448,7 +448,7 @@ @interface FLTMarkersController () @property(strong, nonatomic) NSMutableDictionary *markerIdentifierToController; @property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; /// Controller for adding/removing/fetching cluster managers -@property(weak, nonatomic, nullable) FLTClusterManagersController *clusterManagersController; +@property(weak, nonatomic, nullable) FGMClusterManagersController *clusterManagersController; @property(weak, nonatomic) NSObject *registrar; @property(weak, nonatomic) GMSMapView *mapView; @@ -458,7 +458,7 @@ @implementation FLTMarkersController - (instancetype)initWithMapView:(GMSMapView *)mapView callbackHandler:(FGMMapsCallbackApi *)callbackHandler - clusterManagersController:(nullable FLTClusterManagersController *)clusterManagersController + clusterManagersController:(nullable FGMClusterManagersController *)clusterManagersController registrar:(NSObject *)registrar { self = [super init]; if (self) { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h index ef380aca498..c88ee79c7a0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios-umbrella.h @@ -3,11 +3,11 @@ // found in the LICENSE file. #import +#import #import #import #import #import -#import #import FOUNDATION_EXPORT double google_maps_flutterVersionNumber; From b25170bc4de20ed0d3d522fa6eab66d293988bc8 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Tue, 6 Aug 2024 21:27:32 +0300 Subject: [PATCH 6/8] [google_maps_flutter_ios] improve obj-c documentation --- .../ios/Runner.xcodeproj/project.pbxproj | 16 +++---- .../Classes/FGMClusterManagersController.h | 43 ++++++++----------- .../Classes/FGMClusterManagersController.m | 16 +++---- .../ios/Classes/FGMMarkerUserData.h | 17 +++----- .../ios/Classes/FLTGoogleMapsPlugin.h | 1 + .../ios/Classes/GoogleMapController.h | 6 ++- .../ios/Classes/GoogleMapMarkerController.h | 6 ++- .../ios/Classes/GoogleMapMarkerController.m | 1 + 8 files changed, 48 insertions(+), 58 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj index 07befd30b82..b9fa7719dcf 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/Runner.xcodeproj/project.pbxproj @@ -12,8 +12,8 @@ 2BDE99378062AE3E60B40021 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ACE0AFE8D82CD5962486AFD /* Pods_RunnerTests.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */; }; - 521AB0032B876A76005F460D /* ExtractIconFromDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 521AB0022B876A76005F460D /* ExtractIconFromDataTests.m */; }; - 52E9C2AE2C5CEBD600078060 /* FLTClusterManagersControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 52E9C2AD2C5CEBD600078060 /* FLTClusterManagersControllerTests.m */; }; + 528F16832C62941000148160 /* FGMClusterManagersControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 528F16822C62941000148160 /* FGMClusterManagersControllerTests.m */; }; + 528F16872C62952700148160 /* ExtractIconFromDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 528F16862C62952700148160 /* ExtractIconFromDataTests.m */; }; 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */; }; 68E4726A2836FF0C00BDDDAC /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E472692836FF0C00BDDDAC /* MapKit.framework */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; @@ -65,9 +65,9 @@ 3ACE0AFE8D82CD5962486AFD /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsPolylinesControllerTests.m; sourceTree = ""; }; - 521AB0022B876A76005F460D /* ExtractIconFromDataTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExtractIconFromDataTests.m; sourceTree = ""; }; + 528F16822C62941000148160 /* FGMClusterManagersControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FGMClusterManagersControllerTests.m; sourceTree = ""; }; + 528F16862C62952700148160 /* ExtractIconFromDataTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExtractIconFromDataTests.m; sourceTree = ""; }; 61A9A8623F5CA9BBC813DC6B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 52E9C2AD2C5CEBD600078060 /* FLTClusterManagersControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTClusterManagersControllerTests.m; sourceTree = ""; }; 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = ""; }; 68E472692836FF0C00BDDDAC /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.0.sdk/System/iOSSupport/System/Library/Frameworks/MapKit.framework; sourceTree = DEVELOPER_DIR; }; 733AFAB37683A9DA7512F09C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; @@ -207,8 +207,8 @@ isa = PBXGroup; children = ( F269303A2BB389BF00BF17C4 /* assets */, - 52E9C2AF2C5CECD100078060 /* ExtractIconFromDataTests.m */, - 52E9C2AD2C5CEBD600078060 /* FGMClusterManagersControllerTests.m */, + 528F16862C62952700148160 /* ExtractIconFromDataTests.m */, + 528F16822C62941000148160 /* FGMClusterManagersControllerTests.m */, 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */, 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */, F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, @@ -511,13 +511,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 528F16832C62941000148160 /* FGMClusterManagersControllerTests.m in Sources */, F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, - 52E9C2AE2C5CEBD600078060 /* FGMClusterManagersControllerTests.m in Sources */, 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */, 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */, 0DD7B6C32B744EEF00E857FD /* FLTTileProviderControllerTests.m in Sources */, - 52E9C2B02C5CECD100078060 /* ExtractIconFromDataTests.m in Sources */, + 528F16872C62952700148160 /* ExtractIconFromDataTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.h index c77c4127b0d..9e5f6f16ae6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.h @@ -10,60 +10,51 @@ NS_ASSUME_NONNULL_BEGIN -// Defines cluster managers controller interface which -// is responsible for adding/removing/returning one or more cluster managers. +/// A controller that manages all of the cluster managers on a map. @interface FGMClusterManagersController : NSObject -/// Initializes FLTClusterManagersController. +/// Initializes cluster manager controller. /// /// @param callbackHandler A callback handler. /// @param mapView A map view that will be used to display clustered markers. - (instancetype)initWithMapView:(GMSMapView *)mapView callbackHandler:(FGMMapsCallbackApi *)callbackHandler; -/// Creates ClusterManagers and initializes them form JSON data. +/// Creates cluster managers and initializes them form JSON data. /// -/// @param clusterManagersToAdd List of clustermanager object data. +/// @param clusterManagersToAdd Array of cluster managers JSON data to add. - (void)addJSONClusterManagers:(NSArray *)clusterManagersToAdd; -/// Creates ClusterManagers and initializes them. +/// Creates cluster managers and initializes them. /// -/// @param clusterManagersToAdd List of clustermanager object data. +/// @param clusterManagersToAdd Array of cluster managers to add. - (void)addClusterManagers:(NSArray *)clusterManagersToAdd; -/// Removes requested ClusterManagers from the controller. +/// Removes requested cluster managers from the controller. /// -/// @param identifiers List of clusterManagerIds to remove. - +/// @param identifiers Array of cluster manager IDs to remove. - (void)removeClusterManagersWithIdentifiers:(NSArray *)identifiers; -/// Returns the ClusterManager for the given identifier. +/// Returns the cluster managers for the given identifier. /// -/// @param identifier identifier of the ClusterManager. -/// @return GMUClusterManager if found; otherwise, nil. +/// @param identifier The identifier of the cluster manager. +/// @return A cluster manager if found; otherwise, nil. - (nullable GMUClusterManager *)clusterManagerWithIdentifier:(NSString *)identifier; -/// Converts clusters managed by the specified ClusterManager to -/// a serializable array of clusters. -/// -/// This method fetches and serializes clusters at the current zoom -/// level from the ClusterManager identified by the given identifier. -/// If the specified ClusterManager identifier does not exist, an empty -/// array is returned. +/// Returns an array of clusters managed by the cluster manager. /// -/// @param identifier The identifier of the ClusterManager to serialize. -/// @return An array of FGMPlatformCluster objects representing the clusters. `nil` is returned only -/// when `error != nil`. +/// @param identifier The identifier of the cluster manager whose clusters are to be retrieved. +/// @return An array of clusters. Returns `nil` only if `error` is populated. - (nullable NSArray *) clustersWithIdentifier:(NSString *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error; -/// Called when cluster marker is tapped on the map. +/// Called when a cluster marker is tapped on the map. /// -/// @param cluster GMUStaticCluster object. +/// @param cluster The cluster that was tapped on. - (void)didTapCluster:(GMUStaticCluster *)cluster; -/// Calls cluster method of all ClusterManagers. +/// Calls the cluster method of all the cluster managers. - (void)invokeClusteringForEachClusterManager; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.m index 7114bc532bf..d9fcb6a9db8 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMClusterManagersController.m @@ -9,14 +9,14 @@ @interface FGMClusterManagersController () -/// A dictionary that cluster managers unique identifiers to GMUClusterManager instances. +/// A dictionary mapping unique cluster manager identifiers to their corresponding cluster managers. @property(strong, nonatomic) NSMutableDictionary *clusterManagerIdentifierToManagers; /// The callback handler interface for calls to Flutter. @property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; -/// The current GMSMapView instance on which the cluster managers are operating. +/// The current map instance on which the cluster managers are operating. @property(strong, nonatomic) GMSMapView *mapView; @end @@ -53,10 +53,9 @@ - (void)addClusterManager:(NSString *)identifier { id renderer = [[GMUDefaultClusterRenderer alloc] initWithMapView:self.mapView clusterIconGenerator:iconGenerator]; - GMUClusterManager *clusterManager = [[GMUClusterManager alloc] initWithMap:self.mapView - algorithm:algorithm - renderer:renderer]; - self.clusterManagerIdentifierToManagers[identifier] = clusterManager; + self.clusterManagerIdentifierToManagers[identifier] = + [[GMUClusterManager alloc] initWithMap:self.mapView algorithm:algorithm renderer:renderer]; + ; } - (void)removeClusterManagersWithIdentifiers:(NSArray *)identifiers { @@ -121,10 +120,9 @@ - (void)didTapCluster:(GMUStaticCluster *)cluster { #pragma mark - Private methods -/// Returns the cluster manager id for given cluster. +/// Returns the cluster manager identifier for given cluster. /// -/// @param cluster identifier of the ClusterManager. -/// @return id NSString if found; otherwise, nil. +/// @return The cluster manager identifier if found; otherwise, nil. - (nullable NSString *)clusterManagerIdentifierForCluster:(GMUStaticCluster *)cluster { if ([cluster.items.firstObject isKindOfClass:[GMSMarker class]]) { GMSMarker *firstMarker = (GMSMarker *)cluster.items.firstObject; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.h index a4d881635ff..310455a0565 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMMarkerUserData.h @@ -18,25 +18,20 @@ NS_ASSUME_NONNULL_BEGIN @end -/// Sets MarkerId and optionally ClusterManagerId to GMSMarker UserData. -/// -/// @param markerIdentifier Identifier of marker. -/// @param clusterManagerIdentifier Optional identifier of cluster manager. -/// @param marker GMSMarker object. +/// Associates a marker identifier and optionally a cluster manager identifier with a marker's user +/// data. extern void FGMSetIdentifiersToMarkerUserData(NSString *markerIdentifier, NSString *_Nullable clusterManagerIdentifier, GMSMarker *marker); -/// Get MarkerIdentifier from GMSMarker UserData. +/// Get the marker identifier from marker's user data. /// -/// @param marker GMSMarker object. -/// @return NSString if found; otherwise, nil. +/// @return The marker identifier if found; otherwise, nil. extern NSString *_Nullable FGMGetMarkerIdentifierFromMarker(GMSMarker *marker); -/// Get ClusterManagerIdentifier from GMSMarker UserData. +/// Get the cluster manager identifier from marker's user data. /// -/// @param marker GMSMarker object. -/// @return NSString if found; otherwise, nil. +/// @return The cluster manager identifier if found; otherwise, nil. extern NSString *_Nullable FGMGetClusterManagerIdentifierFromMarker(GMSMarker *marker); NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h index b21ef16dcf8..cc69bcd39f2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapsPlugin.h @@ -4,6 +4,7 @@ #import #import + #import "FGMClusterManagersController.h" #import "GoogleMapCircleController.h" #import "GoogleMapController.h" diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h index 45f0a68cae2..71068957344 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import -#import #import "FGMClusterManagersController.h" #import "GoogleMapCircleController.h" #import "GoogleMapMarkerController.h" #import "GoogleMapPolygonController.h" #import "GoogleMapPolylineController.h" + +#import +#import + #import "messages.g.h" NS_ASSUME_NONNULL_BEGIN diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h index a46e6fd5e97..81dd5c86f60 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import -#import #import "FGMClusterManagersController.h" #import "GoogleMapController.h" + +#import +#import + #import "messages.g.h" NS_ASSUME_NONNULL_BEGIN diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m index 6c6b6610118..f4e14942735 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "GoogleMapMarkerController.h" + #import "FGMMarkerUserData.h" #import "FLTGoogleMapJSONConversions.h" From 7c2cdf97a76b1c344516fbdfe7aa7c812c272223 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Tue, 6 Aug 2024 22:15:20 +0300 Subject: [PATCH 7/8] [google_maps_flutter_ios] revert obj-c import ordering changes --- .../ios/Classes/GoogleMapController.h | 7 +++---- .../ios/Classes/GoogleMapMarkerController.h | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h index 71068957344..3b785c81df3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h @@ -2,15 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import +#import + #import "FGMClusterManagersController.h" #import "GoogleMapCircleController.h" #import "GoogleMapMarkerController.h" #import "GoogleMapPolygonController.h" #import "GoogleMapPolylineController.h" - -#import -#import - #import "messages.g.h" NS_ASSUME_NONNULL_BEGIN diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h index 81dd5c86f60..95b834c5f71 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h @@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "FGMClusterManagersController.h" -#import "GoogleMapController.h" - #import #import +#import "FGMClusterManagersController.h" +#import "GoogleMapController.h" #import "messages.g.h" NS_ASSUME_NONNULL_BEGIN From bc4eb1baaee443e2e50722cd99cdf6fc7c6c1cfd Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Tue, 6 Aug 2024 22:29:09 +0300 Subject: [PATCH 8/8] [google_maps_flutter_ios] revert unnecessary changes --- .../lib/src/google_maps_flutter_ios.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart index a296a7642de..937e34d72d2 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart @@ -500,7 +500,6 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { Set polylines = const {}, Set circles = const {}, Set tileOverlays = const {}, - Set clusterManagers = const {}, Set>? gestureRecognizers, Map mapOptions = const {}, }) { @@ -515,7 +514,6 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { polygons: polygons, polylines: polylines, circles: circles, - clusterManagers: clusterManagers, tileOverlays: tileOverlays), mapOptions: mapOptions, ); @@ -531,7 +529,6 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { Set polylines = const {}, Set circles = const {}, Set tileOverlays = const {}, - Set clusterManagers = const {}, Set>? gestureRecognizers, Map mapOptions = const {}, }) { @@ -545,7 +542,6 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { polylines: polylines, circles: circles, tileOverlays: tileOverlays, - clusterManagers: clusterManagers, gestureRecognizers: gestureRecognizers, mapOptions: mapOptions, );