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 9fb403c399f..3862b89afd6 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.15.1 + +* Fixes regression in displaying info windows. + ## 2.15.0 * Adds support for animating the camera with a duration. 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 4db85635bea..7bb0fbd3cc4 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 @@ -11,6 +11,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 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 */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 478116522BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */; }; 528F16832C62941000148160 /* FGMClusterManagersControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 528F16822C62941000148160 /* FGMClusterManagersControllerTests.m */; }; @@ -64,6 +65,7 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -212,6 +214,7 @@ 6851F3552835BC180032B7C8 /* FLTGoogleMapJSONConversionsConversionTests.m */, 0DD7B6C22B744EEF00E857FD /* FLTTileProviderControllerTests.m */, F7151F12265D7ED70028CB91 /* GoogleMapsTests.m */, + 330909FE2D99B79B0077A751 /* GoogleMapsMarkerControllerTests.m */, 478116512BEF8F47002F593E /* GoogleMapsPolylinesControllerTests.m */, 2A6906C62D263DE7001F8426 /* GoogleMapsGroundOverlayControllerTests.m */, 982F2A6A27BADE17003C81F4 /* PartiallyMockedMapView.h */, @@ -522,6 +525,7 @@ 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 */, 2A6906C72D263DF4001F8426 /* GoogleMapsGroundOverlayControllerTests.m in Sources */, 0DD7B6C32B744EEF00E857FD /* FLTTileProviderControllerTests.m in Sources */, 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 new file mode 100644 index 00000000000..5b5329e9880 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsMarkerControllerTests.m @@ -0,0 +1,225 @@ +// 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 GoogleMapsMarkerControllerTests : XCTestCase +@end + +@implementation GoogleMapsMarkerControllerTests + +/// Returns a mocked map view for use with marker controllers. +- (GMSMapView *)mockedMapView { + 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 FLTMarkersController instance instantiated with the given map view. +/// +/// The mapView should outlive the controller, as the controller keeps a weak reference to it. +- (FLTMarkersController *)markersControllerWithMapView:(GMSMapView *)mapView { + NSObject *mockRegistrar = + OCMStrictProtocolMock(@protocol(FlutterPluginRegistrar)); + return [[FLTMarkersController alloc] initWithMapView:mapView + callbackHandler:[[FGMMapsCallbackApi alloc] init] + clusterManagersController:nil + registrar:mockRegistrar]; +} + +- (FGMPlatformBitmap *)placeholderBitmap { + return [FGMPlatformBitmap makeWithBitmap:[FGMPlatformBitmapDefaultMarker makeWithHue:@0]]; +} + +- (void)testSetsMarkerNumericProperties { + GMSMapView *mapView = [self mockedMapView]; + FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; + + NSString *markerIdentifier = @"marker"; + double anchorX = 3.14; + double anchorY = 2.718; + double alpha = 0.4; + double rotation = 90.0; + double zIndex = 3.0; + double latitutde = 10.0; + double longitude = 20.0; + [controller addMarkers:@[ [FGMPlatformMarker + makeWithAlpha:alpha + anchor:[FGMPlatformPoint makeWithX:anchorX y:anchorY] + 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:latitutde + longitude:longitude] + rotation:rotation + visible:YES + zIndex:zIndex + markerId:markerIdentifier + clusterManagerId:nil] ]]; + + FLTGoogleMapMarkerController *markerController = + controller.markerIdentifierToController[markerIdentifier]; + GMSMarker *marker = markerController.marker; + + const double delta = 0.0001; + XCTAssertEqualWithAccuracy(marker.opacity, alpha, delta); + XCTAssertEqualWithAccuracy(marker.rotation, rotation, delta); + XCTAssertEqualWithAccuracy(marker.zIndex, zIndex, delta); + XCTAssertEqualWithAccuracy(marker.groundAnchor.x, anchorX, delta); + XCTAssertEqualWithAccuracy(marker.groundAnchor.y, anchorY, delta); + XCTAssertEqualWithAccuracy(marker.position.latitude, latitutde, delta); + XCTAssertEqualWithAccuracy(marker.position.longitude, longitude, delta); +} + +// Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from +// another property. +- (void)testSetsDraggable { + GMSMapView *mapView = [self mockedMapView]; + FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; + + NSString *markerIdentifier = @"marker"; + [controller addMarkers:@[ [FGMPlatformMarker + makeWithAlpha:1.0 + anchor:[FGMPlatformPoint makeWithX:0 y:0] + consumeTapEvents:NO + draggable:YES + flat:NO + icon:[self placeholderBitmap] + infoWindow:[FGMPlatformInfoWindow + makeWithTitle:@"info title" + snippet:@"info snippet" + anchor:[FGMPlatformPoint makeWithX:0 y:0]] + position:[FGMPlatformLatLng makeWithLatitude:0.0 longitude:0.0] + rotation:0 + visible:NO + zIndex:0 + markerId:markerIdentifier + clusterManagerId:nil] ]]; + + FLTGoogleMapMarkerController *markerController = + controller.markerIdentifierToController[markerIdentifier]; + GMSMarker *marker = markerController.marker; + + XCTAssertTrue(marker.draggable); +} + +// Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from +// another property. +- (void)testSetsFlat { + GMSMapView *mapView = [self mockedMapView]; + FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; + + NSString *markerIdentifier = @"marker"; + [controller addMarkers:@[ [FGMPlatformMarker + makeWithAlpha:1.0 + anchor:[FGMPlatformPoint makeWithX:0 y:0] + consumeTapEvents:NO + draggable:NO + flat:YES + icon:[self placeholderBitmap] + infoWindow:[FGMPlatformInfoWindow + makeWithTitle:@"info title" + snippet:@"info snippet" + anchor:[FGMPlatformPoint makeWithX:0 y:0]] + position:[FGMPlatformLatLng makeWithLatitude:0.0 longitude:0.0] + rotation:0 + visible:NO + zIndex:0 + markerId:markerIdentifier + clusterManagerId:nil] ]]; + + FLTGoogleMapMarkerController *markerController = + controller.markerIdentifierToController[markerIdentifier]; + GMSMarker *marker = markerController.marker; + + XCTAssertTrue(marker.flat); +} + +// Boolean properties are tested individually to ensure they aren't accidentally cross-assigned from +// another property. +- (void)testSetsVisible { + GMSMapView *mapView = [self mockedMapView]; + FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; + + NSString *markerIdentifier = @"marker"; + [controller addMarkers:@[ [FGMPlatformMarker + makeWithAlpha:1.0 + anchor:[FGMPlatformPoint makeWithX:0 y:0] + consumeTapEvents:NO + draggable:NO + flat:NO + icon:[self placeholderBitmap] + infoWindow:[FGMPlatformInfoWindow + makeWithTitle:@"info title" + snippet:@"info snippet" + anchor:[FGMPlatformPoint makeWithX:0 y:0]] + position:[FGMPlatformLatLng makeWithLatitude:0.0 longitude:0.0] + rotation:0 + visible:YES + zIndex:0 + markerId:markerIdentifier + clusterManagerId:nil] ]]; + + FLTGoogleMapMarkerController *markerController = + controller.markerIdentifierToController[markerIdentifier]; + GMSMarker *marker = markerController.marker; + + // Visibility is controlled by being set to a map. + XCTAssertNotNil(marker.map); +} + +- (void)testSetsMarkerInfoWindowProperties { + GMSMapView *mapView = [self mockedMapView]; + FLTMarkersController *controller = [self markersControllerWithMapView:mapView]; + + NSString *markerIdentifier = @"marker"; + NSString *title = @"info title"; + NSString *snippet = @"info snippet"; + double anchorX = 3.14; + double anchorY = 2.718; + [controller + addMarkers:@[ [FGMPlatformMarker + makeWithAlpha:1.0 + anchor:[FGMPlatformPoint makeWithX:0 y:0] + consumeTapEvents:YES + draggable:YES + flat:YES + icon:[self placeholderBitmap] + infoWindow:[FGMPlatformInfoWindow + makeWithTitle:title + snippet:snippet + anchor:[FGMPlatformPoint makeWithX:anchorX + y:anchorY]] + position:[FGMPlatformLatLng makeWithLatitude:0 longitude:0] + rotation:0 + visible:YES + zIndex:0 + markerId:markerIdentifier + clusterManagerId:nil] ]]; + + FLTGoogleMapMarkerController *markerController = + controller.markerIdentifierToController[markerIdentifier]; + GMSMarker *marker = markerController.marker; + + const double delta = 0.0001; + XCTAssertEqualWithAccuracy(marker.infoWindowAnchor.x, anchorX, delta); + XCTAssertEqualWithAccuracy(marker.infoWindowAnchor.y, anchorY, delta); + XCTAssertEqual(marker.title, title); + XCTAssertEqual(marker.snippet, snippet); +} + +@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 a76915d7d20..4db08d52e89 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 @@ -10,7 +10,7 @@ @interface FLTGoogleMapMarkerController () -@property(strong, nonatomic) GMSMarker *marker; +@property(strong, nonatomic, readwrite) GMSMarker *marker; @property(weak, nonatomic) GMSMapView *mapView; @property(assign, nonatomic, readwrite) BOOL consumeTapEvents; /// The unique identifier for the cluster manager. @@ -119,30 +119,21 @@ - (void)updateFromPlatformMarker:(FGMPlatformMarker *)platformMarker [self setConsumeTapEvents:platformMarker.consumeTapEvents]; [self setPosition:FGMGetCoordinateForPigeonLatLng(platformMarker.position)]; [self setRotation:platformMarker.rotation]; - [self setVisible:platformMarker.visible]; [self setZIndex:platformMarker.zIndex]; -} - -- (void)interpretInfoWindow:(NSDictionary *)data { - NSDictionary *infoWindow = FGMGetValueOrNilFromDict(data, @"infoWindow"); - if (infoWindow) { - NSString *title = FGMGetValueOrNilFromDict(infoWindow, @"title"); - NSString *snippet = FGMGetValueOrNilFromDict(infoWindow, @"snippet"); - if (title) { - [self setInfoWindowTitle:title snippet:snippet]; - } - NSArray *infoWindowAnchor = infoWindow[@"infoWindowAnchor"]; - if (infoWindowAnchor) { - [self setInfoWindowAnchor:[FLTGoogleMapJSONConversions pointFromArray:infoWindowAnchor]]; - } + FGMPlatformInfoWindow *infoWindow = platformMarker.infoWindow; + [self setInfoWindowAnchor:FGMGetCGPointForPigeonPoint(infoWindow.anchor)]; + if (infoWindow.title) { + [self setInfoWindowTitle:infoWindow.title snippet:infoWindow.snippet]; } + + [self setVisible:platformMarker.visible]; } @end @interface FLTMarkersController () -@property(strong, nonatomic) NSMutableDictionary *markerIdentifierToController; +@property(strong, nonatomic, readwrite) NSMutableDictionary *markerIdentifierToController; @property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; /// Controller for adding/removing/fetching cluster managers @property(weak, nonatomic, nullable) FGMClusterManagersController *clusterManagersController; 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 213f975c5e7..3ae9b7ef869 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 @@ -4,26 +4,18 @@ #import "GoogleMapMarkerController.h" +/// Methods exposed for unit testing. @interface FLTGoogleMapMarkerController (Test) -/// Extracts an icon image from the iconData array. -/// -/// @param platformBitmap The Pigeon representation of the icon image. -/// @param registrar A Flutter plugin registrar. -/// @param screenScale Screen scale factor for scaling bitmaps. Must be greater than 0. -/// @return A UIImage object created from the icon data. -/// @note Assert unless screenScale is greater than 0. -- (UIImage *)iconFromBitmap:(FGMPlatformBitmap *)platformBitmap - registrar:(NSObject *)registrar - screenScale:(CGFloat)screenScale; +/// The underlying controlled GMSMarker. +@property(strong, nonatomic, readonly) GMSMarker *marker; -/// Checks if an image can be scaled from an original size to a target size using a scale factor -/// while maintaining the aspect ratio. -/// -/// @param originalSize The original size of the image. -/// @param targetSize The desired target size to scale the image to. -/// @return A BOOL indicating whether the image can be scaled to the target size with scale -/// factor. -+ (BOOL)isScalableWithScaleFactorFromSize:(CGSize)originalSize toSize:(CGSize)targetSize; +@end + +/// Methods exposed for unit testing. +@interface FLTMarkersController (Test) + +/// A mapping from marker identifiers to corresponding marker controllers. +@property(strong, nonatomic, readonly) NSMutableDictionary *markerIdentifierToController; @end 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 32520c7e3fe..55c040865da 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.0 +version: 2.15.1 environment: sdk: ^3.4.0