summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2015-05-16 14:48:18 -0700
committerMinh Nguyễn <mxn@1ec5.org>2016-04-25 00:24:35 -0700
commitd9a2a845dec1697f6b68b6aa4c4a49b0c2738329 (patch)
tree5a1bf6cbdd642cf7bfdada03e573fc85a4da8487
parent7cc3928a19ffc40e2835264f1349dc7a07fd4017 (diff)
downloadqtlocation-mapboxgl-d9a2a845dec1697f6b68b6aa4c4a49b0c2738329.tar.gz
[ios] Added accessibility labels, hints, gestures
Fixed an issue where the “return to map” accessibility element lacked a label. Always update the title and subtitle of an annotation accessibility element, in case the title or subtitle changes from one showing to the next. Added a hint for annotations. Use declarative rather than imperative for hints. Marked the map view and its annotations as adjustable so that swiping up and down with VoiceOver zooms out and in, respectively. Added accessibility values to toolbar buttons in iosapp.
-rw-r--r--platform/ios/app/MBXViewController.m8
-rw-r--r--platform/ios/app/Main.storyboard15
-rw-r--r--platform/ios/resources/Base.lproj/Localizable.strings5
-rw-r--r--platform/ios/src/MGLMapView.mm89
-rw-r--r--platform/ios/src/MGLUserLocationAnnotationView.m13
5 files changed, 100 insertions, 30 deletions
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 098fc7b744..196601d494 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -518,24 +518,30 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
[titleButton setTitle:styleNames[self.styleIndex] forState:UIControlStateNormal];
}
-- (IBAction)locateUser:(__unused id)sender
+- (IBAction)locateUser:(id)sender
{
MGLUserTrackingMode nextMode;
+ NSString *nextAccessibilityValue;
switch (self.mapView.userTrackingMode) {
case MGLUserTrackingModeNone:
nextMode = MGLUserTrackingModeFollow;
+ nextAccessibilityValue = @"Follow location";
break;
case MGLUserTrackingModeFollow:
nextMode = MGLUserTrackingModeFollowWithHeading;
+ nextAccessibilityValue = @"Follow location and heading";
break;
case MGLUserTrackingModeFollowWithHeading:
nextMode = MGLUserTrackingModeFollowWithCourse;
+ nextAccessibilityValue = @"Follow course";
break;
case MGLUserTrackingModeFollowWithCourse:
nextMode = MGLUserTrackingModeNone;
+ nextAccessibilityValue = @"Off";
break;
}
self.mapView.userTrackingMode = nextMode;
+ [sender setAccessibilityValue:nextAccessibilityValue];
}
- (IBAction)startWorldTour:(__unused id)sender
diff --git a/platform/ios/app/Main.storyboard b/platform/ios/app/Main.storyboard
index 72f9a02219..1190070d8e 100644
--- a/platform/ios/app/Main.storyboard
+++ b/platform/ios/app/Main.storyboard
@@ -38,7 +38,10 @@
</view>
<navigationItem key="navigationItem" id="p8W-eP-el5">
<nil key="title"/>
- <barButtonItem key="leftBarButtonItem" image="settings.png" id="Jw8-JP-CaZ">
+ <barButtonItem key="leftBarButtonItem" image="settings.png" id="Jw8-JP-CaZ" userLabel="Map Settings">
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="string" keyPath="accessibilityLabel" value="Map settings"/>
+ </userDefinedRuntimeAttributes>
<connections>
<action selector="showSettings:" destination="WaX-pd-UZQ" id="X2C-Ee-Qvt"/>
</connections>
@@ -53,12 +56,18 @@
</connections>
</button>
<rightBarButtonItems>
- <barButtonItem image="TrackingLocationOffMask.png" id="CQ1-GP-M6x">
+ <barButtonItem image="TrackingLocationOffMask.png" id="CQ1-GP-M6x" userLabel="User Tracking Mode">
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="string" keyPath="accessibilityLabel" value="User tracking mode"/>
+ </userDefinedRuntimeAttributes>
<connections>
<action selector="locateUser:" destination="WaX-pd-UZQ" id="XgF-DB-z3f"/>
</connections>
</barButtonItem>
- <barButtonItem systemItem="organize" id="5IK-vz-jKQ">
+ <barButtonItem systemItem="organize" id="5IK-vz-jKQ" userLabel="Offline Packs">
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="string" keyPath="accessibilityLabel" value="Offline packs"/>
+ </userDefinedRuntimeAttributes>
<connections>
<segue destination="7q0-lI-zqb" kind="show" identifier="ShowOfflinePacks" id="xjx-0t-0LD"/>
</connections>
diff --git a/platform/ios/resources/Base.lproj/Localizable.strings b/platform/ios/resources/Base.lproj/Localizable.strings
index 04cc667998..d447568072 100644
--- a/platform/ios/resources/Base.lproj/Localizable.strings
+++ b/platform/ios/resources/Base.lproj/Localizable.strings
@@ -1,3 +1,6 @@
+/* Accessibility hint */
+"ANNOTATION_A11Y_HINT" = "Shows more info";
+
/* No comment provided by engineer. */
"API_CLIENT_400_DESC" = "The session data task failed. Original request was: %@";
@@ -29,7 +32,7 @@
"FIRST_STEPS_URL" = "mapbox.com/help/first-steps-ios-sdk";
/* Accessibility hint */
-"INFO_A11Y_HINT" = "Access credits, a feedback form, and more";
+"INFO_A11Y_HINT" = "Shows credits, a feedback form, and more";
/* Accessibility label */
"INFO_A11Y_LABEL" = "About this map";
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index f6a0cb4904..f2107eb3c9 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -133,10 +133,32 @@ mbgl::Color MGLColorObjectFromUIColor(UIColor *color)
@property (nonatomic) MGLAnnotationTag tag;
+- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)identifier NS_DESIGNATED_INITIALIZER;
+
@end
@implementation MGLAnnotationAccessibilityElement
+- (instancetype)initWithAccessibilityContainer:(id)container tag:(MGLAnnotationTag)tag
+{
+ if (self = [super initWithAccessibilityContainer:container])
+ {
+ _tag = tag;
+ self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitAdjustable;
+ }
+ return self;
+}
+
+- (void)accessibilityIncrement
+{
+ [self.accessibilityContainer accessibilityIncrement];
+}
+
+- (void)accessibilityDecrement
+{
+ [self.accessibilityContainer accessibilityDecrement];
+}
+
@end
/// Lightweight container for metadata about an annotation, including the annotation itself.
@@ -160,7 +182,7 @@ public:
if (self = [super initWithAccessibilityContainer:container])
{
self.accessibilityTraits = UIAccessibilityTraitButton;
- self.accessibilityLabel = self.accessibilityLabel;
+ self.accessibilityLabel = [self.accessibilityContainer accessibilityLabel];
self.accessibilityHint = @"Returns to the map";
}
return self;
@@ -207,7 +229,7 @@ public:
@property (nonatomic) CGFloat quickZoomStart;
@property (nonatomic, getter=isDormant) BOOL dormant;
@property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed;
-@property (nonatomic) UIAccessibilityElement *mapViewProxyAccessibilityElement;
+@property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement;
@end
@@ -341,7 +363,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
//
// self.isAccessibilityElement = YES;
self.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"MAP_A11Y_LABEL", nil, nil, @"Map", @"Accessibility label");
- self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;
+ self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction | UIAccessibilityTraitAdjustable;
_accessibilityCompassFormatter = [[MGLCompassDirectionFormatter alloc] init];
_accessibilityCompassFormatter.unitStyle = NSFormattingUnitStyleLong;
@@ -398,7 +420,7 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
//
_attributionButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
_attributionButton.accessibilityLabel = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_LABEL", nil, nil, @"About this map", @"Accessibility label");
- _attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Access credits, a feedback form, and more", @"Accessibility hint");
+ _attributionButton.accessibilityHint = NSLocalizedStringWithDefaultValue(@"INFO_A11Y_HINT", nil, nil, @"Shows credits, a feedback form, and more", @"Accessibility hint");
[_attributionButton addTarget:self action:@selector(showAttribution) forControlEvents:UIControlEventTouchUpInside];
_attributionButton.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_attributionButton];
@@ -1922,32 +1944,30 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
NSAssert(_annotationContextsByAnnotationTag.count(annotationTag), @"Missing annotation for tag %u.", annotationTag);
MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
id <MGLAnnotation> annotation = annotationContext.annotation;
- MGLAnnotationAccessibilityElement *element = annotationContext.accessibilityElement;
// Lazily create an accessibility element for the found annotation.
- if ( ! element)
+ if ( ! annotationContext.accessibilityElement)
{
- element = [[MGLAnnotationAccessibilityElement alloc] initWithAccessibilityContainer:self];
- element.tag = annotationTag;
- element.accessibilityTraits = UIAccessibilityTraitButton;
- if ([annotation respondsToSelector:@selector(title)])
- {
- element.accessibilityLabel = annotation.title;
- }
- if ([annotation respondsToSelector:@selector(subtitle)])
- {
- element.accessibilityValue = annotation.subtitle;
- }
- annotationContext.accessibilityElement = element;
+ annotationContext.accessibilityElement = [[MGLAnnotationAccessibilityElement alloc] initWithAccessibilityContainer:self tag:annotationTag];
}
- // Update the accessibility element’s frame.
+ // Update the accessibility element.
MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag];
CGRect annotationFrame = [self frameOfImage:annotationImage.image centeredAtCoordinate:annotation.coordinate];
CGRect screenRect = UIAccessibilityConvertFrameToScreenCoordinates(annotationFrame, self);
- element.accessibilityFrame = screenRect;
+ annotationContext.accessibilityElement.accessibilityFrame = screenRect;
+ annotationContext.accessibilityElement.accessibilityHint = NSLocalizedStringWithDefaultValue(@"ANNOTATION_A11Y_HINT", nil, nil, @"Shows more info", @"Accessibility hint");
- return element;
+ if ([annotation respondsToSelector:@selector(title)])
+ {
+ annotationContext.accessibilityElement.accessibilityLabel = annotation.title;
+ }
+ if ([annotation respondsToSelector:@selector(subtitle)])
+ {
+ annotationContext.accessibilityElement.accessibilityValue = annotation.subtitle;
+ }
+
+ return annotationContext.accessibilityElement;
}
- (NSInteger)indexOfAccessibilityElement:(id)element
@@ -1983,15 +2003,38 @@ mbgl::Duration MGLDurationInSeconds(NSTimeInterval duration)
else return std::distance(visibleAnnotations.begin(), foundElement) + 2 /* compass, userLocationAnnotationView */;
}
-- (UIAccessibilityElement *)mapViewProxyAccessibilityElement
+- (MGLMapViewProxyAccessibilityElement *)mapViewProxyAccessibilityElement
{
if ( ! _mapViewProxyAccessibilityElement)
{
- _mapViewProxyAccessibilityElement = [[MGLAnnotationAccessibilityElement alloc] initWithAccessibilityContainer:self];
+ _mapViewProxyAccessibilityElement = [[MGLMapViewProxyAccessibilityElement alloc] initWithAccessibilityContainer:self];
}
return _mapViewProxyAccessibilityElement;
}
+- (void)accessibilityIncrement
+{
+ // Swipe up to zoom out.
+ [self accessibilityScaleBy:0.5];
+}
+
+- (void)accessibilityDecrement
+{
+ // Swipe down to zoom in.
+ [self accessibilityScaleBy:2];
+}
+
+- (void)accessibilityScaleBy:(double)scaleFactor
+{
+ CGPoint centerPoint = self.contentCenter;
+ if (self.userTrackingMode != MGLUserTrackingModeNone)
+ {
+ centerPoint = self.userLocationAnnotationViewCenter;
+ }
+ _mbglMap->scaleBy(scaleFactor, mbgl::ScreenCoordinate { centerPoint.x, centerPoint.y });
+ [self unrotateIfNeededForGesture];
+}
+
#pragma mark - Geography -
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCenterCoordinate
diff --git a/platform/ios/src/MGLUserLocationAnnotationView.m b/platform/ios/src/MGLUserLocationAnnotationView.m
index d4f4a23fbd..98b0c87bd2 100644
--- a/platform/ios/src/MGLUserLocationAnnotationView.m
+++ b/platform/ios/src/MGLUserLocationAnnotationView.m
@@ -57,8 +57,7 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
self.annotation = [[MGLUserLocation alloc] initWithMapView:mapView];
_mapView = mapView;
[self setupLayers];
- self.isAccessibilityElement = YES;
- self.accessibilityTraits = UIAccessibilityTraitButton;
+ self.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitAdjustable;
_accessibilityCoordinateFormatter = [[MGLCoordinateFormatter alloc] init];
_accessibilityCoordinateFormatter.unitStyle = NSFormattingUnitStyleLong;
@@ -108,6 +107,16 @@ const CGFloat MGLUserLocationAnnotationArrowSize = MGLUserLocationAnnotationPuck
return [UIBezierPath bezierPathWithOvalInRect:self.frame];
}
+- (void)accessibilityIncrement
+{
+ [self.mapView accessibilityIncrement];
+}
+
+- (void)accessibilityDecrement
+{
+ [self.mapView accessibilityDecrement];
+}
+
- (void)setTintColor:(UIColor *)tintColor
{
if (_puckModeActivated)