diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h index f7db838f61..812c69da4f 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h @@ -55,7 +55,7 @@ class AccessibilityBridge final { private: SemanticsObject* GetOrCreateObject(int32_t id); void VisitObjectsRecursively(SemanticsObject* object, std::unordered_set* visited_objects); - void ReleaseObjects(const std::unordered_map& objects); + void ReleaseObjects(std::unordered_map& objects); UIView* view_; PlatformViewIOS* platform_view_; diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 12a94ee910..c2e1f10dc6 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -234,6 +234,14 @@ void AccessibilityBridge::UpdateSemantics(std::vector node SemanticsObject* root = objects_[kRootNodeId]; + if (root) { + if (!view_.accessibilityElements) { + view_.accessibilityElements = @[ root ]; + } + } else { + view_.accessibilityElements = nil; + } + std::unordered_set visited_objects; if (root) VisitObjectsRecursively(root, &visited_objects); @@ -247,17 +255,27 @@ void AccessibilityBridge::UpdateSemantics(std::vector node // TODO(abarth): Use extract once we're at C++17. } - ReleaseObjects(doomed_objects); - doomed_objects.clear(); - - if (root) { - if (!view_.accessibilityElements) { - view_.accessibilityElements = @[ root ]; + SemanticsObject* doomed_focused_object = nil; + for (const auto& entry : doomed_objects) { + SemanticsObject* object = entry.second; + if ([object accessibilityElementIsFocused]) { + doomed_focused_object = object; + break; } - } else { - view_.accessibilityElements = nil; } - UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); + + if (doomed_focused_object != nil) { + // Previously focused element is no longer in the tree. + // Passing `nil` as argument to let iOS figure out what to focus next. + // TODO(goderbauer): Figure out which element should be focused next and post + // UIAccessibilityLayoutChangedNotification with that element instead. + UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil); + } else { + // Passing `nil` as argument to keep focus where it is. + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil); + } + + ReleaseObjects(doomed_objects); } void AccessibilityBridge::DispatchSemanticsAction(int32_t uid, blink::SemanticsAction action) { @@ -280,12 +298,13 @@ void AccessibilityBridge::VisitObjectsRecursively(SemanticsObject* object, VisitObjectsRecursively(child, visited_objects); } -void AccessibilityBridge::ReleaseObjects(const std::unordered_map& objects) { +void AccessibilityBridge::ReleaseObjects(std::unordered_map& objects) { for (const auto& entry : objects) { SemanticsObject* object = entry.second; [object neuter]; [object release]; } + objects.clear(); } } // namespace shell