From e2ab04ff7baba60403ae067821e4c76c14d8f7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Sat, 9 Dec 2023 21:14:18 +0100 Subject: [PATCH] fix: add deselectAnnotationOnTap to deselect and hide pointannotation callout on press, refactor point annotation manager or android --- .../annotation/RNMBXPointAnnotation.kt | 64 +++-- .../rnmbx/components/mapview/RNMBXMapView.kt | 259 +++++++++++------- .../components/mapview/RNMBXMapViewManager.kt | 7 + .../RNMBXMapViewManagerDelegate.java | 3 + .../RNMBXMapViewManagerInterface.java | 1 + docs/MapView.md | 10 + docs/PointAnnotation.md | 2 +- docs/docs.json | 7 + docs/examples.json | 5 +- .../Annotations/ShowPointAnnotation.tsx | 8 +- ios/RNMBX/RNMBXMapView.swift | 56 ++-- ios/RNMBX/RNMBXMapViewComponentView.mm | 46 ++-- ios/RNMBX/RNMBXMapViewManager.m | 1 + .../RNMBXMarkerViewContentComponentView.mm | 16 +- src/components/MapView.tsx | 6 + src/specs/RNMBXMapViewNativeComponent.ts | 2 + 16 files changed, 312 insertions(+), 181 deletions(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/annotation/RNMBXPointAnnotation.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/annotation/RNMBXPointAnnotation.kt index 26a8652cc..bce9fd098 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/annotation/RNMBXPointAnnotation.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/annotation/RNMBXPointAnnotation.kt @@ -6,12 +6,9 @@ import com.rnmapbox.rnmbx.components.AbstractMapFeature import com.mapbox.maps.plugin.annotation.generated.PointAnnotation import com.mapbox.maps.MapboxMap import com.rnmapbox.rnmbx.components.mapview.RNMBXMapView -import com.rnmapbox.rnmbx.components.annotation.RNMBXCallout import com.rnmapbox.rnmbx.utils.GeoJSONUtils import com.rnmapbox.rnmbx.events.constants.EventTypes import com.mapbox.maps.plugin.annotation.generated.PointAnnotationOptions -import com.mapbox.maps.plugin.annotation.generated.PointAnnotationManager -import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotation import com.mapbox.maps.extension.style.layers.properties.generated.IconAnchor import com.rnmapbox.rnmbx.events.PointAnnotationClickEvent import android.graphics.PointF @@ -28,7 +25,7 @@ import java.util.* import com.rnmapbox.rnmbx.v11compat.annotation.*; class RNMBXPointAnnotation(private val mContext: Context, private val mManager: RNMBXPointAnnotationManager) : AbstractMapFeature(mContext), View.OnLayoutChangeListener { - var marker: PointAnnotation? = null + var annotation: PointAnnotation? = null private set private var mMap: MapboxMap? = null private val mHasChildren = false @@ -100,8 +97,8 @@ class RNMBXPointAnnotation(private val mContext: Context, private val mManager: override fun removeFromMap(mapView: RNMBXMapView, reason: RemovalReason): Boolean { val map = (if (mMapView != null) mMapView else mapView) ?: return true - if (marker != null) { - map.pointAnnotationManager?.delete(marker!!) + if (annotation != null) { + map.pointAnnotationManager?.delete(annotation!!) } if (mChildView != null) { map.offscreenAnnotationViewContainer?.removeView(mChildView) @@ -141,33 +138,33 @@ class RNMBXPointAnnotation(private val mContext: Context, private val mManager: val latLng: LatLng? get() = mCoordinate?.let { GeoJSONUtils.toLatLng(it) } val mapboxID: AnnotationID - get() = if (marker == null) INVALID_ANNOTATION_ID else marker!!.id + get() = if (annotation == null) INVALID_ANNOTATION_ID else annotation!!.id fun setCoordinate(point: Point) { mCoordinate = point - if (marker != null) { - marker!!.point = point - mMapView?.pointAnnotationManager?.update(marker!!) + annotation?.let { + it.point = point + mMapView?.pointAnnotationManager?.update(it) } - if (mCalloutSymbol != null) { - mCalloutSymbol!!.point = point - mMapView?.pointAnnotationManager?.update(mCalloutSymbol!!) + mCalloutSymbol?.let { + it.point = point + mMapView?.pointAnnotationManager?.update(it) } } fun setAnchor(x: Float, y: Float) { mAnchor = arrayOf(x, y) - if (marker != null) { + if (annotation != null) { updateAnchor() - mMapView?.pointAnnotationManager?.update(marker!!) + mMapView?.pointAnnotationManager?.update(annotation!!) } } fun setDraggable(draggable: Boolean) { mDraggable = draggable - if (marker != null) { - marker!!.isDraggable = draggable - mMapView?.pointAnnotationManager?.update(marker!!) + annotation?.let { + it.isDraggable = draggable + mMapView?.pointAnnotationManager?.update(it) } } @@ -188,17 +185,17 @@ class RNMBXPointAnnotation(private val mContext: Context, private val mManager: } fun onDragStart() { - mCoordinate = marker!!.point + mCoordinate = annotation!!.point mManager.handleEvent(makeDragEvent(EventTypes.ANNOTATION_DRAG_START)) } fun onDrag() { - mCoordinate = marker!!.point + mCoordinate = annotation!!.point mManager.handleEvent(makeDragEvent(EventTypes.ANNOTATION_DRAG)) } fun onDragEnd() { - mCoordinate = marker!!.point + mCoordinate = annotation!!.point mManager.handleEvent(makeDragEvent(EventTypes.ANNOTATION_DRAG_END)) } @@ -210,41 +207,42 @@ class RNMBXPointAnnotation(private val mContext: Context, private val mManager: .withIconSize(1.0) .withSymbolSortKey(10.0) } - val symbolManager = mMapView?.pointAnnotationManager - if (symbolManager != null && options != null) { - marker = symbolManager.create(options) - updateOptions() + mMapView?.pointAnnotationManager?.let { annotationManager -> + options?.let { + annotation = annotationManager.create(options) + updateOptions() + } } } private fun updateOptions() { - if (marker != null) { + if (annotation != null) { updateIconImage() updateAnchor() - mMapView?.pointAnnotationManager?.update(marker!!) + mMapView?.pointAnnotationManager?.update(annotation!!) } } private fun updateIconImage() { if (mChildView != null) { if (mChildBitmapId != null) { - marker?.iconImage = mChildBitmapId + annotation?.iconImage = mChildBitmapId } } else { - marker?.iconImage = MARKER_IMAGE_ID - marker?.iconAnchor = IconAnchor.BOTTOM + annotation?.iconImage = MARKER_IMAGE_ID + annotation?.iconAnchor = IconAnchor.BOTTOM } } private fun updateAnchor() { - if (mAnchor != null && mChildView != null && mChildBitmap != null && marker != null) { + if (mAnchor != null && mChildView != null && mChildBitmap != null && annotation != null) { var w = mChildBitmap!!.width var h = mChildBitmap!!.height val scale = resources.displayMetrics.density w = (w / scale).toInt() h = (h / scale).toInt() - marker?.iconAnchor = IconAnchor.TOP_LEFT - marker?.iconOffset = Arrays.asList(w.toDouble() * mAnchor!![0] * -1.0, h.toDouble() * mAnchor!![1] * -1.0) + annotation?.iconAnchor = IconAnchor.TOP_LEFT + annotation?.iconOffset = Arrays.asList(w.toDouble() * mAnchor!![0] * -1.0, h.toDouble() * mAnchor!![1] * -1.0) } } diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt index 324c73735..6355c6f53 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt @@ -209,8 +209,18 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie private val mSources: MutableMap> private val mImages: MutableList - private var mPointAnnotationManager: PointAnnotationManager? = null - private var mActiveMarkerID: AnnotationID = INVALID_ANNOTATION_ID + public val pointAnnotationManager: RNMBXPointAnnotationManager by lazy { + val gesturesPlugin: GesturesPlugin = mapView.gestures + gesturesPlugin.removeOnMapClickListener(this) + gesturesPlugin.removeOnMapLongClickListener(this) + + val result = RNMBXPointAnnotationManager(mapView) + + gesturesPlugin.addOnMapClickListener(this) + gesturesPlugin.addOnMapLongClickListener(this) + + result + } private var mProjection: ProjectionName = ProjectionName.MERCATOR private var mLocaleString: String? = null private var mLocaleLayerIds: List? = null @@ -219,7 +229,6 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie private var mCamera: RNMBXCamera? = null private val mFeatures = mutableListOf() private var mQueuedFeatures: MutableList? = ArrayList() - private val mPointAnnotations: MutableMap private val mCameraChangeTracker = CameraChangeTracker() private lateinit var mMap: MapboxMap @@ -233,8 +242,6 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie private var styleLoaded = false private var mHandledMapChangedEvents: HashSet? = null - private var mAnnotationClicked = false - private var mAnnotationDragged = false private var mLocationComponentManager: LocationComponentManager? = null var tintColor: Int? = null private set @@ -255,6 +262,8 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie updateRequestDisallowInterceptTouchEvent(oldValue, value) } + var deselectAnnotationOnTap = false + fun getMapboxMap(): MapboxMap { return mapView.getMapboxMap() } @@ -264,66 +273,6 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie mManager.tagAssigned(id) } - val pointAnnotationManager: PointAnnotationManager? - get() { - if (mPointAnnotationManager == null) { - val _this = this - val gesturesPlugin: GesturesPlugin = mapView.gestures - gesturesPlugin.removeOnMapClickListener(_this) - gesturesPlugin.removeOnMapLongClickListener(_this) - - mPointAnnotationManager = mapView.annotations.createPointAnnotationManager(AnnotationConfig(layerId = "RNMBX-mapview-annotations")) - mPointAnnotationManager?.addClickListener(OnPointAnnotationClickListener { pointAnnotation -> - onMarkerClick(pointAnnotation) - false - } - ) - mPointAnnotationManager?.addDragListener(object : OnPointAnnotationDragListener { - override fun onAnnotationDragStarted(_annotation: Annotation<*>) { - mAnnotationDragged = true; - var reactAnnotation: RNMBXPointAnnotation? = null - for (key in mPointAnnotations.keys) { - val annotation = mPointAnnotations[key] - val curMarkerID = annotation?.mapboxID - if (_annotation.id == curMarkerID) { - reactAnnotation = annotation - } - } - reactAnnotation?.let { it.onDragStart() } - } - - override fun onAnnotationDrag(_annotation: Annotation<*>) { - var reactAnnotation: RNMBXPointAnnotation? = null - for (key in mPointAnnotations.keys) { - val annotation = mPointAnnotations[key] - val curMarkerID = annotation?.mapboxID - if (_annotation.id == curMarkerID) { - reactAnnotation = annotation - } - } - reactAnnotation?.let { it.onDrag() } - } - - override fun onAnnotationDragFinished(_annotation: Annotation<*>) { - mAnnotationDragged = false; - var reactAnnotation: RNMBXPointAnnotation? = null - for (key in mPointAnnotations.keys) { - val annotation = mPointAnnotations[key] - val curMarkerID = annotation?.mapboxID - if (_annotation.id == curMarkerID) { - reactAnnotation = annotation - } - } - reactAnnotation?.let { it.onDragEnd() } - } - }) - gesturesPlugin.addOnMapClickListener(_this) - gesturesPlugin.addOnMapLongClickListener(_this) - - } - return mPointAnnotationManager - } - private fun styleLoaded(style: Style) { savedStyle = style styleLoaded = true @@ -478,7 +427,7 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie feature = childView } else if (childView is RNMBXPointAnnotation) { val annotation = childView - mPointAnnotations[annotation.iD.toString()] = annotation + pointAnnotationManager.add(annotation) feature = childView } else if (childView is RNMBXMarkerView) { feature = childView @@ -514,10 +463,7 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie mSources.remove(feature.iD) } else if (feature is RNMBXPointAnnotation) { val annotation = feature - if (annotation.mapboxID == mActiveMarkerID) { - mActiveMarkerID = INVALID_ANNOTATION_ID - } - mPointAnnotations.remove(annotation.iD) + pointAnnotationManager.remove(annotation) } else if (feature is RNMBXImages) { mImages.remove(feature) } @@ -783,12 +729,14 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie override fun onMapClick(point: Point): Boolean { val _this = this - /*if (mPointAnnotationManager != nil) { - getAnnotations() - }*/if (mAnnotationClicked) { - mAnnotationClicked = false + if (pointAnnotationManager.getAndClearAnnotationClicked()) { return true } + if (deselectAnnotationOnTap) { + if (pointAnnotationManager.deselectSelectedAnnotation()) { + return true + } + } val screenPoint = mMap?.pixelForCoordinate(point) val touchableSources = allTouchableSources val hits = HashMap?>() @@ -817,8 +765,7 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie override fun onMapLongClick(point: Point): Boolean { val _this = this - if (mAnnotationDragged) { - mAnnotationDragged = false + if (pointAnnotationManager.getAndClearAnnotationDragged()) { return true } val screenPoint = mMap?.pixelForCoordinate(point) @@ -830,35 +777,6 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie return false } - fun onMarkerClick(symbol: PointAnnotation) { - mAnnotationClicked = true - val selectedMarkerID = symbol.id - var activeAnnotation: RNMBXPointAnnotation? = null - var nextActiveAnnotation: RNMBXPointAnnotation? = null - for (key in mPointAnnotations.keys) { - val annotation = mPointAnnotations[key] - val curMarkerID = annotation?.mapboxID - if (mActiveMarkerID == curMarkerID) { - activeAnnotation = annotation - } - if (selectedMarkerID == curMarkerID && mActiveMarkerID != curMarkerID) { - nextActiveAnnotation = annotation - } - } - activeAnnotation?.let { deselectAnnotation(it) } - nextActiveAnnotation?.let { selectAnnotation(it) } - } - - fun selectAnnotation(annotation: RNMBXPointAnnotation) { - mActiveMarkerID = annotation.mapboxID - annotation.onSelect(true) - } - - fun deselectAnnotation(annotation: RNMBXPointAnnotation) { - mActiveMarkerID = INVALID_ANNOTATION_ID - annotation.onDeselect() - } - interface FoundLayerCallback { fun found(layer: Layer?) } @@ -1266,7 +1184,6 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie mSources = HashMap() mImages = ArrayList() - mPointAnnotations = HashMap() } // region Ornaments @@ -1610,4 +1527,134 @@ fun OrnamentSettings.setPosAndMargins(posAndMargins: ReadableMap?) { this.margins = margins } +class RNMBXPointAnnotationManager(val mapView: MapView) { + val manager: PointAnnotationManager; + var annotationClicked = false + var annotationDragged = false + + var selected: RNMBXPointAnnotation? = null + + val annotations: MutableMap = hashMapOf() + + init { + manager = mapView.annotations.createPointAnnotationManager(AnnotationConfig(layerId = "RNMBX-mapview-annotations")) + manager.addClickListener(OnPointAnnotationClickListener { pointAnnotation -> + onAnnotationClick(pointAnnotation) + false + }) + } + + fun getAndClearAnnotationClicked(): Boolean { + if (annotationClicked) { + annotationClicked = false + return true + } + return false + } + + fun getAndClearAnnotationDragged(): Boolean { + if (annotationDragged) { + annotationDragged = false + return true + } + return false + } + + fun onAnnotationClick(symbol: PointAnnotation) { + annotationClicked = true + var oldSelected: RNMBXPointAnnotation? = selected + var newSelected: RNMBXPointAnnotation? = null + + for (annotation in annotations.values) { + if (symbol.id == annotation.mapboxID && selected?.mapboxID != symbol.id) { + newSelected = annotation + } + } + + manager.addDragListener(object : OnPointAnnotationDragListener { + override fun onAnnotationDragStarted(_annotation: Annotation<*>) { + annotationDragged = true; + var reactAnnotation: RNMBXPointAnnotation? = null + for (key in annotations.keys) { + val annotation = annotations[key] + val curMarkerID = annotation?.mapboxID + if (_annotation.id == curMarkerID) { + reactAnnotation = annotation + } + } + reactAnnotation?.let { it.onDragStart() } + } + + override fun onAnnotationDrag(_annotation: Annotation<*>) { + var reactAnnotation: RNMBXPointAnnotation? = null + for (key in annotations.keys) { + val annotation = annotations[key] + val curMarkerID = annotation?.mapboxID + if (_annotation.id == curMarkerID) { + reactAnnotation = annotation + } + } + reactAnnotation?.let { it.onDrag() } + } + + override fun onAnnotationDragFinished(_annotation: Annotation<*>) { + annotationDragged = false; + var reactAnnotation: RNMBXPointAnnotation? = null + for (key in annotations.keys) { + val annotation = annotations[key] + val curMarkerID = annotation?.mapboxID + if (_annotation.id == curMarkerID) { + reactAnnotation = annotation + } + } + reactAnnotation?.let { it.onDragEnd() } + } + }) + + oldSelected?.let { deselectAnnotation(it) } + newSelected?.let { selectAnnotation(it) } + } + + fun deselectSelectedAnnotation(): Boolean { + selected?.let { + deselectAnnotation(it) + return true + } + return false + } + + fun selectAnnotation(annotation: RNMBXPointAnnotation) { + selected = annotation + annotation.onSelect(true) + } + + fun deselectAnnotation(annotation: RNMBXPointAnnotation) { + selected = null + annotation.onDeselect() + } + + fun remove(annotation: RNMBXPointAnnotation) { + if (annotation == selected) { + selected = null + } + annotations.remove(annotation.iD) + } + + fun delete(annotation: PointAnnotation) { + manager.delete(annotation) + } + + fun update(annotation: PointAnnotation) { + manager.update(annotation) + } + + fun create(options: PointAnnotationOptions): PointAnnotation { + return manager.create(options) + } + + fun add(annotation: RNMBXPointAnnotation) { + annotations[annotation.iD!!] = annotation + } +} + diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt index 5473fa6be..b671c5de8 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt @@ -330,6 +330,13 @@ open class RNMBXMapViewManager(context: ReactApplicationContext, val viewTagReso mapView.requestDisallowInterceptTouchEvent = requestDisallowInterceptTouchEvent.asBoolean() } + @ReactProp(name = "deselectAnnotationOnTap") + override fun setDeselectAnnotationOnTap(mapView: RNMBXMapView, value: Dynamic?) { + value?.let { + mapView.deselectAnnotationOnTap = it.asBoolean() + } + } + override fun setCompassImage(view: RNMBXMapView, value: Dynamic?) { // TODO: No-op on Android? } diff --git a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerDelegate.java b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerDelegate.java index c09acf65f..6e92af4df 100644 --- a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerDelegate.java +++ b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerDelegate.java @@ -67,6 +67,9 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "pitchEnabled": mViewManager.setPitchEnabled(view, new DynamicFromObject(value)); break; + case "deselectAnnotationOnTap": + mViewManager.setDeselectAnnotationOnTap(view, new DynamicFromObject(value)); + break; case "requestDisallowInterceptTouchEvent": mViewManager.setRequestDisallowInterceptTouchEvent(view, new DynamicFromObject(value)); break; diff --git a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerInterface.java b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerInterface.java index 13388a15d..3f4824585 100644 --- a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerInterface.java +++ b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerInterface.java @@ -28,6 +28,7 @@ public interface RNMBXMapViewManagerInterface { void setScrollEnabled(T view, Dynamic value); void setRotateEnabled(T view, Dynamic value); void setPitchEnabled(T view, Dynamic value); + void setDeselectAnnotationOnTap(T view, Dynamic value); void setRequestDisallowInterceptTouchEvent(T view, Dynamic value); void setProjection(T view, Dynamic value); void setLocalizeLabels(T view, Dynamic value); diff --git a/docs/MapView.md b/docs/MapView.md index e05b7aa63..8f252f119 100644 --- a/docs/MapView.md +++ b/docs/MapView.md @@ -522,6 +522,16 @@ The emitted frequency of regiondidchange events _defaults to:_ `500` +### deselectAnnotationOnTap + +```tsx +boolean +``` +Set to true to deselect any selected annotation when the map is tapped. If set to true you will not receive +the onPress event for the taps that deselect the annotation. Default is false. + +[Show Point Annotations](../examples/Annotations/ShowPointAnnotation) + diff --git a/docs/PointAnnotation.md b/docs/PointAnnotation.md index cc99c223d..a915e5df6 100644 --- a/docs/PointAnnotation.md +++ b/docs/PointAnnotation.md @@ -179,4 +179,4 @@ On v10 and pre v10 android point annotation is rendered offscreen with a canvas - +[Show Point Annotations](../examples/Annotations/ShowPointAnnotation) diff --git a/docs/docs.json b/docs/docs.json index cbcf863df..b2e1bf91f 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -4227,6 +4227,13 @@ "type": "number", "default": "500", "description": "The emitted frequency of regiondidchange events" + }, + { + "name": "deselectAnnotationOnTap", + "required": false, + "type": "boolean", + "default": "none", + "description": "Set to true to deselect any selected annotation when the map is tapped. If set to true you will not receive\nthe onPress event for the taps that deselect the annotation. Default is false." } ], "fileNameWithExt": "MapView.tsx", diff --git a/docs/examples.json b/docs/examples.json index 1a7771efd..10b8b390d 100644 --- a/docs/examples.json +++ b/docs/examples.json @@ -566,7 +566,10 @@ "metadata": { "title": "Show Point Annotations", "tags": [ - "PointAnnotation" + "PointAnnotation", + "MapView#deselectAnnotationOnTap", + "PointAnnotation#refresh", + "getAnnotationsLayerID" ], "docs": "\nShows Point annotation with images\n" }, diff --git a/example/src/examples/Annotations/ShowPointAnnotation.tsx b/example/src/examples/Annotations/ShowPointAnnotation.tsx index 760afe17d..fa1d6a4eb 100755 --- a/example/src/examples/Annotations/ShowPointAnnotation.tsx +++ b/example/src/examples/Annotations/ShowPointAnnotation.tsx @@ -138,6 +138,7 @@ const ShowPointAnnotation = () => { ]); }} style={styles.matchParent} + deselectAnnotationOnTap={true} > Void]] = [:] + + @objc + public var deselectAnnotationOnTap: Bool = false #if RNMBX_11 var cancelables = Set() #endif - lazy var pointAnnotationManager : PointAnnotationManager = { - let result = PointAnnotationManager(annotations: mapView.annotations, mapView: mapView) + lazy var pointAnnotationManager : RNMBXPointAnnotationManager = { + let result = RNMBXPointAnnotationManager(annotations: mapView.annotations, mapView: mapView) self._removeMapboxLongPressGestureRecognizer() return result }() @@ -1158,11 +1161,29 @@ extension RNMBXMapView: GestureManagerDelegate { return orderedLayers.lazy.reversed().compactMap { layersToSource[$0.id] }.first ?? sources.first } + + + func _tapEvent(_ tapPoint: CGPoint) -> RNMBXEvent { + let location = self.mapboxMap.coordinate(for: tapPoint) + var geojson = Feature(geometry: .point(Point(location))); + geojson.properties = [ + "screenPointX": .number(Double(tapPoint.x)), + "screenPointY": .number(Double(tapPoint.y)) + ] + let event = RNMBXEvent(type:.tap, payload: logged("reactOnPress") { try geojson.toJSON() }) + return event + } + @objc func doHandleTap(_ sender: UITapGestureRecognizer) { let tapPoint = sender.location(in: self) pointAnnotationManager.handleTap(sender) { (_: UITapGestureRecognizer) in DispatchQueue.main.async { + if (self.deselectAnnotationOnTap) { + if (self.pointAnnotationManager.deselecteCurrentlySelected(self._tapEvent(tapPoint))) { + return + } + } let touchableSources = self.touchableSources() self.doHandleTapInSources(sources: touchableSources, tapPoint: tapPoint, hits: [:], touchedSources: []) { (hits, touchedSources) in @@ -1194,14 +1215,7 @@ extension RNMBXMapView: GestureManagerDelegate { } else { if let reactOnPress = self.reactOnPress { - let location = self.mapboxMap.coordinate(for: tapPoint) - var geojson = Feature(geometry: .point(Point(location))); - geojson.properties = [ - "screenPointX": .number(Double(tapPoint.x)), - "screenPointY": .number(Double(tapPoint.y)) - ] - let event = RNMBXEvent(type:.tap, payload: logged("reactOnPress") { try geojson.toJSON() }) - self.fireEvent(event: event, callback: reactOnPress) + self.fireEvent(event: self._tapEvent(tapPoint), callback: reactOnPress) } } } @@ -1341,7 +1355,7 @@ extension RNMBXMapView { } } -class PointAnnotationManager : AnnotationInteractionDelegate { +class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { weak var selected : RNMBXPointAnnotation? = nil private var draggedAnnotation: PointAnnotation? @@ -1349,6 +1363,18 @@ class PointAnnotationManager : AnnotationInteractionDelegate { // We handle taps ourselfs // onTap(annotations: annotations) } + + func deselecteCurrentlySelected(_ event: RNMBXEvent) -> Bool { + if let selected = selected { + if let onDeselected = selected.onDeselected { + onDeselected(event.toJSON()) + } + selected.onDeselect() + self.selected = nil + return true + } + return false + } func onTap(annotations: [Annotation]) { guard annotations.count > 0 else { @@ -1370,13 +1396,7 @@ class PointAnnotationManager : AnnotationInteractionDelegate { "screenPointY": .number(Double(position!.y)) ] let event = RNMBXEvent(type:.tap, payload: logged("doHandleTap") { try geojson.toJSON() }) - if let selected = selected { - guard let onDeselected = pt.onDeselected else { - return - } - onDeselected(event.toJSON()) - selected.onDeselect() - } + deselecteCurrentlySelected(event) guard let onSelected = pt.onSelected else { return } diff --git a/ios/RNMBX/RNMBXMapViewComponentView.mm b/ios/RNMBX/RNMBXMapViewComponentView.mm index 77ed1dd48..16fb98d3e 100644 --- a/ios/RNMBX/RNMBXMapViewComponentView.mm +++ b/ios/RNMBX/RNMBXMapViewComponentView.mm @@ -11,6 +11,8 @@ #import #import +#import "RNMBXFabricPropConvert.h" + using namespace facebook::react; @interface RNMBXMapViewComponentView () @@ -125,99 +127,103 @@ + (ComponentDescriptorProvider)componentDescriptorProvider - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps { - const auto &newProps = static_cast(*props); - id attributionEnabled = RNMBXConvertFollyDynamicToId(newProps.attributionEnabled); + const auto &oldViewProps = static_cast(*oldProps); + const auto &newViewProps = static_cast(*props); + + id attributionEnabled = RNMBXConvertFollyDynamicToId(newViewProps.attributionEnabled); if (attributionEnabled != nil) { _view.reactAttributionEnabled = attributionEnabled; } - id attributionPosition = RNMBXConvertFollyDynamicToId(newProps.attributionPosition); + id attributionPosition = RNMBXConvertFollyDynamicToId(newViewProps.attributionPosition); if (attributionPosition != nil) { _view.reactAttributionPosition = attributionPosition; } - id logoEnabled = RNMBXConvertFollyDynamicToId(newProps.logoEnabled); + id logoEnabled = RNMBXConvertFollyDynamicToId(newViewProps.logoEnabled); if (logoEnabled != nil) { _view.reactLogoEnabled = logoEnabled; } - id logoPosition = RNMBXConvertFollyDynamicToId(newProps.logoPosition); + id logoPosition = RNMBXConvertFollyDynamicToId(newViewProps.logoPosition); if (logoPosition != nil) { _view.reactLogoPosition = logoPosition; } - id compassEnabled = RNMBXConvertFollyDynamicToId(newProps.compassEnabled); + id compassEnabled = RNMBXConvertFollyDynamicToId(newViewProps.compassEnabled); if (compassEnabled != nil) { _view.reactCompassEnabled = compassEnabled; } - id compassFadeWhenNorth = RNMBXConvertFollyDynamicToId(newProps.compassFadeWhenNorth); + id compassFadeWhenNorth = RNMBXConvertFollyDynamicToId(newViewProps.compassFadeWhenNorth); if (compassFadeWhenNorth != nil) { _view.reactCompassFadeWhenNorth = compassFadeWhenNorth; } - id compassPosition = RNMBXConvertFollyDynamicToId(newProps.compassPosition); + id compassPosition = RNMBXConvertFollyDynamicToId(newViewProps.compassPosition); if (compassPosition != nil) { _view.reactCompassPosition = compassPosition; } - id compassViewPosition = RNMBXConvertFollyDynamicToId(newProps.compassViewPosition); + id compassViewPosition = RNMBXConvertFollyDynamicToId(newViewProps.compassViewPosition); if (compassViewPosition != nil) { _view.reactCompassViewPosition = [(NSNumber *)compassViewPosition doubleValue]; } - NSDictionary *compassViewMargins = RNMBXConvertFollyDynamicToId(newProps.compassViewMargins); + NSDictionary *compassViewMargins = RNMBXConvertFollyDynamicToId(newViewProps.compassViewMargins); if (compassViewMargins != nil) { CGPoint margins = CGPointMake([compassViewMargins[@"x"] doubleValue], [compassViewMargins[@"y"] doubleValue]); _view.reactCompassViewMargins = margins; } - id compassImage = RNMBXConvertFollyDynamicToId(newProps.compassImage); + id compassImage = RNMBXConvertFollyDynamicToId(newViewProps.compassImage); if (compassImage != nil) { _view.reactCompassImage = compassImage; } - id scaleBarEnabled = RNMBXConvertFollyDynamicToId(newProps.scaleBarEnabled); + id scaleBarEnabled = RNMBXConvertFollyDynamicToId(newViewProps.scaleBarEnabled); if (scaleBarEnabled != nil) { _view.reactScaleBarEnabled = scaleBarEnabled; } - id scaleBarPosition = RNMBXConvertFollyDynamicToId(newProps.scaleBarPosition); + id scaleBarPosition = RNMBXConvertFollyDynamicToId(newViewProps.scaleBarPosition); if (scaleBarPosition != nil) { _view.reactScaleBarPosition = scaleBarPosition; } - id zoomEnabled = RNMBXConvertFollyDynamicToId(newProps.zoomEnabled); + id zoomEnabled = RNMBXConvertFollyDynamicToId(newViewProps.zoomEnabled); if (zoomEnabled != nil) { _view.reactZoomEnabled = zoomEnabled; } - id scrollEnabled = RNMBXConvertFollyDynamicToId(newProps.scrollEnabled); + id scrollEnabled = RNMBXConvertFollyDynamicToId(newViewProps.scrollEnabled); if (scrollEnabled != nil) { _view.reactScrollEnabled = scrollEnabled; } - id rotateEnabled = RNMBXConvertFollyDynamicToId(newProps.rotateEnabled); + id rotateEnabled = RNMBXConvertFollyDynamicToId(newViewProps.rotateEnabled); if (rotateEnabled != nil) { _view.reactRotateEnabled = rotateEnabled; } - id pitchEnabled = RNMBXConvertFollyDynamicToId(newProps.pitchEnabled); + id pitchEnabled = RNMBXConvertFollyDynamicToId(newViewProps.pitchEnabled); if (pitchEnabled != nil) { _view.reactPitchEnabled = pitchEnabled; } - id projection = RNMBXConvertFollyDynamicToId(newProps.projection); + id projection = RNMBXConvertFollyDynamicToId(newViewProps.projection); if (projection != nil) { _view.reactProjection = projection; } - id localizeLabels = RNMBXConvertFollyDynamicToId(newProps.localizeLabels); + id localizeLabels = RNMBXConvertFollyDynamicToId(newViewProps.localizeLabels); if (localizeLabels != nil) { _view.reactLocalizeLabels = localizeLabels; } + + RNMBX_OPTIONAL_PROP_BOOL(deselectAnnotationOnTap); - id styleURL = RNMBXConvertFollyDynamicToId(newProps.styleURL); + id styleURL = RNMBXConvertFollyDynamicToId(newViewProps.styleURL); if (styleURL != nil) { _view.reactStyleURL = styleURL; } diff --git a/ios/RNMBX/RNMBXMapViewManager.m b/ios/RNMBX/RNMBXMapViewManager.m index 46aa79278..eb9ef54d9 100644 --- a/ios/RNMBX/RNMBXMapViewManager.m +++ b/ios/RNMBX/RNMBXMapViewManager.m @@ -25,6 +25,7 @@ @interface RCT_EXTERN_REMAP_MODULE(RNMBXMapView, RNMBXMapViewManager, RCTViewMan RCT_REMAP_VIEW_PROPERTY(scrollEnabled, reactScrollEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(rotateEnabled, reactRotateEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(pitchEnabled, reactPitchEnabled, BOOL) +RCT_EXPORT_VIEW_PROPERTY(deselectAnnotationOnTap, BOOL) RCT_REMAP_VIEW_PROPERTY(projection, reactProjection, NSString) RCT_REMAP_VIEW_PROPERTY(localizeLabels, reactLocalizeLabels, NSDictionary) diff --git a/ios/RNMBX/RNMBXMarkerViewContentComponentView.mm b/ios/RNMBX/RNMBXMarkerViewContentComponentView.mm index 047eb7bbd..f6ce2f179 100644 --- a/ios/RNMBX/RNMBXMarkerViewContentComponentView.mm +++ b/ios/RNMBX/RNMBXMarkerViewContentComponentView.mm @@ -17,11 +17,25 @@ @interface RNMBXMarkerViewContentComponentView () (); + _props = defaultProps; + _frame = frame; + [self prepareView]; + } + return self; +} + +- (void)prepareView +{ + _view = [[RNMBXMarkerView alloc] initWithFrame:_frame]; + self.contentView = _view; } #pragma mark - RCTComponentViewProtocol diff --git a/src/components/MapView.tsx b/src/components/MapView.tsx index 4d2b59fdf..36e1541f8 100644 --- a/src/components/MapView.tsx +++ b/src/components/MapView.tsx @@ -427,6 +427,12 @@ type Props = ViewProps & { * The emitted frequency of regiondidchange events */ regionDidChangeDebounceTime?: number; + + /** + * Set to true to deselect any selected annotation when the map is tapped. If set to true you will not receive + * the onPress event for the taps that deselect the annotation. Default is false. + */ + deselectAnnotationOnTap?: boolean; }; type CallbablePropKeys = diff --git a/src/specs/RNMBXMapViewNativeComponent.ts b/src/specs/RNMBXMapViewNativeComponent.ts index 09a51f98b..2804a63a7 100644 --- a/src/specs/RNMBXMapViewNativeComponent.ts +++ b/src/specs/RNMBXMapViewNativeComponent.ts @@ -52,6 +52,8 @@ export interface NativeProps extends ViewProps { rotateEnabled?: OptionalProp; pitchEnabled?: OptionalProp; + deselectAnnotationOnTap?: OptionalProp; + requestDisallowInterceptTouchEvent?: OptionalProp; projection?: OptionalProp<'mercator' | 'globe'>;