diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h index 9f71eeccb1a35..0e3b09f8e059f 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h @@ -179,6 +179,14 @@ FLUTTER_DARWIN_EXPORT */ - (id)pluginRegistry; +/** + * A wrapper around UIAccessibilityIsVoiceOverRunning(). + * + * As a C function, UIAccessibilityIsVoiceOverRunning() cannot be mocked in testing. Mock + * this class method to testing features depends on UIAccessibilityIsVoiceOverRunning(). + */ ++ (BOOL)isUIAccessibilityIsVoiceOverRunning; + /** * True if at least one frame has rendered and the ViewController has appeared. * diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 63a8c7515baec..a324e881c3d17 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -826,7 +826,9 @@ - (void)applicationBecameActive:(NSNotification*)notification { - (void)applicationWillResignActive:(NSNotification*)notification { TRACE_EVENT0("flutter", "applicationWillResignActive"); - self.view.accessibilityElementsHidden = YES; + if ([FlutterViewController isUIAccessibilityIsVoiceOverRunning]) { + self.view.accessibilityElementsHidden = YES; + } [self goToApplicationLifecycle:@"AppLifecycleState.inactive"]; } @@ -1718,6 +1720,10 @@ - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { return _engine; } ++ (BOOL)isUIAccessibilityIsVoiceOverRunning { + return UIAccessibilityIsVoiceOverRunning(); +} + #pragma mark - FlutterPluginRegistry - (NSObject*)registrarForPlugin:(NSString*)pluginKey { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index 24d194fa86971..41cab5ba87148 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -939,6 +939,9 @@ - (void)testHideA11yElements { FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil]; + id flutterViewControllerClassMOCK = OCMClassMock([FlutterViewController class]); + [[[flutterViewControllerClassMOCK stub] andReturnValue:@YES] isUIAccessibilityIsVoiceOverRunning]; + XCTAssertFalse(realVC.view.accessibilityElementsHidden); [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationWillResignActiveNotification @@ -949,6 +952,32 @@ - (void)testHideA11yElements { object:nil]; XCTAssertFalse(realVC.view.accessibilityElementsHidden); engine.viewController = nil; + + [flutterViewControllerClassMOCK stopMocking]; +} + +- (void)testDontHideA11yElementsWhenVoiceOverIsOff { + FlutterDartProject* project = [[FlutterDartProject alloc] init]; + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; + [engine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + id flutterViewControllerClassMOCK = OCMClassMock([FlutterViewController class]); + [[[flutterViewControllerClassMOCK stub] andReturnValue:@NO] isUIAccessibilityIsVoiceOverRunning]; + + XCTAssertFalse(realVC.view.accessibilityElementsHidden); + [[NSNotificationCenter defaultCenter] + postNotificationName:UIApplicationWillResignActiveNotification + object:nil]; + XCTAssertFalse(realVC.view.accessibilityElementsHidden); + [[NSNotificationCenter defaultCenter] + postNotificationName:UIApplicationDidBecomeActiveNotification + object:nil]; + XCTAssertFalse(realVC.view.accessibilityElementsHidden); + engine.viewController = nil; + + [flutterViewControllerClassMOCK stopMocking]; } - (void)testNotifyLowMemory {