-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[ios, macos] Add shapes annotations select/deselect delegates. #9755
[ios, macos] Add shapes annotations select/deselect delegates. #9755
Conversation
@param mapView The map view containing the annotation. | ||
@param shapeAnnotation The shape annotation that was selected. | ||
*/ | ||
- (void)mapView:(MGLMapView *)mapView didSelectShapeAnnotation:(MGLShape *)shapeAnnotation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that the existing methods are called -mapView:didSelectAnnotation:
and -mapView:didDeselectAnnotation:
, without referring to point annotations specifically, it’s interesting that this PR adds separate shape-specific methods.
On the one hand, I understand that suddenly passing shape annotations into a method that has long received only point annotations could be a surprise to some developers. On the other hand, we’ve never documented that prior limitation – it’s essentially a bug. There is precedent for opening up delegate methods to new data, namely when we started passing MGLUserLocation into -mapView:viewForAnnotation:
in #5882.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In #9755 (comment) I mention an alternative
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason behind naming this as didSelectShapeAnnotation
is based on been clear to developers that this is not going to be called by selecting any *MGLPointAnnotation thus there is no callout view.
platform/ios/src/MGLMapView.mm
Outdated
/// Mapping from a shape annotation object to shape layer id. | ||
typedef std::map<id<MGLAnnotation>, std::string> MGLShapeAnnotationObjectLayerIDMap; | ||
|
||
const NSString *MGLLayerIDShapeAnnotation = @"com.mapbox.annotations.shape."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The @""
string literal NSString is already immutable. So this should be a constant pointer. As it stands, you can repoint MGLLayerIDShapeAnnotation
and change its value. Also, this is only used in the implementation so it can be designated static
. (i.e. static NSString *const MGLFoo = @"foo"
)
platform/ios/src/MGLMapView.mm
Outdated
@@ -285,9 +290,11 @@ @implementation MGLMapView | |||
|
|||
MGLAnnotationTagContextMap _annotationContextsByAnnotationTag; | |||
MGLAnnotationObjectTagMap _annotationTagsByAnnotation; | |||
|
|||
MGLShapeAnnotationObjectLayerIDMap _shapeAnnotationLayerIDs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd help readability later on to follow the convention (see _annotationTagsByAnnotation
just above) and call this _shapeAnnotationLayerIDsByAnnotation
if(!annotation) { | ||
return NO; | ||
if(!annotation && !_selectedShapeAnnotation) { | ||
MGLShape *shapeAnnotation = [self shapeAnnotationForGestureRecognizer:(UITapGestureRecognizer*)gestureRecognizer]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tapping a map with no annotations now crashes in -[MGLMapView shapeAnnotationForGestureRecognizer:]
because the hitAnnotationTag
is 0 (instead of MGLAnnotationTagNotFound
) and that gets a nil result from [self annotationWithTag:hitAnnotationTag]
which triggers an assert. I think the root cause is that -[MGLMapView shapeAnnotationTagsInRect:]
has no _shapeAnnotationLayerIDs
values to put in options so the _mbglMap->queryShapeAnnotations()
actually returns some values instead of an empty list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thank you for catching this bug.
@param mapView The map view containing the annotation. | ||
@param shapeAnnotation The shape annotation that was selected. | ||
*/ | ||
- (void)mapView:(MGLMapView *)mapView didSelectShapeAnnotation:(MGLShape *)shapeAnnotation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These new APIs don't provide a shape "annotation". They do provide an MGLShape
. Since they are intended for MGLPolyline(gon)(Feature) instances, should we remove the word "annotation" from the API and use a more specific type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MGLShape conforms to MGLAnnotation.
platform/ios/src/MGLMapView.mm
Outdated
- (MGLShape*)shapeAnnotationForGestureRecognizer:(UITapGestureRecognizer*)singleTap | ||
{ | ||
CGPoint tapPoint = [singleTap locationInView:self]; | ||
// CGRect queryRect = CGRectInset({ tapPoint, CGSizeZero }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These commented lines were pulled into methods. Don't forget to delete them here.
// Choose the first nearby annotation. | ||
if (nearbyAnnotations.size()) | ||
{ | ||
hitAnnotationTag = nearbyAnnotations.front(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The older -[MGLMapView annotationTagAtPoint:persistingResults:] method has logic to cycle through points that re overlapping / nearby each other. Do we need to worry about that for shapes? What is the expected behavior if a user touches in an area where several polygons overlap or several polylines intersect? cc @1ec5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It’s currently possible to disable touch interaction for a given point annotation using either MGLAnnotationImage.enabled
. That’s a weird API, but do we need something like it for shape annotations?
@fabian-guerra wrt comments #9755 (comment) and #9755 (comment), it might be worth another look at the approach taken in #5502. That PR reuses the existing annotation delegate path (and cc @1ec5 |
[self deselectShapeAnnotation:_selectedShapeAnnotation]; | ||
|
||
_selectedShapeAnnotation = shapeAnnotation; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intent of #2082 was that we’d display a callout over the selected shape. Per #2082 (comment), we’d need to constrain the coordinate to the current viewport before calculating the centroid. So coordinate
wouldn’t give us quite the right behavior, but we could fashion a method that takes an MGLCoordinateBounds
and clips the polygon to polyline to that bounds before passing the geometry off to Polylabel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intent of #2082 was that we’d display a callout over the selected shape.
Our Android SDK gained essentially the exact same functionality that is proposed here in #9443. I don't think there is any mechanism in that SDK to assist with callout placement, yet. I think it would be ok to use the same approach and keep #2082 open and handle callouts separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that would be an argument for adding a separate set of delegate methods, because it would be weird for -mapView:didSelectAnnotation:
to work with -mapView:annotationCanShowCallout:
in some cases but not others.
It doesn’t feel great to potentially have a point annotation and shape annotation both be selected at the same time. It’ll lock us into a design that’s reminiscent of MapKit but quite different, even after we implement the callout views.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My principal deterrent to avoid using [MGLMapView annotationTagAtPoint:persistingResults:]
was the fact that *MGLShape
"annotations" does not have a callout view. So selecting an annotation that handles a callout only in specific cases would be confusing for developers and interpreted as a bug.
But selecting a MGLShape
annotation deselects MGLPointAnnotation
and viceversa.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the broader question is whether shapes should have callouts. The original request in #2082 and much of the subsequent comments were about callouts, so I guess I’d consider that PR unfinished without them. If we land this feature without callouts and with an API that’s separate from normal annotation selection, does that make it more difficult for us to implement shape callouts later? Or are we counting on being able to clean up this API in v4.0.0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How much of a lift would it be to support callouts for shape annotations? At a glance, it looks like most of the pieces are already in place (especially polylabel).
4a9edb4
to
4a13933
Compare
Closing in favor of #9984 |
Fixes #2082
The new delegates return a MGLShape annotation. The consideration is for polyllines you get the centroid (coordinate) for the whole line. The initial implementation I made returned a feature and the feature's centroid. I'm thinking add a delegate only for polylines. Thoughts?