From b27bb9167b00bd5e15c00e1864405a3a5206dd83 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 2 Aug 2023 09:15:16 -0700 Subject: [PATCH] [iOS][A11Y] fix hittest with non-SemanticsObject (#44014) PlatformViewSemanticsContainer did not implement the nativeAccessibility method, leads to a nil object being returned. Fixes https://github.com/flutter/flutter/issues/131014 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style --- .../ios/framework/Source/SemanticsObject.mm | 9 ++-- .../framework/Source/SemanticsObjectTest.mm | 48 +++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm index 6e2f9c5a20160..5889a32463892 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm @@ -547,17 +547,16 @@ - (bool)containsPoint:(CGPoint)point { } // Finds the first eligiable semantics object in hit test order. -- (SemanticsObject*)search:(CGPoint)point { +- (id)search:(CGPoint)point { // Search children in hit test order. for (SemanticsObject* child in [self childrenInHitTestOrder]) { if ([child containsPoint:point]) { - SemanticsObject* childSearchResult = [child search:point]; + id childSearchResult = [child search:point]; if (childSearchResult != nil) { return childSearchResult; } } } - // Check if the current semantic object should be returned. if ([self containsPoint:point] && [self isFocusable]) { return self.nativeAccessibility; @@ -879,6 +878,10 @@ - (void)dealloc { [super dealloc]; } +- (id)nativeAccessibility { + return _platformView; +} + #pragma mark - UIAccessibilityContainer overrides - (NSArray*)accessibilityElements { diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm index 48ebd0063563a..c2200af6a6881 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm @@ -203,6 +203,54 @@ - (void)testAccessibilityHitTestNoFocusableItem { XCTAssertNil(hitTestResult); } +- (void)testAccessibilityHitTestSearchCanReturnPlatformView { + fml::WeakPtrFactory factory( + new flutter::MockAccessibilityBridge()); + fml::WeakPtr bridge = factory.GetWeakPtr(); + SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0]; + SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1]; + SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3]; + UIView* platformView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer = + [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge + uid:1 + platformView:platformView]; + + object0.children = @[ object1 ]; + object0.childrenInHitTestOrder = @[ object1 ]; + object1.children = @[ platformViewSemanticsContainer, object3 ]; + object1.childrenInHitTestOrder = @[ platformViewSemanticsContainer, object3 ]; + + flutter::SemanticsNode node0; + node0.id = 0; + node0.rect = SkRect::MakeXYWH(0, 0, 200, 200); + node0.label = "0"; + [object0 setSemanticsNode:&node0]; + + flutter::SemanticsNode node1; + node1.id = 1; + node1.rect = SkRect::MakeXYWH(0, 0, 200, 200); + node1.label = "1"; + [object1 setSemanticsNode:&node1]; + + flutter::SemanticsNode node2; + node2.id = 2; + node2.rect = SkRect::MakeXYWH(0, 0, 100, 100); + node2.label = "2"; + [platformViewSemanticsContainer setSemanticsNode:&node2]; + + flutter::SemanticsNode node3; + node3.id = 3; + node3.rect = SkRect::MakeXYWH(0, 0, 200, 200); + node3.label = "3"; + [object3 setSemanticsNode:&node3]; + + CGPoint point = CGPointMake(10, 10); + id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil]; + + XCTAssertEqual(hitTestResult, platformView); +} + - (void)testAccessibilityScrollToVisible { fml::WeakPtrFactory factory( new flutter::MockAccessibilityBridge());