-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Conversation
I am still working on the tests. The challenge I'm currently running into is with simulating a tap, since the annotations can still be selected programmatically. For the implementation, I just used the same approach we currently take with |
In order to test tapping on an annotation, I will likely need to add UI Testing to either the Integration Test Harness or to cc @friedbunny |
Upon reading the discussion in #12313, I'm going to remove the attempts at UI tests in this PR and add a test for this PR to |
c297443
to
a0822d9
Compare
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.
With this change we will have the following methods that can be used to "enable" annotations callbacks:
Post tap processing- Shape and View based annotations:
[MGLMapViewDelegate mapView:didSelectAnnotation:]
.-[MGLMapViewDelegate mapView:annotationCanShowCallout:]
.
Tap processing:
-[MGLAnnotationImage isEnabled]
.-[MGLShape isEnabled]
Shape and PointAnnotations.
Should we document the appropriate usage for each? Or should we reduce the options? Can we change the level of granularity to enable all or non shape/view based annotations?
platform/ios/src/MGLMapView.mm
Outdated
@@ -4091,6 +4091,10 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) | |||
{ | |||
if ([annotation isKindOfClass:[MGLMultiPoint class]]) | |||
{ | |||
if ( ! ((MGLShape *)annotation).enabled) { |
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.
MGLPointAnnotation
is a form of MGLShape
annotations if we set the enable
property to NO
on a point annotation I would expect that is going to be disabled. This line is never reached in that case.
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.
Thanks for pointing that out!
I'm going to reevaluate where I'm doing the check. There may be issues with cycling through Also, I should allow the |
platform/darwin/src/MGLMultiPoint.mm
Outdated
@@ -20,6 +20,7 @@ - (instancetype)initWithCoordinates:(const CLLocationCoordinate2D *)coords count | |||
[NSException raise:NSInvalidArgumentException | |||
format:@"A multipoint must have at least one vertex."]; | |||
} | |||
self.enabled = YES; |
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.
Since you're in an init
, this would be better as _enabled = YES;
platform/darwin/src/MGLShape.h
Outdated
`NO`, the `MGLShape` object ignores touch events and cannot be selected. | ||
*/ | ||
@property (nonatomic, getter=isEnabled) BOOL enabled; | ||
|
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.
Discussion point for later: I wonder if this property could be part of a protocol that MGLShape
, MGLAnnotationImage
, MGLAnnotationView
should conform to?
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.
That could be interesting. MGLAnnotation
covers two of the three (but not MGLAnnotationImage
as far as I know).
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 important to distinguish between data and presentation: MGLShape, MGLAnnotation, etc. are all about data storage, whereas MGLAnnotationView and MGLAnnotationImage are ways to render that data. The same distinction can be made between MGLSource and MGLStyleLayer.
MGLAnnotationView and MGLAnnotationImage both have a existing enabled
properties. How will the property being added in this PR interact with the existing ones? What if an annotation’s enabled
property disagrees with its view or image’s enabled
property at the time the user taps it?
In #10539 (comment), I suggested that MGLAnnotationImage.enabled
property be deprecated, but to me a property on MGLShape is worse because an aspect of the data’s presentation is defined on the data itself. The problem with MGLAnnotationImage.enabled
is not that it’s on the wrong kind of class but that there’s no way to change the annotation image’s interactivity after it appears on the map for the first time. MGLAnnotationView.enabled
doesn’t suffer from this problem, because you can call -[MGLMapView viewForAnnotation:]
at any time.
platform/ios/src/MGLMapView.mm
Outdated
@@ -4089,8 +4089,12 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) | |||
} | |||
else | |||
{ | |||
if ([annotation isKindOfClass:[MGLMultiPoint class]]) | |||
if ([annotation conformsToProtocol:[MGLShape self]]) |
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
isn't a protocol (unless there's also a protocol with the same name?) - so this check should be a isKindOfClass
test.
However, this relates to my question above - perhaps it is worth having an "interactable annotation" protocol after all?
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.
Ack, I was mixing up MGLShape
and MGLAnnotation
, thank you
This PR needs to target |
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.
Below are some concerns about the direction these changes take the SDK. It seems to me that there must be other avenues to explore. If it would be burdensome for the developer to track the selected annotation themselves in order to achieve custom behavior, then perhaps we could make MGLMapView’s selection functionality more customizable via MGLMapViewDelegate methods. But before we go down that route, I think we should confirm whether -[MGLMapViewDelegate mapView:annotationCanShowCallout:
, as described in #10539 (comment), addresses the present need without adding to the public API.
platform/darwin/src/MGLShape.h
Outdated
The default value of this property is `YES`. If the value of this property is | ||
`NO`, the `MGLShape` object ignores touch events and cannot be selected. | ||
*/ | ||
@property (nonatomic, getter=isEnabled) BOOL enabled; |
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.
Implementing this property on MGLShape prevents the developer from opting custom MGLAnnotation classes out of selection and interactivity. At the same time, it means features that are part of a shape source or feature querying result have a meaningless enabled
property.
platform/darwin/src/MGLShape.h
Outdated
`NO`, the `MGLShape` object ignores touch events and cannot be selected. | ||
*/ | ||
@property (nonatomic, getter=isEnabled) BOOL enabled; | ||
|
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 important to distinguish between data and presentation: MGLShape, MGLAnnotation, etc. are all about data storage, whereas MGLAnnotationView and MGLAnnotationImage are ways to render that data. The same distinction can be made between MGLSource and MGLStyleLayer.
MGLAnnotationView and MGLAnnotationImage both have a existing enabled
properties. How will the property being added in this PR interact with the existing ones? What if an annotation’s enabled
property disagrees with its view or image’s enabled
property at the time the user taps it?
In #10539 (comment), I suggested that MGLAnnotationImage.enabled
property be deprecated, but to me a property on MGLShape is worse because an aspect of the data’s presentation is defined on the data itself. The problem with MGLAnnotationImage.enabled
is not that it’s on the wrong kind of class but that there’s no way to change the annotation image’s interactivity after it appears on the map for the first time. MGLAnnotationView.enabled
doesn’t suffer from this problem, because you can call -[MGLMapView viewForAnnotation:]
at any time.
platform/darwin/src/MGLShape.mm
Outdated
@@ -72,6 +73,7 @@ - (void)encodeWithCoder:(NSCoder *)coder | |||
{ | |||
[coder encodeObject:_title forKey:@"title"]; | |||
[coder encodeObject:_subtitle forKey:@"subtitle"]; | |||
[coder encodeBool:_enabled forKey:@"enabled"]; |
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.
If two MGLShape objects are identical in every way except that one is enabled and one isn’t, the two are considered object-equal according to this PR. Is that correct, or should object equality also consider enabled state? Philosophically, a shape’s coordinates – its form – is intrinsic to the shape. Could the same be said of its interactivity? If so, what about the feature’s cursor (which is currently implemented on macOS as MGLAnnotationImage.cursor
)?
19524ec
to
5ce307f
Compare
54fd60c
to
a5c9e0a
Compare
Changed the previous implementation to what is required in this comment: #10539 (comment) |
platform/ios/CHANGELOG.md
Outdated
@@ -8,6 +8,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT | |||
* When a symbol in an `MGLSymbolStyleLayer` has both an icon and text, both are shown or hidden together based on available space. ([#12521](https://github.com/mapbox/mapbox-gl-native/pull/12521)) | |||
* The `-[MGLMapView visibleFeaturesAtPoint:]` method can now return features near tile boundaries at high zoom levels. ([#12570](https://github.com/mapbox/mapbox-gl-native/pull/12570)) | |||
* Fixed inconsistencies in exception naming. ([#12583](https://github.com/mapbox/mapbox-gl-native/issues/12583)) | |||
* `-[MGLMapViewDelegate mapView:canSelectAnnotation:]` can now specify if an annotation is selectable. ([#12352](https://github.com/mapbox/mapbox-gl-native/pull/12352)) |
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.
Added an
-[MGLMapViewDelegate mapView:canSelectAnnotation:]
method to specify whether an annotation is selectable.
Returns a Boolean value indicating whether the annotation can be selected. | ||
|
||
If the return value is `YES`, or if this method is absent from the delegate, | ||
the annotation will be selected. |
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 don’t think it’s the case that, if this method is unimplemented, the annotation automatically gets selected when it’s added to the map view. It also doesn’t seem to affect whether the developer can select it programmatically via -selectAnnotation:
. Rather, it seems to only affect whether an annotation can be selected by a user gesture:
If the return value is
YES
, the user can select the annotation by tapping [clicking] on it. If the delegate does not implement this method, the default value isYES
.
There’s demand in #10539 for an option to prevent tap-to-select, but no requests, as far as I can tell, for an option to prevent programmatic selection.
platform/ios/src/MGLMapView.mm
Outdated
@@ -4125,6 +4125,11 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) | |||
return true; | |||
} | |||
|
|||
if ([self.delegate respondsToSelector:@selector(mapView:canSelectAnnotation:)] && | |||
![self.delegate mapView:self canSelectAnnotation:annotation]) { |
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.
Should this delegate method apply to all annotations or only shape annotations? Point annotations already have a MGLAnnotationImage.enabled
and MGLAnnotationView.enabled
, so a redundant option without a defined order of precedence could be confusing. The enabled
properties can be more efficient because they’re declarative: application code doesn’t run when MGLMapView needs to check whether a point annotation is interactive.
Consider renaming the method -mapView:shapeAnnotationIsEnabled:
and mentioning the enabled
properties in its documentation. There’s precedent in MGLMapViewDelegate for shape annotation–specific options.
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.
Mostly documentation nits, but otherwise good to go.
platform/ios/CHANGELOG.md
Outdated
@@ -8,7 +8,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT | |||
* When a symbol in an `MGLSymbolStyleLayer` has both an icon and text, both are shown or hidden together based on available space. ([#12521](https://github.com/mapbox/mapbox-gl-native/pull/12521)) | |||
* The `-[MGLMapView visibleFeaturesAtPoint:]` method can now return features near tile boundaries at high zoom levels. ([#12570](https://github.com/mapbox/mapbox-gl-native/pull/12570)) | |||
* Fixed inconsistencies in exception naming. ([#12583](https://github.com/mapbox/mapbox-gl-native/issues/12583)) | |||
* `-[MGLMapViewDelegate mapView:canSelectAnnotation:]` can now specify if an annotation is selectable. ([#12352](https://github.com/mapbox/mapbox-gl-native/pull/12352)) | |||
* Added an -[MGLMapViewDelegate mapView:shapeAnnotationIsEnabled:] method to specify whether an annotation is selectable. ([#12352](https://github.com/mapbox/mapbox-gl-native/pull/12352)) |
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.
Nit: use backticks around -[MGLMapViewDelegate mapView:shapeAnnotationIsEnabled:]
. (×2)
platform/ios/src/MGLMapView.mm
Outdated
@@ -4156,7 +4151,12 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) | |||
{ | |||
if ([annotation isKindOfClass:[MGLMultiPoint class]]) | |||
{ | |||
return false; | |||
if ([self.delegate respondsToSelector:@selector(mapView:shapeAnnotationIsEnabled:)] && |
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.
Not sure if it matters for performance reasons, but for better readability, you could factor this call out of the std::remove_if()
call. (×2)
platform/ios/src/MGLMapView.mm
Outdated
![self.delegate mapView:self shapeAnnotationIsEnabled:(MGLMultiPoint *)annotation]) { | ||
return true; | ||
} else { | ||
return false; |
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.
This is fine, but FYI you can also wrap the conditional in !!
or compare to YES
. (×2)
|
||
If the return value is `YES`, or if this method is absent from the delegate, | ||
the annotation will be selected. | ||
If the return value is YES, the user can select the annotation by tapping [clicking] |
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 “clicking” was meant for the macOS implementation of MGLMapView. iOS documentation should never refer to clicking, while macOS documentation should never refer to tapping. (×2)
If the return value is `YES`, or if this method is absent from the delegate, | ||
the annotation will be selected. | ||
If the return value is YES, the user can select the annotation by tapping [clicking] | ||
on it. If the delegate does not implement this method, the default value is YES. |
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.
Nit: surround YES
in backticks. (×4)
platform/ios/src/MGLMapView.mm
Outdated
@@ -4151,7 +4151,12 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) | |||
{ | |||
if ([annotation isKindOfClass:[MGLMultiPoint class]]) | |||
{ | |||
return false; | |||
if ([self.delegate respondsToSelector:@selector(mapView:shapeAnnotationIsEnabled:)] && |
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.
Minor: You could replace the if
with return (...)
here and below.
8617ca8
to
09a377a
Compare
09a377a
to
995f0b9
Compare
Thanks @fabian-guerra! |
To Do:
enabled
property toMGLShape
.- [x] Account for MGLShapeCollection, which does not inherit fromThis is not necessary.MGLMultiPoint
.enabled
property only applies to annotations, and not style layers.Fixes #10539