@@ -61,6 +61,8 @@ @interface ApptentiveSurveyViewController ()
61
61
@property (assign , nonatomic ) CGFloat toolbarInset;
62
62
@property (assign , nonatomic ) BOOL keyboardVisible;
63
63
64
+ @property (assign , nonatomic ) BOOL shouldPostAccessibiltyNotificationOnScrollViewDidEndScrollingAnimation;
65
+
64
66
@end
65
67
66
68
@@ -177,15 +179,28 @@ - (IBAction)submit:(id)sender {
177
179
HUD.imageView .image = [ApptentiveUtilities imageNamed: @" at_thanks" ];
178
180
}
179
181
} else {
180
- NSIndexPath *firstInvalidQuestionIndex = self.viewModel .firstInvalidAnswerIndexPath ;
181
- ApptentiveAssertNotNil (firstInvalidQuestionIndex, @" Expected non-nil index" );
182
- if (firstInvalidQuestionIndex) {
183
- [self .collectionView scrollToItemAtIndexPath: firstInvalidQuestionIndex atScrollPosition: UICollectionViewScrollPositionCenteredVertically animated: YES ];
184
- UIAccessibilityPostNotification (UIAccessibilityAnnouncementNotification, [self .viewModel errorMessageAtIndex: firstInvalidQuestionIndex.section]);
182
+ NSIndexPath *firstInvalidQuestionIndexPath = self.viewModel .firstInvalidAnswerIndexPath ;
183
+ ApptentiveAssertNotNil (firstInvalidQuestionIndexPath, @" Expected non-nil index" );
184
+ if (firstInvalidQuestionIndexPath) {
185
+ // Defer moving VoiceOver focus to first invalid question until after scrolling completes
186
+ self.shouldPostAccessibiltyNotificationOnScrollViewDidEndScrollingAnimation = YES ;
187
+
188
+ [self .collectionView scrollToItemAtIndexPath: firstInvalidQuestionIndexPath atScrollPosition: UICollectionViewScrollPositionCenteredVertically animated: YES ];
185
189
}
186
190
}
187
191
}
188
192
193
+ - (void )scrollViewDidEndScrollingAnimation : (UIScrollView *)scrollView {
194
+ if (self.shouldPostAccessibiltyNotificationOnScrollViewDidEndScrollingAnimation ) {
195
+ NSIndexPath *firstInvalidQuestionIndexPath = self.viewModel .firstInvalidAnswerIndexPath ;
196
+
197
+ UIView *firstInvalidQuestionView = [self .collectionView supplementaryViewForElementKind: UICollectionElementKindSectionHeader atIndexPath: firstInvalidQuestionIndexPath];
198
+ UIAccessibilityPostNotification (UIAccessibilityLayoutChangedNotification, firstInvalidQuestionView);
199
+
200
+ self.shouldPostAccessibiltyNotificationOnScrollViewDidEndScrollingAnimation = NO ;
201
+ }
202
+ }
203
+
189
204
- (IBAction )close : (id )sender {
190
205
UIViewController *presentingViewController = self.presentingViewController ;
191
206
[self dismissViewControllerAnimated: YES
@@ -238,6 +253,7 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
238
253
cell.textView .text = [self .viewModel textOfAnswerAtIndexPath: indexPath];
239
254
cell.placeholderLabel .attributedText = [self .viewModel placeholderTextOfAnswerAtIndexPath: indexPath];
240
255
cell.placeholderLabel .hidden = cell.textView .text .length > 0 ;
256
+ cell.placeholderLabel .isAccessibilityElement = NO ;
241
257
cell.textView .delegate = self;
242
258
cell.textView .tag = [self .viewModel textFieldTagForIndexPath: indexPath];
243
259
cell.textView .accessibilityLabel = cell.placeholderLabel .text ;
@@ -254,7 +270,6 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
254
270
cell.textField .delegate = self;
255
271
cell.textField .tag = [self .viewModel textFieldTagForIndexPath: indexPath];
256
272
cell.textField .font = [self .viewModel.styleSheet fontForStyle: ApptentiveTextStyleTextInput];
257
- cell.textField .accessibilityLabel = cell.textField .placeholder ;
258
273
cell.textField .textColor = [self .viewModel.styleSheet colorForStyle: ApptentiveTextStyleTextInput];
259
274
260
275
return cell;
@@ -263,30 +278,33 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
263
278
case ATSurveyQuestionTypeSingleSelect:
264
279
case ATSurveyQuestionTypeMultipleSelect: {
265
280
NSString *reuseIdentifier, *buttonImageName, *detailText;
266
- NSString *accessibilityHintDetails = nil ;
281
+ NSString *accessibilityLabel = nil ;
282
+ NSString *accessibilityHint = nil ;
267
283
268
284
switch ([self .viewModel typeOfQuestionAtIndex: indexPath.section]) {
269
285
case ATSurveyQuestionTypeRange:
270
286
if (indexPath.item == 0 ) {
271
287
reuseIdentifier = @" RangeMinimum" ;
272
288
detailText = [self .viewModel minimumLabelForQuestionAtIndex: indexPath.section];
273
- accessibilityHintDetails = [self .viewModel minimumLabelForQuestionAtIndex: indexPath.section];
274
289
} else if (indexPath.item == [self .viewModel numberOfAnswersForQuestionAtIndex: indexPath.section] - 1 ) {
275
290
reuseIdentifier = @" RangeMaximum" ;
276
291
detailText = [self .viewModel maximumLabelForQuestionAtIndex: indexPath.section];
277
- accessibilityHintDetails = [self .viewModel maximumLabelForQuestionAtIndex: indexPath.section];
278
292
} else {
279
293
reuseIdentifier = @" Range" ;
280
294
}
281
295
buttonImageName = @" at_circle" ;
296
+ accessibilityLabel = [self .viewModel rangeOptionAccessibilityLabelForQuestionAtIndexPath: indexPath];
297
+ accessibilityHint = [self .viewModel rangeOptionAccessibilityHintForQuestionAtIndexPath: indexPath];
282
298
break ;
283
299
case ATSurveyQuestionTypeMultipleSelect:
284
300
reuseIdentifier = @" Checkbox" ;
285
301
buttonImageName = @" at_checkmark" ;
302
+ accessibilityLabel = [self .viewModel textOfChoiceAtIndexPath: indexPath];
286
303
break ;
287
304
default :
288
305
reuseIdentifier = @" Radio" ;
289
306
buttonImageName = @" at_circle" ;
307
+ accessibilityLabel = [self .viewModel textOfChoiceAtIndexPath: indexPath];
290
308
break ;
291
309
}
292
310
@@ -306,17 +324,11 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell
306
324
cell.detailTextLabel .text = detailText;
307
325
cell.detailTextLabel .font = [self .viewModel.styleSheet fontForStyle: ApptentiveTextStyleSurveyInstructions];
308
326
cell.detailTextLabel .textColor = [self .viewModel.styleSheet colorForStyle: ApptentiveTextStyleSurveyInstructions];
309
-
310
- if (detailText) {
311
- cell.accessibilityHint = detailText;
312
- }
313
-
314
- if (accessibilityHintDetails.length > 0 ) {
315
- cell.accessibilityLabel = [NSString stringWithFormat: @" %@ , %@ " , accessibilityHintDetails, [self .viewModel textOfChoiceAtIndexPath: indexPath]];
316
- } else {
317
- cell.accessibilityLabel = [self .viewModel textOfChoiceAtIndexPath: indexPath];
318
- }
327
+
328
+ cell.accessibilityLabel = accessibilityLabel;
329
+ cell.accessibilityHint = accessibilityHint;
319
330
cell.accessibilityTraits |= UIAccessibilityTraitButton;
331
+
320
332
cell.button .image = buttonImage;
321
333
cell.button .highlightedImage = highlightedButtonImage;
322
334
[cell.button sizeToFit ];
@@ -352,12 +364,15 @@ - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
352
364
view.textLabel .text = [self .viewModel textOfQuestionAtIndex: indexPath.section];
353
365
view.textLabel .font = [self .viewModel.styleSheet fontForStyle: UIFontTextStyleBody];
354
366
view.textLabel .textColor = [self .viewModel.styleSheet colorForStyle: UIFontTextStyleBody];
355
- view.textLabel .accessibilityHint = [self .viewModel accessibilityHintForQuestionAtIndexPath: indexPath];
356
367
view.instructionsTextLabel .attributedText = [self .viewModel instructionTextOfQuestionAtIndex: indexPath.section];
357
368
view.instructionsTextLabel .font = [self .viewModel.styleSheet fontForStyle: ApptentiveTextStyleSurveyInstructions];
358
369
359
370
view.separatorView .backgroundColor = [self .viewModel.styleSheet colorForStyle: ApptentiveColorSeparator];
360
371
372
+ view.isAccessibilityElement = YES ;
373
+ view.accessibilityLabel = [self .viewModel accessibilityLabelForQuestionAtIndexPath: indexPath];
374
+ view.accessibilityHint = [self .viewModel accessibilityHintForQuestionAtIndexPath: indexPath];
375
+
361
376
return view;
362
377
} else {
363
378
ApptentiveSurveyQuestionFooterView *view = [collectionView dequeueReusableSupplementaryViewOfKind: kind withReuseIdentifier: @" Footer" forIndexPath: indexPath];
@@ -611,7 +626,7 @@ - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRan
611
626
return NO ;
612
627
}
613
628
614
- if ([UIApplication.sharedApplication canOpenURL: URL]) {
629
+ if ([UIApplication.sharedApplication canOpenURL: URL]) {
615
630
[ApptentiveURLOpener openURL: URL completionHandler: NULL ];
616
631
}
617
632
@@ -652,7 +667,7 @@ - (void)presentationControllerDidDismiss:(UIPresentationController *)presentatio
652
667
#pragma mark - View model delegate
653
668
654
669
- (void )viewModelValidationChanged : (ApptentiveSurveyViewModel *)viewModel isValid : (BOOL )valid {
655
- [self .collectionViewLayout invalidateLayout ];
670
+ [self .collectionView reloadData ];
656
671
657
672
[self setToolbarHidden: valid];
658
673
0 commit comments