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 8d82f086aeb..74da49ab9fb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,5 +1,7 @@ -## NEXT +## 2.15.6 +* Fixes potential flickers of default property values when adding objects to + the map. * Updates minimum supported SDK version to Flutter 3.29/Dart 3.7. ## 2.15.5 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 7bb0fbd3cc4..dd2afe77070 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,11 @@ 2A6906C72D263DF4001F8426 /* GoogleMapsGroundOverlayControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2A6906C62D263DE7001F8426 /* GoogleMapsGroundOverlayControllerTests.m */; }; 2BDE99378062AE3E60B40021 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3ACE0AFE8D82CD5962486AFD /* Pods_RunnerTests.framework */; }; 330909FF2D99B7A60077A751 /* GoogleMapsMarkerControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 330909FE2D99B79B0077A751 /* GoogleMapsMarkerControllerTests.m */; }; + 339355BA2EB3E50300EBF864 /* GoogleMapsCircleControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 339355B92EB3E4F900EBF864 /* GoogleMapsCircleControllerTests.m */; }; + 339355BD2EB3E56300EBF864 /* GoogleMapsTileOverlayControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 339355BC2EB3E55600EBF864 /* GoogleMapsTileOverlayControllerTests.m */; }; + 339355BF2EB535A600EBF864 /* FLTGoogleMapHeatmapControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 339355BE2EB5359B00EBF864 /* FLTGoogleMapHeatmapControllerTests.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */; }; + 478116522BEF8F47002F593E /* GoogleMapsPolylineControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 478116512BEF8F47002F593E /* GoogleMapsPolylineControllerTests.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 */; }; @@ -66,14 +69,19 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 2A6906C62D263DE7001F8426 /* GoogleMapsGroundOverlayControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsGroundOverlayControllerTests.m; sourceTree = ""; }; 330909FE2D99B79B0077A751 /* GoogleMapsMarkerControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsMarkerControllerTests.m; sourceTree = ""; }; + 339355B82EB3E4D500EBF864 /* GoogleMapsPolygonControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsPolygonControllerTests.m; sourceTree = ""; }; + 339355B92EB3E4F900EBF864 /* GoogleMapsCircleControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsCircleControllerTests.m; sourceTree = ""; }; + 339355BC2EB3E55600EBF864 /* GoogleMapsTileOverlayControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsTileOverlayControllerTests.m; sourceTree = ""; }; + 339355BE2EB5359B00EBF864 /* FLTGoogleMapHeatmapControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapHeatmapControllerTests.m; sourceTree = ""; }; 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 = ""; }; + 478116512BEF8F47002F593E /* GoogleMapsPolylineControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GoogleMapsPolylineControllerTests.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; }; 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLTGoogleMapJSONConversionsConversionTests.m; sourceTree = ""; }; 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 = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -139,6 +147,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, @@ -211,12 +220,16 @@ F269303A2BB389BF00BF17C4 /* assets */, 528F16862C62952700148160 /* ExtractIconFromDataTests.m */, 528F16822C62941000148160 /* FGMClusterManagersControllerTests.m */, + 339355BE2EB5359B00EBF864 /* FLTGoogleMapHeatmapControllerTests.m */, 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */, 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */, F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, - 330909FE2D99B79B0077A751 /* GoogleMapsMarkerControllerTests.m */, - 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */, + 339355B92EB3E4F900EBF864 /* GoogleMapsCircleControllerTests.m */, 2A6906C62D263DE7001F8426 /* GoogleMapsGroundOverlayControllerTests.m */, + 330909FE2D99B79B0077A751 /* GoogleMapsMarkerControllerTests.m */, + 339355B82EB3E4D500EBF864 /* GoogleMapsPolygonControllerTests.m */, + 478116512BEF8F47002F593E /* GoogleMapsPolylineControllerTests.m */, + 339355BC2EB3E55600EBF864 /* GoogleMapsTileOverlayControllerTests.m */, 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */, 982F2A6B27BADE17003C81F4 /* PartiallyMockedMapView.m */, F7151F14265D7ED70028CB91 /* Info.plist */, @@ -248,7 +261,6 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */, - 9C5FE6CAF02237D44998DDC0 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -426,24 +438,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - 9C5FE6CAF02237D44998DDC0 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; BB6BD9A1101E970BEF85B6D2 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -522,11 +516,14 @@ buildActionMask = 2147483647; files = ( 528F16832C62941000148160 /* FGMClusterManagersControllerTests.m in Sources */, + 339355BA2EB3E50300EBF864 /* GoogleMapsCircleControllerTests.m in Sources */, + 339355BF2EB535A600EBF864 /* FLTGoogleMapHeatmapControllerTests.m in Sources */, + 339355BD2EB3E56300EBF864 /* GoogleMapsTileOverlayControllerTests.m in Sources */, F7151F13265D7ED70028CB91 /* GoogleMapsTests.m in Sources */, 6851F3562835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m in Sources */, 982F2A6C27BADE17003C81F4 /* PartiallyMockedMapView.m in Sources */, 330909FF2D99B7A60077A751 /* GoogleMapsMarkerControllerTests.m in Sources */, - 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */, + 478116522BEF8F47002F593E /* GoogleMapsPolylineControllerTests.m in Sources */, 2A6906C72D263DF4001F8426 /* GoogleMapsGroundOverlayControllerTests.m in Sources */, 0DD7B6C32B744EEF00E857FD /* FLTTileProviderControllerTests.m in Sources */, 528F16872C62952700148160 /* ExtractIconFromDataTests.m in Sources */, diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapHeatmapControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapHeatmapControllerTests.m new file mode 100644 index 00000000000..2f345e02c57 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapHeatmapControllerTests.m @@ -0,0 +1,106 @@ +// Copyright 2013 The Flutter Authors +// 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 GoogleMapsUtils; + +#import "PartiallyMockedMapView.h" + +@interface PropertyOrderValidatingHeatmap : GMUHeatmapTileLayer { +} +@property(nonatomic) BOOL hasSetMap; +@end + +@interface GoogleMapsHeatmapControllerTests : XCTestCase +@end + +@implementation GoogleMapsHeatmapControllerTests + +- (void)testUpdateHeatmapSetsVisibilityLast { + PropertyOrderValidatingHeatmap *heatmap = [[PropertyOrderValidatingHeatmap alloc] init]; + [FLTGoogleMapHeatmapController + updateHeatmap:heatmap + fromOptions:@{ + @"data" : @[ @[ @[ @(5), @(5) ], @(0.5) ], @[ @[ @(10), @(10) ], @(0.75) ] ], + @"gradient" : @{ + @"colors" : @[ @(0), @(1) ], + @"startPoints" : @[ @(0), @(1) ], + @"colorMapSize" : @(256), + }, + @"opacity" : @(0.5), + @"radius" : @(1), + @"minimumZoomIntensity" : @(1), + @"maximumZoomIntensity" : @(2), + } + withMapView:[GoogleMapsHeatmapControllerTests mapView]]; + XCTAssertTrue(heatmap.hasSetMap); +} + +/// Returns a simple map view to add map objects to. ++ (GMSMapView *)mapView { + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = CGRectMake(0, 0, 100, 100); + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + return [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; +} + +@end + +@implementation PropertyOrderValidatingHeatmap + +- (void)setWeightedData:(NSArray *)weightedData { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.weightedData = weightedData; +} + +- (void)setRadius:(NSUInteger)radius { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.radius = radius; +} + +- (void)setGradient:(GMUGradient *)gradient { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.gradient = gradient; +} + +- (void)setMinimumZoomIntensity:(NSUInteger)minimumZoomIntensity { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.minimumZoomIntensity = minimumZoomIntensity; +} + +- (void)setMaximumZoomIntensity:(NSUInteger)maximumZoomIntensity { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.maximumZoomIntensity = maximumZoomIntensity; +} + +- (void)setZIndex:(int)zIndex { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.zIndex = zIndex; +} + +- (void)setTileSize:(NSInteger)tileSize { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tileSize = tileSize; +} + +- (void)setOpacity:(float)opacity { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.opacity = opacity; +} + +- (void)setFadeIn:(BOOL)fadeIn { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.fadeIn = fadeIn; +} + +- (void)setMap:(GMSMapView *)map { + // Don't actually set the map, since that requires more test setup. + if (map) { + self.hasSetMap = YES; + } +} +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsCircleControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsCircleControllerTests.m new file mode 100644 index 00000000000..44785692f28 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsCircleControllerTests.m @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors +// 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 "PartiallyMockedMapView.h" + +/// A GMSCircle that ensures that property updates are made before the map is set. +@interface PropertyOrderValidatingCircle : GMSCircle { +} +@property(nonatomic) BOOL hasSetMap; +@end + +@interface GoogleMapsCircleControllerTests : XCTestCase +@end + +@implementation GoogleMapsCircleControllerTests + +- (void)testUpdateCircleSetsVisibilityLast { + PropertyOrderValidatingCircle *circle = [[PropertyOrderValidatingCircle alloc] init]; + [FLTGoogleMapCircleController + updateCircle:circle + fromPlatformCircle:[FGMPlatformCircle + makeWithConsumeTapEvents:NO + fillColor:0 + strokeColor:0 + visible:YES + strokeWidth:0 + zIndex:0 + center:[FGMPlatformLatLng makeWithLatitude:0 + longitude:0] + radius:10 + circleId:@"circle"] + withMapView:[GoogleMapsCircleControllerTests mapView]]; + XCTAssertTrue(circle.hasSetMap); +} + +/// Returns a simple map view to add map objects to. ++ (GMSMapView *)mapView { + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = CGRectMake(0, 0, 100, 100); + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + return [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; +} + +@end + +@implementation PropertyOrderValidatingCircle +- (void)setPosition:(CLLocationCoordinate2D)position { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.position = position; +} + +- (void)setRadius:(CLLocationDistance)radius { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.radius = radius; +} + +- (void)setStrokeWidth:(CGFloat)strokeWidth { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.strokeWidth = strokeWidth; +} + +- (void)setStrokeColor:(UIColor *)strokeColor { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.strokeColor = strokeColor; +} + +- (void)setFillColor:(UIColor *)fillColor { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.fillColor = fillColor; +} + +- (void)setTitle:(NSString *)title { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.title = title; +} + +- (void)setTappable:(BOOL)tappable { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tappable = tappable; +} + +- (void)setZIndex:(int)zIndex { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.zIndex = zIndex; +} + +- (void)setMap:(GMSMapView *)map { + // Don't actually set the map, since that requires more test setup. + if (map) { + self.hasSetMap = YES; + } +} +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsGroundOverlayControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsGroundOverlayControllerTests.m index df8b39bb03a..03ec813c644 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsGroundOverlayControllerTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsGroundOverlayControllerTests.m @@ -12,6 +12,12 @@ #import #import "PartiallyMockedMapView.h" +/// A GMSGroundOverlay that ensures that property updates are made before the map is set. +@interface PropertyOrderValidatingGroundOverlay : GMSGroundOverlay { +} +@property(nonatomic) BOOL hasSetMap; +@end + @interface GoogleMapsGroundOverlayControllerTests : XCTestCase @end @@ -31,13 +37,7 @@ + (FGMGroundOverlayController *)groundOverlayControllerWithPositionWithMockedMap icon:wideGamutImage zoomLevel:14.0]; - GMSCameraPosition *camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; - CGRect frame = CGRectMake(0, 0, 100, 100); - GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; - mapViewOptions.frame = frame; - mapViewOptions.camera = camera; - - PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + GMSMapView *mapView = [GoogleMapsGroundOverlayControllerTests mapView]; return [[FGMGroundOverlayController alloc] initWithGroundOverlay:groundOverlay identifier:@"id_1" @@ -60,13 +60,7 @@ + (FGMGroundOverlayController *)groundOverlayControllerWithBoundsWithMockedMap { coordinate:CLLocationCoordinate2DMake(30, 40)] icon:wideGamutImage]; - GMSCameraPosition *camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; - CGRect frame = CGRectMake(0, 0, 100, 100); - GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; - mapViewOptions.frame = frame; - mapViewOptions.camera = camera; - - PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + GMSMapView *mapView = [GoogleMapsGroundOverlayControllerTests mapView]; return [[FGMGroundOverlayController alloc] initWithGroundOverlay:groundOverlay identifier:@"id_1" @@ -191,4 +185,100 @@ - (void)testUpdatingGroundOverlayWithBounds { XCTAssertEqual(convertedPlatformGroundOverlay.zIndex, platformGroundOverlay.zIndex); } +- (void)testUpdateGroundOverlaySetsVisibilityLast { + PropertyOrderValidatingGroundOverlay *groundOverlay = + [[PropertyOrderValidatingGroundOverlay alloc] init]; + [FGMGroundOverlayController + updateGroundOverlay:groundOverlay + fromPlatformGroundOverlay: + [FGMPlatformGroundOverlay + makeWithGroundOverlayId:@"groundOverlay" + image:[FGMPlatformBitmap + makeWithBitmap:[FGMPlatformBitmapDefaultMarker + makeWithHue:@0]] + position:[FGMPlatformLatLng makeWithLatitude:0 longitude:0] + bounds:[FGMPlatformLatLngBounds + makeWithNortheast:[FGMPlatformLatLng + makeWithLatitude:54.4816 + longitude:5.1791] + southwest:[FGMPlatformLatLng + makeWithLatitude:52.4816 + longitude:3.1791]] + anchor:[FGMPlatformPoint makeWithX:0.5 y:0.5] + transparency:0.5 + bearing:65.0 + zIndex:2.0 + visible:YES + clickable:YES + zoomLevel:nil] + withMapView:[GoogleMapsGroundOverlayControllerTests mapView] + registrar:nil + screenScale:1.0 + usingBounds:YES]; + XCTAssertTrue(groundOverlay.hasSetMap); +} + +/// Returns a simple map view to add map objects to. ++ (GMSMapView *)mapView { + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = CGRectMake(0, 0, 100, 100); + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + return [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; +} + +@end + +@implementation PropertyOrderValidatingGroundOverlay + +- (void)setPosition:(CLLocationCoordinate2D)position { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.position = position; +} + +- (void)setAnchor:(CGPoint)anchor { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.anchor = anchor; +} + +- (void)setIcon:(UIImage *)icon { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.icon = icon; +} + +- (void)setOpacity:(float)opacity { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.opacity = opacity; +} + +- (void)setBearing:(CLLocationDirection)bearing { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.bearing = bearing; +} + +- (void)setBounds:(GMSCoordinateBounds *)bounds { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.bounds = bounds; +} + +- (void)setTitle:(NSString *)title { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.title = title; +} + +- (void)setTappable:(BOOL)tappable { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tappable = tappable; +} + +- (void)setZIndex:(int)zIndex { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.zIndex = zIndex; +} + +- (void)setMap:(GMSMapView *)map { + // Don't actually set the map, since that requires more test setup. + if (map) { + self.hasSetMap = YES; + } +} @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsMarkerControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsMarkerControllerTests.m index 67619f0c987..55272563162 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsMarkerControllerTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsMarkerControllerTests.m @@ -9,15 +9,22 @@ #import #import + #import "PartiallyMockedMapView.h" +/// A GMSMarker that ensures that property updates are made before the map is set. +@interface PropertyOrderValidatingMarker : GMSMarker { +} +@property(nonatomic) BOOL hasSetMap; +@end + @interface GoogleMapsMarkerControllerTests : XCTestCase @end @implementation GoogleMapsMarkerControllerTests -/// Returns a mocked map view for use with marker controllers. -- (GMSMapView *)mockedMapView { +/// Returns a simple map view for use with marker controllers. ++ (GMSMapView *)mapView { GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; mapViewOptions.frame = CGRectMake(0, 0, 100, 100); mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; @@ -41,7 +48,7 @@ - (FGMPlatformBitmap *)placeholderBitmap { } - (void)testSetsMarkerNumericProperties { - GMSMapView *mapView = [self mockedMapView]; + GMSMapView *mapView = [GoogleMapsMarkerControllerTests mapView]; FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; NSString *markerIdentifier = @"marker"; @@ -88,7 +95,7 @@ - (void)testSetsMarkerNumericProperties { // Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from // another property. - (void)testSetsDraggable { - GMSMapView *mapView = [self mockedMapView]; + GMSMapView *mapView = [GoogleMapsMarkerControllerTests mapView]; FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; NSString *markerIdentifier = @"marker"; @@ -120,7 +127,7 @@ - (void)testSetsDraggable { // Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from // another property. - (void)testSetsFlat { - GMSMapView *mapView = [self mockedMapView]; + GMSMapView *mapView = [GoogleMapsMarkerControllerTests mapView]; FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; NSString *markerIdentifier = @"marker"; @@ -152,7 +159,7 @@ - (void)testSetsFlat { // Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from // another property. - (void)testSetsVisible { - GMSMapView *mapView = [self mockedMapView]; + GMSMapView *mapView = [GoogleMapsMarkerControllerTests mapView]; FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; NSString *markerIdentifier = @"marker"; @@ -183,7 +190,7 @@ - (void)testSetsVisible { } - (void)testSetsMarkerInfoWindowProperties { - GMSMapView *mapView = [self mockedMapView]; + GMSMapView *mapView = [GoogleMapsMarkerControllerTests mapView]; FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; NSString *markerIdentifier = @"marker"; @@ -222,4 +229,135 @@ - (void)testSetsMarkerInfoWindowProperties { XCTAssertEqual(marker.snippet, snippet); } +- (void)testUpdateMarkerSetsVisibilityLast { + PropertyOrderValidatingMarker *marker = [[PropertyOrderValidatingMarker alloc] init]; + [FLTGoogleMapMarkerController + updateMarker:marker + fromPlatformMarker:[FGMPlatformMarker + makeWithAlpha:1.0 + anchor:[FGMPlatformPoint makeWithX:0 y:0] + consumeTapEvents:YES + draggable:YES + flat:YES + icon:[self placeholderBitmap] + infoWindow:[FGMPlatformInfoWindow + makeWithTitle:@"info title" + snippet:@"info snippet" + anchor:[FGMPlatformPoint + makeWithX:0 + y:0]] + position:[FGMPlatformLatLng makeWithLatitude:0 + longitude:0] + rotation:0 + visible:YES + zIndex:0 + markerId:@"marker" + clusterManagerId:nil] + withMapView:[GoogleMapsMarkerControllerTests mapView] + registrar:nil + screenScale:1 + usingOpacityForVisibility:NO]; + XCTAssertTrue(marker.hasSetMap); +} + +@end + +@implementation PropertyOrderValidatingMarker + +- (void)setPosition:(CLLocationCoordinate2D)position { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.position = position; +} + +- (void)setSnippet:(NSString *)snippet { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.snippet = snippet; +} + +- (void)setIcon:(UIImage *)icon { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.icon = icon; +} + +- (void)setIconView:(UIView *)iconView { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.iconView = iconView; +} + +- (void)setTracksViewChanges:(BOOL)tracksViewChanges { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tracksViewChanges = tracksViewChanges; +} + +- (void)setTracksInfoWindowChanges:(BOOL)tracksInfoWindowChanges { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tracksInfoWindowChanges = tracksInfoWindowChanges; +} + +- (void)setGroundAnchor:(CGPoint)groundAnchor { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.groundAnchor = groundAnchor; +} + +- (void)setInfoWindowAnchor:(CGPoint)infoWindowAnchor { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.infoWindowAnchor = infoWindowAnchor; +} + +- (void)setAppearAnimation:(GMSMarkerAnimation)appearAnimation { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.appearAnimation = appearAnimation; +} + +- (void)setDraggable:(BOOL)draggable { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.draggable = draggable; +} + +- (void)setFlat:(BOOL)flat { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.flat = flat; +} + +- (void)setRotation:(CLLocationDegrees)rotation { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.rotation = rotation; +} + +- (void)setOpacity:(float)opacity { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.opacity = opacity; +} + +- (void)setPanoramaView:(GMSPanoramaView *)panoramaView { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.panoramaView = panoramaView; +} + +- (void)setTitle:(NSString *)title { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.title = title; +} + +- (void)setTappable:(BOOL)tappable { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tappable = tappable; +} + +- (void)setZIndex:(int)zIndex { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.zIndex = zIndex; +} + +- (void)setUserData:(id)userData { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.userData = userData; +} + +- (void)setMap:(GMSMapView *)map { + // Don't actually set the map, since that requires more test setup. + if (map) { + self.hasSetMap = YES; + } +} @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolygonControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolygonControllerTests.m new file mode 100644 index 00000000000..778b1d44112 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolygonControllerTests.m @@ -0,0 +1,106 @@ +// Copyright 2013 The Flutter Authors +// 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; + +/// A GMSPolygon that ensures that property updates are made before the map is set. +@interface PropertyOrderValidatingPolygon : GMSPolygon { +} +@property(nonatomic) BOOL hasSetMap; +@end + +@interface GoogleMapsPolygonControllerTests : XCTestCase +@end + +@implementation GoogleMapsPolygonControllerTests + +- (void)testUpdatePolygonSetsVisibilityLast { + PropertyOrderValidatingPolygon *polygon = [[PropertyOrderValidatingPolygon alloc] init]; + [FLTGoogleMapPolygonController + updatePolygon:polygon + fromPlatformPolygon:[FGMPlatformPolygon makeWithConsumeTapEvents:NO + fillColor:0 + geodesic:NO + holes:@[] + strokeColor:0 + strokeWidth:0 + visible:YES + zIndex:0 + points:@[] + polygonId:@"polygon"] + withMapView:[GoogleMapsPolygonControllerTests mapView]]; + XCTAssertTrue(polygon.hasSetMap); +} + +/// Returns a simple map view to add map objects to. ++ (GMSMapView *)mapView { + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = CGRectMake(0, 0, 100, 100); + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + return [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; +} + +@end + +@implementation PropertyOrderValidatingPolygon +- (void)setPath:(GMSPath *)path { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.path = path; +} + +- (void)setHoles:(NSArray *)holes { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.holes = holes; +} + +- (void)setStrokeWidth:(CGFloat)strokeWidth { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.strokeWidth = strokeWidth; +} + +- (void)setStrokeColor:(UIColor *)strokeColor { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.strokeColor = strokeColor; +} + +- (void)setFillColor:(UIColor *)fillColor { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.fillColor = fillColor; +} + +- (void)setGeodesic:(BOOL)geodesic { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.geodesic = geodesic; +} + +- (void)setTitle:(NSString *)title { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.title = title; +} + +- (void)setTappable:(BOOL)tappable { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tappable = tappable; +} + +- (void)setZIndex:(int)zIndex { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.zIndex = zIndex; +} + +- (void)setUserData:(id)userData { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.userData = userData; +} + +- (void)setMap:(GMSMapView *)map { + // Don't actually set the map, since that requires more test setup. + if (map) { + self.hasSetMap = YES; + } +} +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylineControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylineControllerTests.m new file mode 100644 index 00000000000..6dbe1b93a0e --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylineControllerTests.m @@ -0,0 +1,178 @@ +// Copyright 2013 The Flutter Authors +// 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 +#import "PartiallyMockedMapView.h" + +/// A GMSPolyline that ensures that property updates are made before the map is set. +@interface PropertyOrderValidatingPolyline : GMSPolyline { +} +@property(nonatomic) BOOL hasSetMap; +@end + +@interface GoogleMapsPolylineControllerTests : XCTestCase +@end + +@implementation GoogleMapsPolylineControllerTests + +/// Returns GoogleMapPolylineController object instantiated with a mocked map instance +/// +/// @return An object of FLTGoogleMapPolylineController +- (FLTGoogleMapPolylineController *)polylineControllerWithMockedMap { + FGMPlatformPolyline *polyline = + [FGMPlatformPolyline makeWithPolylineId:@"polyline_id_0" + consumesTapEvents:NO + color:0 + geodesic:NO + jointType:FGMPlatformJointTypeRound + patterns:@[] + points:[GoogleMapsPolylineControllerTests polylinePoints] + visible:NO + width:1 + zIndex:0]; + + CGRect frame = CGRectMake(0, 0, 100, 100); + GMSCameraPosition *camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = frame; + mapViewOptions.camera = camera; + + PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; + + GMSMutablePath *path = FGMGetPathFromPoints(FGMGetPointsForPigeonLatLngs(polyline.points)); + + FLTGoogleMapPolylineController *polylineControllerWithMockedMap = + [[FLTGoogleMapPolylineController alloc] initWithPath:path + identifier:polyline.polylineId + mapView:mapView]; + + return polylineControllerWithMockedMap; +} + +- (void)testPatternsSetSpans { + FLTGoogleMapPolylineController *polylineController = [self polylineControllerWithMockedMap]; + + XCTAssertNil(polylineController.polyline.spans); + + [polylineController + updateFromPlatformPolyline:[FGMPlatformPolyline + makeWithPolylineId:@"polyline_id_0" + consumesTapEvents:NO + color:0 + geodesic:NO + jointType:FGMPlatformJointTypeRound + patterns:@[ + [FGMPlatformPatternItem + makeWithType:FGMPlatformPatternItemTypeDot + length:@(10)], + [FGMPlatformPatternItem + makeWithType:FGMPlatformPatternItemTypeDash + length:@(10)] + ] + points:[GoogleMapsPolylineControllerTests + polylinePoints] + visible:YES + width:1 + zIndex:0]]; + + // `GMSStyleSpan` doesn't implement `isEqual` so cannot be compared by value at present. + XCTAssertNotNil(polylineController.polyline.spans); +} + +- (void)testUpdatePolylineSetsVisibilityLast { + PropertyOrderValidatingPolyline *polyline = [[PropertyOrderValidatingPolyline alloc] init]; + [FLTGoogleMapPolylineController + updatePolyline:polyline + fromPlatformPolyline:[FGMPlatformPolyline + makeWithPolylineId:@"polyline" + consumesTapEvents:NO + color:0 + geodesic:NO + jointType:FGMPlatformJointTypeRound + patterns:@[] + points:[GoogleMapsPolylineControllerTests polylinePoints] + visible:YES + width:1 + zIndex:0] + withMapView:[GoogleMapsPolylineControllerTests mapView]]; + XCTAssertTrue(polyline.hasSetMap); +} + +/// Returns a simple map view to add map objects to. ++ (GMSMapView *)mapView { + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = CGRectMake(0, 0, 100, 100); + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + return [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; +} + +/// Returns a set of points to use for tests that need a valid but arbitrary line. ++ (NSArray *)polylinePoints { + return @[ + [FGMPlatformLatLng makeWithLatitude:52.4816 longitude:-3.1791], + [FGMPlatformLatLng makeWithLatitude:54.043 longitude:-2.9925], + [FGMPlatformLatLng makeWithLatitude:54.1396 longitude:-4.2739], + [FGMPlatformLatLng makeWithLatitude:53.4153 longitude:-4.0829], + ]; +} + +@end + +@implementation PropertyOrderValidatingPolyline + +- (void)setPath:(GMSPath *)path { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.path = path; +} + +- (void)setStrokeWidth:(CGFloat)strokeWidth { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.strokeWidth = strokeWidth; +} + +- (void)setStrokeColor:(UIColor *)strokeColor { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.strokeColor = strokeColor; +} + +- (void)setGeodesic:(BOOL)geodesic { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.geodesic = geodesic; +} + +- (void)setTitle:(NSString *)title { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.title = title; +} + +- (void)setTappable:(BOOL)tappable { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tappable = tappable; +} + +- (void)setZIndex:(int)zIndex { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.zIndex = zIndex; +} + +- (void)setUserData:(id)userData { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.userData = userData; +} + +- (void)setMap:(GMSMapView *)map { + // Don't actually set the map, since that requires more test setup. + if (map) { + self.hasSetMap = YES; + } +} +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m deleted file mode 100644 index 7537a5e3c20..00000000000 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2013 The Flutter Authors -// 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 -#import "PartiallyMockedMapView.h" - -@interface GoogleMapsPolylinesControllerTests : XCTestCase -@end - -@implementation GoogleMapsPolylinesControllerTests - -/// Returns GoogleMapPolylineController object instantiated with a mocked map instance -/// -/// @return An object of FLTGoogleMapPolylineController -- (FLTGoogleMapPolylineController *)polylineControllerWithMockedMap { - FGMPlatformPolyline *polyline = [FGMPlatformPolyline - makeWithPolylineId:@"polyline_id_0" - consumesTapEvents:NO - color:0 - geodesic:NO - jointType:FGMPlatformJointTypeRound - patterns:@[] - points:@[ - [FGMPlatformLatLng makeWithLatitude:52.4816 longitude:-3.1791], - [FGMPlatformLatLng makeWithLatitude:54.043 longitude:-2.9925], - [FGMPlatformLatLng makeWithLatitude:54.1396 longitude:-4.2739], - [FGMPlatformLatLng makeWithLatitude:53.4153 longitude:-4.0829], - ] - visible:NO - width:1 - zIndex:0]; - - CGRect frame = CGRectMake(0, 0, 100, 100); - GMSCameraPosition *camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; - - GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; - mapViewOptions.frame = frame; - mapViewOptions.camera = camera; - - PartiallyMockedMapView *mapView = [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; - - GMSMutablePath *path = FGMGetPathFromPoints(FGMGetPointsForPigeonLatLngs(polyline.points)); - - FLTGoogleMapPolylineController *polylineControllerWithMockedMap = - [[FLTGoogleMapPolylineController alloc] initWithPath:path - identifier:polyline.polylineId - mapView:mapView]; - - return polylineControllerWithMockedMap; -} - -- (void)testSetPatterns { - NSArray *styles = @[ - [GMSStrokeStyle solidColor:UIColor.clearColor], [GMSStrokeStyle solidColor:UIColor.redColor] - ]; - - NSArray *lengths = @[ @10, @10 ]; - - FLTGoogleMapPolylineController *polylineController = [self polylineControllerWithMockedMap]; - - XCTAssertNil(polylineController.polyline.spans); - - [polylineController setPattern:styles lengths:lengths]; - - // `GMSStyleSpan` doesn't implement `isEqual` so cannot be compared by value at present. - XCTAssertNotNil(polylineController.polyline.spans); -} - -@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsTileOverlayControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsTileOverlayControllerTests.m new file mode 100644 index 00000000000..a727386bfd2 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsTileOverlayControllerTests.m @@ -0,0 +1,74 @@ +// Copyright 2013 The Flutter Authors +// 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 "PartiallyMockedMapView.h" + +/// A GMSTileOverlay that ensures that property updates are made before the map is set. +@interface PropertyOrderValidatingTileLayer : GMSTileLayer +@property(nonatomic) BOOL hasSetMap; +@end + +@interface GoogleMapsTileOverlayControllerTests : XCTestCase +@end + +@implementation GoogleMapsTileOverlayControllerTests + +- (void)testUpdateTileOverlaySetsVisibilityLast { + PropertyOrderValidatingTileLayer *tileLayer = [[PropertyOrderValidatingTileLayer alloc] init]; + [FLTGoogleMapTileOverlayController + updateTileLayer:tileLayer + fromPlatformTileOverlay:[FGMPlatformTileOverlay makeWithTileOverlayId:@"overlay" + fadeIn:NO + transparency:0.5 + zIndex:0 + visible:YES + tileSize:1] + withMapView:[GoogleMapsTileOverlayControllerTests mapView]]; + XCTAssertTrue(tileLayer.hasSetMap); +} + +/// Returns a simple map view to add map objects to. ++ (GMSMapView *)mapView { + GMSMapViewOptions *mapViewOptions = [[GMSMapViewOptions alloc] init]; + mapViewOptions.frame = CGRectMake(0, 0, 100, 100); + mapViewOptions.camera = [[GMSCameraPosition alloc] initWithLatitude:0 longitude:0 zoom:0]; + return [[PartiallyMockedMapView alloc] initWithOptions:mapViewOptions]; +} + +@end + +@implementation PropertyOrderValidatingTileLayer + +- (void)setZIndex:(int)zIndex { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.zIndex = zIndex; +} + +- (void)setTileSize:(NSInteger)tileSize { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.tileSize = tileSize; +} + +- (void)setOpacity:(float)opacity { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.opacity = opacity; +} + +- (void)setFadeIn:(BOOL)fadeIn { + XCTAssertFalse(self.hasSetMap, @"Property set after map was set."); + super.fadeIn = fadeIn; +} + +- (void)setMap:(GMSMapView *)map { + // Don't actually set the map, since that requires more test setup. + if (map) { + self.hasSetMap = YES; + } +} +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMGroundOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMGroundOverlayController.m index 29e6f1e1ce4..2260dfb9e71 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMGroundOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMGroundOverlayController.m @@ -3,6 +3,7 @@ // found in the LICENSE file. #import "FGMGroundOverlayController.h" +#import "FGMGroundOverlayController_Test.h" #import "FGMImageUtils.h" #import "FLTGoogleMapJSONConversions.h" @@ -34,67 +35,46 @@ - (void)removeGroundOverlay { self.groundOverlay.map = nil; } -- (void)setConsumeTapEvents:(BOOL)consumes { - self.groundOverlay.tappable = consumes; -} - -- (void)setVisible:(BOOL)visible { - self.groundOverlay.map = visible ? self.mapView : nil; -} - -- (void)setZIndex:(int)zIndex { - self.groundOverlay.zIndex = zIndex; -} - -- (void)setAnchor:(CGPoint)anchor { - self.groundOverlay.anchor = anchor; -} - -- (void)setBearing:(CLLocationDirection)bearing { - self.groundOverlay.bearing = bearing; -} - -- (void)setTransparency:(float)transparency { - float opacity = 1.0 - transparency; - self.groundOverlay.opacity = opacity; -} - -- (void)setPositionFromBounds:(GMSCoordinateBounds *)bounds { - self.groundOverlay.bounds = bounds; -} - -- (void)setPositionFromCoordinates:(CLLocationCoordinate2D)coordinates { - self.groundOverlay.position = coordinates; -} - -- (void)setIcon:(UIImage *)icon { - self.groundOverlay.icon = icon; -} - - (void)updateFromPlatformGroundOverlay:(FGMPlatformGroundOverlay *)groundOverlay registrar:(NSObject *)registrar screenScale:(CGFloat)screenScale { - [self setConsumeTapEvents:groundOverlay.clickable]; - [self setZIndex:(int)groundOverlay.zIndex]; - [self setAnchor:CGPointMake(groundOverlay.anchor.x, groundOverlay.anchor.y)]; - UIImage *image = FGMIconFromBitmap(groundOverlay.image, registrar, screenScale); - [self setIcon:image]; - [self setBearing:groundOverlay.bearing]; - [self setTransparency:groundOverlay.transparency]; - if ([self isCreatedWithBounds]) { - [self setPositionFromBounds:[[GMSCoordinateBounds alloc] - initWithCoordinate:CLLocationCoordinate2DMake( - groundOverlay.bounds.northeast.latitude, - groundOverlay.bounds.northeast.longitude) - coordinate:CLLocationCoordinate2DMake( - groundOverlay.bounds.southwest.latitude, - groundOverlay.bounds.southwest - .longitude)]]; + [FGMGroundOverlayController updateGroundOverlay:self.groundOverlay + fromPlatformGroundOverlay:groundOverlay + withMapView:self.mapView + registrar:registrar + screenScale:screenScale + usingBounds:self.createdWithBounds]; +} + ++ (void)updateGroundOverlay:(GMSGroundOverlay *)groundOverlay + fromPlatformGroundOverlay:(FGMPlatformGroundOverlay *)platformGroundOverlay + withMapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar + screenScale:(CGFloat)screenScale + usingBounds:(BOOL)useBounds { + groundOverlay.tappable = platformGroundOverlay.clickable; + groundOverlay.zIndex = (int)platformGroundOverlay.zIndex; + groundOverlay.anchor = + CGPointMake(platformGroundOverlay.anchor.x, platformGroundOverlay.anchor.y); + UIImage *image = FGMIconFromBitmap(platformGroundOverlay.image, registrar, screenScale); + groundOverlay.icon = image; + groundOverlay.bearing = platformGroundOverlay.bearing; + groundOverlay.opacity = 1.0 - platformGroundOverlay.transparency; + if (useBounds) { + groundOverlay.bounds = [[GMSCoordinateBounds alloc] + initWithCoordinate:CLLocationCoordinate2DMake( + platformGroundOverlay.bounds.northeast.latitude, + platformGroundOverlay.bounds.northeast.longitude) + coordinate:CLLocationCoordinate2DMake( + platformGroundOverlay.bounds.southwest.latitude, + platformGroundOverlay.bounds.southwest.longitude)]; } else { - [self setPositionFromCoordinates:CLLocationCoordinate2DMake(groundOverlay.position.latitude, - groundOverlay.position.longitude)]; + groundOverlay.position = CLLocationCoordinate2DMake(platformGroundOverlay.position.latitude, + platformGroundOverlay.position.longitude); } - [self setVisible:groundOverlay.visible]; + + // This must be done last, to avoid visual flickers of default property values. + groundOverlay.map = platformGroundOverlay.visible ? mapView : nil; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMGroundOverlayController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMGroundOverlayController_Test.h index 9d05daa99e7..87123539947 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMGroundOverlayController_Test.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FGMGroundOverlayController_Test.h @@ -15,4 +15,15 @@ registrar:(NSObject *)registrar screenScale:(CGFloat)screenScale; +/// Updates the underlying GMSGroundOverlay with the properties from the given +/// FGMPlatformGroundOverlay. +/// +/// Setting the ground overlay to visible will set its map to the given mapView. ++ (void)updateGroundOverlay:(GMSGroundOverlay *)groundOverlay + fromPlatformGroundOverlay:(FGMPlatformGroundOverlay *)groundOverlay + withMapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar + screenScale:(CGFloat)screenScale + usingBounds:(BOOL)useBounds; + @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.m index e1c3bd40023..aa51fb93c63 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController.m @@ -3,9 +3,12 @@ // found in the LICENSE file. #import "FLTGoogleMapHeatmapController.h" -#import "FLTGoogleMapJSONConversions.h" +#import "FLTGoogleMapHeatmapController_Test.h" + @import GoogleMapsUtils; +#import "FLTGoogleMapJSONConversions.h" + @interface FLTGoogleMapHeatmapController () /// The heatmap tile layer this controller handles. @@ -25,9 +28,9 @@ - (instancetype)initWithHeatmapTileLayer:(GMUHeatmapTileLayer *)heatmapTileLayer _heatmapTileLayer = heatmapTileLayer; _mapView = mapView; - [FLTGoogleMapHeatmapController interpretHeatmapOptions:_heatmapTileLayer - mapView:_mapView - options:options]; + [FLTGoogleMapHeatmapController updateHeatmap:_heatmapTileLayer + fromOptions:options + withMapView:_mapView]; } return self; } @@ -41,14 +44,16 @@ - (void)clearTileCache { } - (void)interpretHeatmapOptions:(NSDictionary *)data { - [FLTGoogleMapHeatmapController interpretHeatmapOptions:_heatmapTileLayer - mapView:_mapView - options:data]; + [FLTGoogleMapHeatmapController updateHeatmap:_heatmapTileLayer + fromOptions:data + withMapView:_mapView]; } -+ (void)interpretHeatmapOptions:(GMUHeatmapTileLayer *)heatmapTileLayer - mapView:(GMSMapView *)mapView - options:(NSDictionary *)options { ++ (void)updateHeatmap:(GMUHeatmapTileLayer *)heatmapTileLayer + fromOptions:(NSDictionary *)options + withMapView:(GMSMapView *)mapView { + // TODO(stuartmorgan): Migrate this to Pigeon. See + // https://github.com/flutter/flutter/issues/117907 id weightedData = options[kHeatmapDataKey]; if ([weightedData isKindOfClass:[NSArray class]]) { heatmapTileLayer.weightedData = @@ -81,6 +86,7 @@ + (void)interpretHeatmapOptions:(GMUHeatmapTileLayer *)heatmapTileLayer } // The map must be set each time for options to update. + // This must be done last, to avoid visual flickers of default property values. heatmapTileLayer.map = mapView; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController_Test.h new file mode 100644 index 00000000000..36bb9f559c2 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapHeatmapController_Test.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTGoogleMapHeatmapController.h" + +/// Internal APIs exposed for unit testing +@interface FLTGoogleMapHeatmapController (Test) + +/// Updates the underlying GMUHeatmapTileLayer with the properties from the given options. +/// +/// Setting the heatmap to visible will set its map to the given mapView. ++ (void)updateHeatmap:(GMUHeatmapTileLayer *)heatmapTileLayer + fromOptions:(NSDictionary *)options + withMapView:(GMSMapView *)mapView; + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m index 1bb35917dbd..8132ffbdf28 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -3,6 +3,8 @@ // found in the LICENSE file. #import "FLTGoogleMapTileOverlayController.h" +#import "FLTGoogleMapTileOverlayController_Test.h" + #import "FLTGoogleMapJSONConversions.h" @interface FLTGoogleMapTileOverlayController () @@ -21,8 +23,9 @@ - (instancetype)initWithTileOverlay:(FGMPlatformTileOverlay *)tileOverlay if (self) { _layer = tileLayer; _mapView = mapView; - // TODO(stuartmorgan: Refactor to avoid this call to an instance method in init. - [self updateFromPlatformTileOverlay:tileOverlay]; + [FLTGoogleMapTileOverlayController updateTileLayer:tileLayer + fromPlatformTileOverlay:tileOverlay + withMapView:mapView]; } return self; } @@ -35,33 +38,22 @@ - (void)clearTileCache { [self.layer clearTileCache]; } -- (void)setFadeIn:(BOOL)fadeIn { - self.layer.fadeIn = fadeIn; -} - -- (void)setTransparency:(float)transparency { - float opacity = 1.0 - transparency; - self.layer.opacity = opacity; -} - -- (void)setVisible:(BOOL)visible { - self.layer.map = visible ? self.mapView : nil; +- (void)updateFromPlatformTileOverlay:(FGMPlatformTileOverlay *)overlay { + [FLTGoogleMapTileOverlayController updateTileLayer:self.layer + fromPlatformTileOverlay:overlay + withMapView:self.mapView]; } -- (void)setZIndex:(int)zIndex { - self.layer.zIndex = zIndex; -} ++ (void)updateTileLayer:(GMSTileLayer *)tileLayer + fromPlatformTileOverlay:(FGMPlatformTileOverlay *)platformOverlay + withMapView:(GMSMapView *)mapView { + tileLayer.opacity = 1.0 - platformOverlay.transparency; + tileLayer.zIndex = (int)platformOverlay.zIndex; + tileLayer.fadeIn = platformOverlay.fadeIn; + tileLayer.tileSize = platformOverlay.tileSize; -- (void)setTileSize:(NSInteger)tileSize { - self.layer.tileSize = tileSize; -} - -- (void)updateFromPlatformTileOverlay:(FGMPlatformTileOverlay *)overlay { - [self setVisible:overlay.visible]; - [self setTransparency:overlay.transparency]; - [self setZIndex:(int)overlay.zIndex]; - [self setFadeIn:overlay.fadeIn]; - [self setTileSize:overlay.tileSize]; + // This must be done last, to avoid visual flickers of default property values. + tileLayer.map = platformOverlay.visible ? mapView : nil; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController_Test.h new file mode 100644 index 00000000000..e753698cde4 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController_Test.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "FLTGoogleMapTileOverlayController.h" + +/// Internal APIs exposed for unit testing +@interface FLTGoogleMapTileOverlayController (Test) + +/// Updates the underlying GMSTileLayer with the properties from the given FGMPlatformTileOverlay. +/// +/// Setting the tile overlay to visible will set its map to the given mapView. ++ (void)updateTileLayer:(GMSTileLayer *)tileLayer + fromPlatformTileOverlay:(FGMPlatformTileOverlay *)platformOverlay + withMapView:(GMSMapView *)mapView; + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h index 07a134c5fc0..7be1b655022 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h @@ -7,6 +7,8 @@ #import "messages.g.h" +NS_ASSUME_NONNULL_BEGIN + // Defines circle controllable by Flutter. @interface FLTGoogleMapCircleController : NSObject - (instancetype)initCircleWithPlatformCircle:(FGMPlatformCircle *)circle @@ -24,3 +26,5 @@ - (void)didTapCircleWithIdentifier:(NSString *)identifier; - (bool)hasCircleWithIdentifier:(NSString *)identifier; @end + +NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m index 136fbc0cc67..81c5549bd32 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m @@ -3,6 +3,8 @@ // found in the LICENSE file. #import "GoogleMapCircleController.h" +#import "GoogleMapCircleController_Test.h" + #import "FLTGoogleMapJSONConversions.h" @interface FLTGoogleMapCircleController () @@ -22,8 +24,9 @@ - (instancetype)initCircleWithPlatformCircle:(FGMPlatformCircle *)circle radius:circle.radius]; _mapView = mapView; _circle.userData = @[ circle.circleId ]; - // TODO(stuartmorgan: Refactor to avoid this call to an instance method in init. - [self updateFromPlatformCircle:circle]; + [FLTGoogleMapCircleController updateCircle:_circle + fromPlatformCircle:circle + withMapView:mapView]; } return self; } @@ -32,41 +35,25 @@ - (void)removeCircle { self.circle.map = nil; } -- (void)setConsumeTapEvents:(BOOL)consumes { - self.circle.tappable = consumes; -} -- (void)setVisible:(BOOL)visible { - self.circle.map = visible ? self.mapView : nil; -} -- (void)setZIndex:(int)zIndex { - self.circle.zIndex = zIndex; -} -- (void)setCenter:(CLLocationCoordinate2D)center { - self.circle.position = center; -} -- (void)setRadius:(CLLocationDistance)radius { - self.circle.radius = radius; -} - -- (void)setStrokeColor:(UIColor *)color { - self.circle.strokeColor = color; -} -- (void)setStrokeWidth:(CGFloat)width { - self.circle.strokeWidth = width; -} -- (void)setFillColor:(UIColor *)color { - self.circle.fillColor = color; -} - - (void)updateFromPlatformCircle:(FGMPlatformCircle *)platformCircle { - [self setConsumeTapEvents:platformCircle.consumeTapEvents]; - [self setVisible:platformCircle.visible]; - [self setZIndex:platformCircle.zIndex]; - [self setCenter:FGMGetCoordinateForPigeonLatLng(platformCircle.center)]; - [self setRadius:platformCircle.radius]; - [self setStrokeColor:FGMGetColorForRGBA(platformCircle.strokeColor)]; - [self setStrokeWidth:platformCircle.strokeWidth]; - [self setFillColor:FGMGetColorForRGBA(platformCircle.fillColor)]; + [FLTGoogleMapCircleController updateCircle:self.circle + fromPlatformCircle:platformCircle + withMapView:self.mapView]; +} + ++ (void)updateCircle:(GMSCircle *)circle + fromPlatformCircle:(FGMPlatformCircle *)platformCircle + withMapView:(GMSMapView *)mapView { + circle.tappable = platformCircle.consumeTapEvents; + circle.zIndex = platformCircle.zIndex; + circle.position = FGMGetCoordinateForPigeonLatLng(platformCircle.center); + circle.radius = platformCircle.radius; + circle.strokeColor = FGMGetColorForRGBA(platformCircle.strokeColor); + circle.strokeWidth = platformCircle.strokeWidth; + circle.fillColor = FGMGetColorForRGBA(platformCircle.fillColor); + + // This must be done last, to avoid visual flickers of default property values. + circle.map = platformCircle.visible ? mapView : nil; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController_Test.h new file mode 100644 index 00000000000..1c72d2060dc --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController_Test.h @@ -0,0 +1,21 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "GoogleMapCircleController.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Private methods exposed for testing. +@interface FLTGoogleMapCircleController (Test) + +/// Updates the underlying GMSCircle with the properties from the given FGMPlatformCircle. +/// +/// Setting the circle to visible will set its map to the given mapView. ++ (void)updateCircle:(GMSCircle *)circle + fromPlatformCircle:(FGMPlatformCircle *)platformCircle + withMapView:(GMSMapView *)mapView; + +@end + +NS_ASSUME_NONNULL_END 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 96939d76d75..1d842094091 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 "GoogleMapMarkerController_Test.h" #import "FGMImageUtils.h" #import "FGMMarkerUserData.h" @@ -34,10 +35,6 @@ - (instancetype)initWithMarker:(GMSMarker *)marker return self; } -- (void)setClusterManagerIdentifier:(nullable NSString *)clusterManagerIdentifier { - _clusterManagerIdentifier = clusterManagerIdentifier; -} - - (void)showInfoWindow { self.mapView.selectedMarker = self.marker; } @@ -56,85 +53,55 @@ - (void)removeMarker { self.marker.map = nil; } -- (void)setAlpha:(float)alpha { - self.marker.opacity = alpha; -} - -- (void)setAnchor:(CGPoint)anchor { - self.marker.groundAnchor = anchor; -} - -- (void)setDraggable:(BOOL)draggable { - self.marker.draggable = draggable; -} - -- (void)setFlat:(BOOL)flat { - self.marker.flat = flat; -} - -- (void)setIcon:(UIImage *)icon { - self.marker.icon = icon; -} - -- (void)setInfoWindowAnchor:(CGPoint)anchor { - self.marker.infoWindowAnchor = anchor; -} - -- (void)setInfoWindowTitle:(NSString *)title snippet:(NSString *)snippet { - self.marker.title = title; - self.marker.snippet = snippet; -} - -- (void)setPosition:(CLLocationCoordinate2D)position { - self.marker.position = position; -} +- (void)updateFromPlatformMarker:(FGMPlatformMarker *)platformMarker + registrar:(NSObject *)registrar + screenScale:(CGFloat)screenScale { + self.clusterManagerIdentifier = platformMarker.clusterManagerId; + self.consumeTapEvents = platformMarker.consumeTapEvents; -- (void)setRotation:(CLLocationDegrees)rotation { - self.marker.rotation = rotation; -} + // Set the marker's user data with current identifiers. + FGMSetIdentifiersToMarkerUserData(self.markerIdentifier, self.clusterManagerIdentifier, + self.marker); -- (void)setVisible:(BOOL)visible { // 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)updateFromPlatformMarker:(FGMPlatformMarker *)platformMarker - registrar:(NSObject *)registrar - screenScale:(CGFloat)screenScale { - [self setClusterManagerIdentifier:platformMarker.clusterManagerId]; - [self setAlpha:platformMarker.alpha]; - [self setAnchor:FGMGetCGPointForPigeonPoint(platformMarker.anchor)]; - [self setDraggable:platformMarker.draggable]; + [FLTGoogleMapMarkerController updateMarker:self.marker + fromPlatformMarker:platformMarker + withMapView:self.mapView + registrar:registrar + screenScale:screenScale + usingOpacityForVisibility:self.clusterManagerIdentifier]; +} + ++ (void)updateMarker:(GMSMarker *)marker + fromPlatformMarker:(FGMPlatformMarker *)platformMarker + withMapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar + screenScale:(CGFloat)screenScale + usingOpacityForVisibility:(BOOL)useOpacityForVisibility { + marker.groundAnchor = FGMGetCGPointForPigeonPoint(platformMarker.anchor); + marker.draggable = platformMarker.draggable; UIImage *image = FGMIconFromBitmap(platformMarker.icon, registrar, screenScale); - [self setIcon:image]; - [self setFlat:platformMarker.flat]; - [self setConsumeTapEvents:platformMarker.consumeTapEvents]; - [self setPosition:FGMGetCoordinateForPigeonLatLng(platformMarker.position)]; - [self setRotation:platformMarker.rotation]; - [self setZIndex:(int)platformMarker.zIndex]; + marker.icon = image; + marker.flat = platformMarker.flat; + marker.position = FGMGetCoordinateForPigeonLatLng(platformMarker.position); + marker.rotation = platformMarker.rotation; + marker.zIndex = (int)platformMarker.zIndex; FGMPlatformInfoWindow *infoWindow = platformMarker.infoWindow; - [self setInfoWindowAnchor:FGMGetCGPointForPigeonPoint(infoWindow.anchor)]; + marker.infoWindowAnchor = FGMGetCGPointForPigeonPoint(infoWindow.anchor); if (infoWindow.title) { - [self setInfoWindowTitle:infoWindow.title snippet:infoWindow.snippet]; + marker.title = infoWindow.title; + marker.snippet = infoWindow.snippet; } - // Set the marker's user data with current identifiers. - FGMSetIdentifiersToMarkerUserData(self.markerIdentifier, self.clusterManagerIdentifier, - self.marker); - // Ensure setVisible is called last as it adds the marker to the map, // and must be done after all other parameters are set. - [self setVisible:platformMarker.visible]; + if (useOpacityForVisibility) { + marker.opacity = platformMarker.visible ? platformMarker.alpha : 0.0f; + } else { + marker.opacity = platformMarker.alpha; + marker.map = platformMarker.visible ? mapView : nil; + } } @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h index 3b7a4b2699c..f6ff32e791e 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController_Test.h @@ -10,6 +10,16 @@ /// The underlying controlled GMSMarker. @property(strong, nonatomic, readonly) GMSMarker *marker; +/// Updates the underlying GMSMarker with the properties from the given FGMPlatformMarker. +/// +/// Setting the marker to visible will set its map to the given mapView. ++ (void)updateMarker:(GMSMarker *)marker + fromPlatformMarker:(FGMPlatformMarker *)platformMarker + withMapView:(GMSMapView *)mapView + registrar:(NSObject *)registrar + screenScale:(CGFloat)screenScale + usingOpacityForVisibility:(BOOL)useOpacityForVisibility; + @end /// Methods exposed for unit testing. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m index c88cd9fd5a0..aedbf1b90db 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m @@ -3,8 +3,21 @@ // found in the LICENSE file. #import "GoogleMapPolygonController.h" +#import "GoogleMapPolygonController_Test.h" + #import "FLTGoogleMapJSONConversions.h" +/// Converts a list of holes represented as CLLocation lists to GMSMutablePath lists. +static NSArray *FMGPathHolesFromLocationHoles( + NSArray *> *locationHoles) { + NSMutableArray *pathHoles = + [NSMutableArray arrayWithCapacity:locationHoles.count]; + for (NSArray *hole in locationHoles) { + [pathHoles addObject:FGMGetPathFromPoints(hole)]; + } + return pathHoles; +} + @interface FLTGoogleMapPolygonController () @property(strong, nonatomic) GMSPolygon *polygon; @@ -30,52 +43,26 @@ - (void)removePolygon { self.polygon.map = nil; } -- (void)setConsumeTapEvents:(BOOL)consumes { - self.polygon.tappable = consumes; +- (void)updateFromPlatformPolygon:(FGMPlatformPolygon *)polygon { + [FLTGoogleMapPolygonController updatePolygon:self.polygon + fromPlatformPolygon:polygon + withMapView:self.mapView]; } -- (void)setVisible:(BOOL)visible { - self.polygon.map = visible ? self.mapView : nil; -} -- (void)setZIndex:(int)zIndex { - self.polygon.zIndex = zIndex; -} -- (void)setPoints:(NSArray *)points { - self.polygon.path = FGMGetPathFromPoints(points); -} -- (void)setHoles:(NSArray *> *)rawHoles { - NSMutableArray *holes = [[NSMutableArray alloc] init]; - for (NSArray *points in rawHoles) { - GMSMutablePath *path = [GMSMutablePath path]; - for (CLLocation *location in points) { - [path addCoordinate:location.coordinate]; - } - [holes addObject:path]; - } - - self.polygon.holes = holes; -} - -- (void)setFillColor:(UIColor *)color { - self.polygon.fillColor = color; -} -- (void)setStrokeColor:(UIColor *)color { - self.polygon.strokeColor = color; -} -- (void)setStrokeWidth:(CGFloat)width { - self.polygon.strokeWidth = width; -} ++ (void)updatePolygon:(GMSPolygon *)polygon + fromPlatformPolygon:(FGMPlatformPolygon *)platformPolygon + withMapView:(GMSMapView *)mapView { + polygon.tappable = platformPolygon.consumesTapEvents; + polygon.zIndex = (int)platformPolygon.zIndex; + polygon.path = FGMGetPathFromPoints(FGMGetPointsForPigeonLatLngs(platformPolygon.points)); + polygon.holes = + FMGPathHolesFromLocationHoles(FGMGetHolesForPigeonLatLngArrays(platformPolygon.holes)); + polygon.fillColor = FGMGetColorForRGBA(platformPolygon.fillColor); + polygon.strokeColor = FGMGetColorForRGBA(platformPolygon.strokeColor); + polygon.strokeWidth = platformPolygon.strokeWidth; -- (void)updateFromPlatformPolygon:(FGMPlatformPolygon *)polygon - registrar:(NSObject *)registrar { - [self setConsumeTapEvents:polygon.consumesTapEvents]; - [self setVisible:polygon.visible]; - [self setZIndex:(int)polygon.zIndex]; - [self setPoints:FGMGetPointsForPigeonLatLngs(polygon.points)]; - [self setHoles:FGMGetHolesForPigeonLatLngArrays(polygon.holes)]; - [self setFillColor:FGMGetColorForRGBA(polygon.fillColor)]; - [self setStrokeColor:FGMGetColorForRGBA(polygon.strokeColor)]; - [self setStrokeWidth:polygon.strokeWidth]; + // This must be done last, to avoid visual flickers of default property values. + polygon.map = platformPolygon.visible ? mapView : nil; } @end @@ -112,7 +99,7 @@ - (void)addPolygons:(NSArray *)polygonsToAdd { [[FLTGoogleMapPolygonController alloc] initWithPath:path identifier:identifier mapView:self.mapView]; - [controller updateFromPlatformPolygon:polygon registrar:self.registrar]; + [controller updateFromPlatformPolygon:polygon]; self.polygonIdentifierToController[identifier] = controller; } } @@ -121,7 +108,7 @@ - (void)changePolygons:(NSArray *)polygonsToChange { for (FGMPlatformPolygon *polygon in polygonsToChange) { NSString *identifier = polygon.polygonId; FLTGoogleMapPolygonController *controller = self.polygonIdentifierToController[identifier]; - [controller updateFromPlatformPolygon:polygon registrar:self.registrar]; + [controller updateFromPlatformPolygon:polygon]; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController_Test.h new file mode 100644 index 00000000000..ab4cf60be22 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController_Test.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "GoogleMapPolygonController.h" + +/// Methods exposed for unit testing. +@interface FLTGoogleMapPolygonController (Test) + +/// Updates the underlying GMSPolygon with the properties from the given FGMPlatformPolygon. +/// +/// Setting the polygon to visible will set its map to the given mapView. ++ (void)updatePolygon:(GMSPolygon *)polygon + fromPlatformPolygon:(FGMPlatformPolygon *)polygon + withMapView:(GMSMapView *)mapView; + +@end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h index 2e9aef1ac60..1574d90d900 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h @@ -13,12 +13,6 @@ identifier:(NSString *)identifier mapView:(GMSMapView *)mapView; - (void)removePolyline; - -/// Sets the pattern on polyline controller -/// -/// @param styles The styles for repeating pattern sections. -/// @param lengths The lengths for repeating pattern sections. -- (void)setPattern:(NSArray *)styles lengths:(NSArray *)lengths; @end @interface FLTPolylinesController : NSObject diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m index ada7e76adb2..cf527779058 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m @@ -3,6 +3,8 @@ // found in the LICENSE file. #import "GoogleMapPolylineController.h" +#import "GoogleMapPolylineController_Test.h" + #import "FLTGoogleMapJSONConversions.h" @interface FLTGoogleMapPolylineController () @@ -30,50 +32,30 @@ - (void)removePolyline { self.polyline.map = nil; } -- (void)setConsumeTapEvents:(BOOL)consumes { - self.polyline.tappable = consumes; -} -- (void)setVisible:(BOOL)visible { - self.polyline.map = visible ? self.mapView : nil; -} -- (void)setZIndex:(int)zIndex { - self.polyline.zIndex = zIndex; -} -- (void)setPoints:(NSArray *)points { - GMSMutablePath *path = [GMSMutablePath path]; - - for (CLLocation *location in points) { - [path addCoordinate:location.coordinate]; - } - self.polyline.path = path; -} - -- (void)setColor:(UIColor *)color { - self.polyline.strokeColor = color; -} -- (void)setStrokeWidth:(CGFloat)width { - self.polyline.strokeWidth = width; -} - -- (void)setGeodesic:(BOOL)isGeodesic { - self.polyline.geodesic = isGeodesic; +- (void)updateFromPlatformPolyline:(FGMPlatformPolyline *)polyline { + [FLTGoogleMapPolylineController updatePolyline:self.polyline + fromPlatformPolyline:polyline + withMapView:self.mapView]; } -- (void)setPattern:(NSArray *)styles lengths:(NSArray *)lengths { - self.polyline.spans = GMSStyleSpans(self.polyline.path, styles, lengths, kGMSLengthRhumb); -} ++ (void)updatePolyline:(GMSPolyline *)polyline + fromPlatformPolyline:(FGMPlatformPolyline *)platformPolyline + withMapView:(GMSMapView *)mapView { + polyline.tappable = platformPolyline.consumesTapEvents; + polyline.zIndex = (int)platformPolyline.zIndex; + GMSMutablePath *path = + FGMGetPathFromPoints(FGMGetPointsForPigeonLatLngs(platformPolyline.points)); + polyline.path = path; + UIColor *strokeColor = FGMGetColorForRGBA(platformPolyline.color); + polyline.strokeColor = strokeColor; + polyline.strokeWidth = platformPolyline.width; + polyline.geodesic = platformPolyline.geodesic; + polyline.spans = + GMSStyleSpans(path, FGMGetStrokeStylesFromPatterns(platformPolyline.patterns, strokeColor), + FGMGetSpanLengthsFromPatterns(platformPolyline.patterns), kGMSLengthRhumb); -- (void)updateFromPlatformPolyline:(FGMPlatformPolyline *)polyline - registrar:(NSObject *)registrar { - [self setConsumeTapEvents:polyline.consumesTapEvents]; - [self setVisible:polyline.visible]; - [self setZIndex:(int)polyline.zIndex]; - [self setPoints:FGMGetPointsForPigeonLatLngs(polyline.points)]; - [self setColor:FGMGetColorForRGBA(polyline.color)]; - [self setStrokeWidth:polyline.width]; - [self setGeodesic:polyline.geodesic]; - [self setPattern:FGMGetStrokeStylesFromPatterns(polyline.patterns, self.polyline.strokeColor) - lengths:FGMGetSpanLengthsFromPatterns(polyline.patterns)]; + // This must be done last, to avoid visual flickers of default property values. + polyline.map = platformPolyline.visible ? mapView : nil; } @end @@ -111,7 +93,7 @@ - (void)addPolylines:(NSArray *)polylinesToAdd { [[FLTGoogleMapPolylineController alloc] initWithPath:path identifier:identifier mapView:self.mapView]; - [controller updateFromPlatformPolyline:polyline registrar:self.registrar]; + [controller updateFromPlatformPolyline:polyline]; self.polylineIdentifierToController[identifier] = controller; } } @@ -120,7 +102,7 @@ - (void)changePolylines:(NSArray *)polylinesToChange { for (FGMPlatformPolyline *polyline in polylinesToChange) { NSString *identifier = polyline.polylineId; FLTGoogleMapPolylineController *controller = self.polylineIdentifierToController[identifier]; - [controller updateFromPlatformPolyline:polyline registrar:self.registrar]; + [controller updateFromPlatformPolyline:polyline]; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController_Test.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController_Test.h index c6dd4def9ce..df9e3129395 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController_Test.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController_Test.h @@ -10,4 +10,16 @@ /// Polyline instance the controller is attached to @property(strong, nonatomic) GMSPolyline *polyline; +/// Updates the controller's polyline with the properties from the given FGMPlatformPolyline. +/// +/// Setting the polyline to visible will set its map to the controller's mapView. +- (void)updateFromPlatformPolyline:(FGMPlatformPolyline *)polyline; + +/// Updates the underlying GMSPolyline with the properties from the given FGMPlatformPolyline. +/// +/// Setting the polyline to visible will set its map to the given mapView. ++ (void)updatePolyline:(GMSPolyline *)polyline + fromPlatformPolyline:(FGMPlatformPolyline *)platformPolyline + withMapView:(GMSMapView *)mapView; + @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap index ad6fe6735b1..3b0f295f9aa 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/google_maps_flutter_ios.modulemap @@ -5,7 +5,13 @@ framework module google_maps_flutter_ios { module * { export * } explicit module Test { + header "FGMGroundOverlayController_Test.h" + header "FLTGoogleMapHeatmapController_Test.h" + header "FLTGoogleMapTileOverlayController_Test.h" + header "GoogleMapCircleController_Test.h" header "GoogleMapController_Test.h" header "GoogleMapMarkerController_Test.h" + header "GoogleMapPolygonController_Test.h" + header "GoogleMapPolylineController_Test.h" } } 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 50eadd0efc7..9f1c846fddb 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.15.5 +version: 2.15.6 environment: sdk: ^3.7.0