Skip to content

Commit f6d3a8c

Browse files
authored
Better VoiceOver experience for in-app messages (#7445)
* Better accessibility labels for FIAM images and close buttons * At FIAM display time, announce that the message has appeared in VoiceOver mode * In VoiceOver mode, announce the close button last. This is a better accessibility experience for finding the close button. * For modal messages, include the action button in accessibility elements. Exclude the image view if there's no image. * Update CHANGELOG * Include campaign name in accessibility label for images * Fix failing modal message UI tests
1 parent d2469ba commit f6d3a8c

File tree

6 files changed

+50
-7
lines changed

6 files changed

+50
-7
lines changed

FirebaseInAppMessaging/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# 2021-2 -- v7.7.0
1+
# 2021-2 -- v.7.7.0
2+
- [fixed] Fixed accessibility experience for in-app messages (#7445).
23
- [fixed] Fixed conversion tracking for in-app messages with a conversion event but not a button / action URL (#7306).
34

45
# 2021-1 -- v7.5.0

FirebaseInAppMessaging/Resources/FIRInAppMessageDisplayStoryboard.storyboard

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rA8-LH-EYm">
2323
<rect key="frame" x="44" y="59" width="294" height="222"/>
2424
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
25-
<accessibility key="accessibilityConfiguration" identifier="image-view-in-image-only-view">
25+
<accessibility key="accessibilityConfiguration" identifier="image-view-in-image-only-view" label="In-app message image">
2626
<bool key="isElement" value="YES"/>
2727
</accessibility>
2828
</imageView>
2929
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vYW-0X-e5S">
3030
<rect key="frame" x="313" y="42" width="40" height="40"/>
3131
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
32-
<accessibility key="accessibilityConfiguration" identifier="close-button"/>
32+
<accessibility key="accessibilityConfiguration" identifier="close-button" label="Close button"/>
3333
<state key="normal" backgroundImage="close-with-transparency.png"/>
3434
<connections>
3535
<action selector="closeButtonClicked:" destination="lGH-bl-7Xw" eventType="touchUpInside" id="lpP-J3-1Jc"/>
@@ -62,7 +62,7 @@
6262
<subviews>
6363
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="VfB-vw-7up">
6464
<rect key="frame" x="5" y="47" width="60" height="60"/>
65-
<accessibility key="accessibilityConfiguration" identifier="banner-image-view">
65+
<accessibility key="accessibilityConfiguration" identifier="banner-image-view" label="In-app message image">
6666
<bool key="isElement" value="YES"/>
6767
</accessibility>
6868
<constraints>
@@ -144,7 +144,7 @@
144144
</label>
145145
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="700" translatesAutoresizingMaskIntoConstraints="NO" id="c7x-Lf-M58">
146146
<rect key="frame" x="24" y="82.000000000000043" width="306" height="230.66666666666663"/>
147-
<accessibility key="accessibilityConfiguration" identifier="modal-image-view">
147+
<accessibility key="accessibilityConfiguration" identifier="modal-image-view" label="In-app message image">
148148
<bool key="isElement" value="YES"/>
149149
</accessibility>
150150
<constraints>
@@ -305,7 +305,7 @@
305305
</view>
306306
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="akA-AE-VPc">
307307
<rect key="frame" x="364" y="167.66666666666666" width="40" height="40"/>
308-
<accessibility key="accessibilityConfiguration" identifier="close-button"/>
308+
<accessibility key="accessibilityConfiguration" identifier="close-button" label="Close button"/>
309309
<constraints>
310310
<constraint firstAttribute="width" constant="40" identifier="Close button width = 40" id="Dwi-In-x47"/>
311311
<constraint firstAttribute="height" constant="40" identifier="Close button height = 40" id="tIe-pH-jRO"/>
@@ -392,7 +392,7 @@
392392
<subviews>
393393
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="gB0-8W-G9z">
394394
<rect key="frame" x="0.0" y="0.0" width="240" height="160"/>
395-
<accessibility key="accessibilityConfiguration" identifier="card-image-view">
395+
<accessibility key="accessibilityConfiguration" identifier="card-image-view" label="In-app message image">
396396
<bool key="isElement" value="YES"/>
397397
</accessibility>
398398
<constraints>

FirebaseInAppMessaging/Sources/DefaultUI/Banner/FIRIAMBannerViewController.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ - (void)viewDidLoad {
142142
}
143143
}
144144
self.imageView.image = image;
145+
self.imageView.accessibilityLabel = self.inAppMessage.campaignInfo.campaignName;
145146
} else {
146147
// Hide image and remove the bottom constraint between body label and image view.
147148
self.imageViewWidthConstraint.constant = 0;
@@ -286,6 +287,9 @@ - (void)viewDidAppear:(BOOL)animated {
286287
self.view.center = normalCenterPoint;
287288
}
288289
completion:nil];
290+
291+
// Announce via VoiceOver that the banner has appeared. Highlight the title label.
292+
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.titleLabel);
289293
}
290294

291295
- (void)setupAutoDismissTimer {

FirebaseInAppMessaging/Sources/DefaultUI/Card/FIRIAMCardViewController.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ - (void)viewDidLoad {
111111
self.bodyTextView.text = self.cardDisplayMessage.body;
112112
self.bodyTextView.textColor = self.cardDisplayMessage.textColor;
113113

114+
self.imageView.accessibilityLabel = self.inAppMessage.campaignInfo.campaignName;
115+
114116
[self.primaryActionButton setTitle:self.cardDisplayMessage.primaryActionButton.buttonText
115117
forState:UIControlStateNormal];
116118
[self.primaryActionButton
@@ -148,6 +150,13 @@ - (void)viewDidLayoutSubviews {
148150
[self.textAreaScrollView setContentOffset:CGPointZero];
149151
}
150152

153+
- (void)viewDidAppear:(BOOL)animated {
154+
[super viewDidAppear:animated];
155+
156+
// Announce via VoiceOver that the card message has appeared. Highlight the title label.
157+
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.titleLabel);
158+
}
159+
151160
@end
152161

153162
#endif // TARGET_OS_IOS

FirebaseInAppMessaging/Sources/DefaultUI/ImageOnly/FIRIAMImageOnlyViewController.m

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,17 @@ - (void)viewDidLoad {
8989
[super viewDidLoad];
9090
[self.view setBackgroundColor:[UIColor.grayColor colorWithAlphaComponent:0.5]];
9191

92+
// Close button should be announced last for better VoiceOver experience.
93+
self.view.accessibilityElements = @[ self.imageView, self.closeButton ];
94+
9295
if (self.imageOnlyMessage.imageData) {
9396
UIImage *image = [UIImage imageWithData:self.imageOnlyMessage.imageData.imageRawData];
9497
self.imageOriginalSize = image.size;
9598
[self.imageView setImage:image];
9699
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
100+
self.imageView.accessibilityLabel = self.inAppMessage.campaignInfo.campaignName;
101+
} else {
102+
self.imageView.isAccessibilityElement = NO;
97103
}
98104

99105
[self setupRecognizers];
@@ -176,6 +182,13 @@ - (void)viewWillAppear:(BOOL)animated {
176182
}
177183
}
178184

185+
- (void)viewDidAppear:(BOOL)animated {
186+
[super viewDidAppear:animated];
187+
188+
// Announce via VoiceOver that the image-only message has appeared. Highlight the image.
189+
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.imageView);
190+
}
191+
179192
- (void)flashCloseButton:(UIButton *)closeButton {
180193
closeButton.alpha = 1.0f;
181194
[UIView animateWithDuration:2.0

FirebaseInAppMessaging/Sources/DefaultUI/Modal/FIRIAMModalViewController.m

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ - (void)viewDidLoad {
130130
[self.imageView
131131
setImage:[UIImage imageWithData:self.modalDisplayMessage.imageData.imageRawData]];
132132
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
133+
self.imageView.accessibilityLabel = self.inAppMessage.campaignInfo.campaignName;
134+
} else {
135+
self.imageView.isAccessibilityElement = NO;
133136
}
134137

135138
self.messageCardView.backgroundColor = self.modalDisplayMessage.displayBackgroundColor;
@@ -163,6 +166,12 @@ - (void)viewDidLoad {
163166
[self.view addConstraint:self.imageActualHeightConstraint];
164167
self.imageActualHeightConstraint.active = YES;
165168
self.fixedMessageCardHeightConstraint.active = NO;
169+
170+
// Close button should be announced last for better VoiceOver experience.
171+
self.view.accessibilityElements = @[
172+
self.titleLabel, self.imageView, self.bodyTextView, self.actionButton, self.closeButton,
173+
self.messageCardView
174+
];
166175
}
167176

168177
// for text display UIview, which could be a UILabel or UITextView, decide the fit height under a
@@ -456,6 +465,13 @@ - (void)viewWillAppear:(BOOL)animated {
456465
}
457466
}
458467

468+
- (void)viewDidAppear:(BOOL)animated {
469+
[super viewDidAppear:animated];
470+
471+
// Announce via VoiceOver that the modal message has appeared. Highlight the title label.
472+
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.titleLabel);
473+
}
474+
459475
- (void)flashCloseButton:(UIButton *)closeButton {
460476
closeButton.alpha = 1.0f;
461477
[UIView animateWithDuration:2.0

0 commit comments

Comments
 (0)