diff options
author | Minh Nguyễn <mxn@1ec5.org> | 2015-05-16 14:48:18 -0700 |
---|---|---|
committer | Minh Nguyễn <mxn@1ec5.org> | 2016-04-25 00:24:35 -0700 |
commit | d9a2a845dec1697f6b68b6aa4c4a49b0c2738329 (patch) | |
tree | 5a1bf6cbdd642cf7bfdada03e573fc85a4da8487 | |
parent | 7cc3928a19ffc40e2835264f1349dc7a07fd4017 (diff) | |
download | qtlocation-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.m | 8 | ||||
-rw-r--r-- | platform/ios/app/Main.storyboard | 15 | ||||
-rw-r--r-- | platform/ios/resources/Base.lproj/Localizable.strings | 5 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 89 | ||||
-rw-r--r-- | platform/ios/src/MGLUserLocationAnnotationView.m | 13 |
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) |