From efe23b7c283139c2bf3f93e160c8e2dac3d667c8 Mon Sep 17 00:00:00 2001 From: Nathan Hamblen Date: Mon, 12 Apr 2021 08:09:00 -0400 Subject: [PATCH] Support override of attribution click action (iOS) This override allows the application to provide a custom UX for Mapbox telemetry settings and all required copyright information. This improves the utility of the existing `getTelemetryEnabled` and `setTelemetryEnabled` methods, as there is otherwise no means to provide an alternative to the basic action sheet shown by default. While it seemed a bit dubious to alter the click target of a UI element that belongs to the underlying framework, it's apparently expected that the application may override the button's visibility as that use case is handled by the framework. (The application is prevented from running unless a particular property is added to the info.plist.) I explored that route as well, hiding the attribution button and replacing it with an imitation, however the built-in button has features that would be difficult to replicate in an externally added view. For example, it repositions itself when the content insets change, and it's always properly aligned with the Mapbox logo to the left. Since the appearance and positioning of the built-in button is good and appropriate for almost any application, but the action sheet is rather basic and won't include whatever additional attributions an application may require, overriding its click action appears the be the best option. --- ios/Classes/MapboxMapController.swift | 27 +++++++++++++++++-- lib/src/controller.dart | 12 +++++++++ lib/src/mapbox_map.dart | 5 ++++ .../lib/src/mapbox_gl_platform_interface.dart | 6 ++++- .../lib/src/method_channel_mapbox_gl.dart | 3 +++ 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift index c1d86e07f..73482bbb7 100644 --- a/ios/Classes/MapboxMapController.swift +++ b/ios/Classes/MapboxMapController.swift @@ -57,7 +57,7 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma longPress.require(toFail: recognizer) } mapView.addGestureRecognizer(longPress) - + if let args = args as? [String: Any] { Convert.interpretMapboxMapOptions(options: args["options"], delegate: self) if let initialCameraPosition = args["initialCameraPosition"] as? [String: Any], @@ -72,8 +72,14 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma if let annotationConsumeTapEventsArg = args["annotationConsumeTapEvents"] as? [String] { annotationConsumeTapEvents = annotationConsumeTapEventsArg } + if let onAttributionClickOverride = args["onAttributionClickOverride"] as? Bool { + if onAttributionClickOverride { + setupAttribution(mapView) + } + } } } + func removeAllForController(controller: MGLAnnotationController, ids: [String]){ let idSet = Set(ids) let annotations = controller.styleAnnotations() @@ -813,7 +819,24 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma } return MGLAnnotationView(frame: CGRect(x: 0, y: 0, width: 10, height: 10)) } - + + /* + * Override the attribution button's click target to handle the event locally. + * Called if the application supplies an onAttributionClick handler. + */ + func setupAttribution(_ mapView: MGLMapView) { + mapView.attributionButton.removeTarget(mapView, action: #selector(mapView.showAttribution), for: .touchUpInside) + mapView.attributionButton.addTarget(self, action: #selector(showAttribution), for: UIControl.Event.touchUpInside) + } + + /* + * Custom click handler for the attribution button. This callback is bound when + * the application specifies an onAttributionClick handler. + */ + @objc func showAttribution() { + channel?.invokeMethod("map#onAttributionClick", arguments: []) + } + /* * MGLMapViewDelegate */ diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 1cc15c8da..95e816809 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -7,6 +7,8 @@ part of mapbox_gl; typedef void OnMapClickCallback(Point point, LatLng coordinates); typedef void OnMapLongClickCallback(Point point, LatLng coordinates); +typedef void OnAttributionClickCallback(); + typedef void OnStyleLoadedCallback(); typedef void OnUserLocationUpdated(UserLocation location); @@ -38,6 +40,7 @@ class MapboxMapController extends ChangeNotifier { {this.onStyleLoadedCallback, this.onMapClick, this.onMapLongClick, + this.onAttributionClick, this.onCameraTrackingDismissed, this.onCameraTrackingChanged, this.onMapIdle, @@ -125,6 +128,12 @@ class MapboxMapController extends ChangeNotifier { } }); + MapboxGlPlatform.getInstance(_id).onAttributionClickPlatform.add((_) { + if (onAttributionClick != null) { + onAttributionClick!(); + } + }); + MapboxGlPlatform.getInstance(_id) .onCameraTrackingChangedPlatform .add((mode) { @@ -158,6 +167,7 @@ class MapboxMapController extends ChangeNotifier { OnMapClickCallback? onMapClick, OnUserLocationUpdated? onUserLocationUpdated, OnMapLongClickCallback? onMapLongClick, + OnAttributionClickCallback? onAttributionClick, OnCameraTrackingDismissedCallback? onCameraTrackingDismissed, OnCameraTrackingChangedCallback? onCameraTrackingChanged, OnCameraIdleCallback? onCameraIdle, @@ -167,6 +177,7 @@ class MapboxMapController extends ChangeNotifier { onMapClick: onMapClick, onUserLocationUpdated: onUserLocationUpdated, onMapLongClick: onMapLongClick, + onAttributionClick: onAttributionClick, onCameraTrackingDismissed: onCameraTrackingDismissed, onCameraTrackingChanged: onCameraTrackingChanged, onCameraIdle: onCameraIdle, @@ -183,6 +194,7 @@ class MapboxMapController extends ChangeNotifier { final OnMapLongClickCallback? onMapLongClick; final OnUserLocationUpdated? onUserLocationUpdated; + final OnAttributionClickCallback? onAttributionClick; final OnCameraTrackingDismissedCallback? onCameraTrackingDismissed; final OnCameraTrackingChangedCallback? onCameraTrackingChanged; diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index b07d7925d..d2d38a9a4 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -35,6 +35,7 @@ class MapboxMap extends StatefulWidget { this.onMapClick, this.onUserLocationUpdated, this.onMapLongClick, + this.onAttributionClick, this.onCameraTrackingDismissed, this.onCameraTrackingChanged, this.onCameraIdle, @@ -172,6 +173,8 @@ class MapboxMap extends StatefulWidget { final OnMapClickCallback? onMapClick; final OnMapClickCallback? onMapLongClick; + final OnAttributionClickCallback? onAttributionClick; + /// While the `myLocationEnabled` property is set to `true`, this method is /// called whenever a new location update is received by the map view. final OnUserLocationUpdated? onUserLocationUpdated; @@ -217,6 +220,7 @@ class _MapboxMapState extends State { 'accessToken': widget.accessToken, 'annotationOrder': annotationOrder, 'annotationConsumeTapEvents': annotationConsumeTapEvents, + 'onAttributionClickOverride': widget.onAttributionClick != null, }; return _mapboxGlPlatform.buildView( creationParams, onPlatformViewCreated, widget.gestureRecognizers); @@ -267,6 +271,7 @@ class _MapboxMapState extends State { onMapClick: widget.onMapClick, onUserLocationUpdated: widget.onUserLocationUpdated, onMapLongClick: widget.onMapLongClick, + onAttributionClick: widget.onAttributionClick, onCameraTrackingDismissed: widget.onCameraTrackingDismissed, onCameraTrackingChanged: widget.onCameraTrackingChanged, onCameraIdle: widget.onCameraIdle, diff --git a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart index a261ca6ac..f28e37531 100644 --- a/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart +++ b/mapbox_gl_platform_interface/lib/src/mapbox_gl_platform_interface.dart @@ -46,7 +46,11 @@ abstract class MapboxGlPlatform { final onMapLongClickPlatform = ArgumentCallbacks>(); - final onCameraTrackingChangedPlatform = + final ArgumentCallbacks onAttributionClickPlatform = + ArgumentCallbacks(); + + final ArgumentCallbacks + onCameraTrackingChangedPlatform = ArgumentCallbacks(); final onCameraTrackingDismissedPlatform = ArgumentCallbacks(); diff --git a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart index fbc12ae07..e471f7d40 100644 --- a/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart +++ b/mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart @@ -67,6 +67,9 @@ class MethodChannelMapboxGl extends MapboxGlPlatform { onMapLongClickPlatform( {'point': Point(x, y), 'latLng': LatLng(lat, lng)}); + break; + case 'map#onAttributionClick': + onAttributionClickPlatform(null); break; case 'map#onCameraTrackingChanged': final int mode = call.arguments['mode'];