Skip to content

Commit

Permalink
Overrides accessibilityScrollToVisible (#42047)
Browse files Browse the repository at this point in the history
fixes flutter/flutter#61624

Observed behavior is that this method is called when an item is swipe-to-focusd in VoiceOver.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
  • Loading branch information
chunhtai authored May 19, 2023
1 parent 482c99a commit aac0919
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 6 deletions.
32 changes: 27 additions & 5 deletions shell/platform/darwin/ios/framework/Source/SemanticsObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ - (NSAttributedString*)createAttributedStringFromString:(NSString*)string
return attributedString;
}

- (void)showOnScreen {
[self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kShowOnScreen);
}

#pragma mark - UIAccessibility overrides

- (BOOL)isAccessibilityElement {
Expand Down Expand Up @@ -561,14 +565,32 @@ - (SemanticsObject*)search:(CGPoint)point {
return nil;
}

// Overrides apple private method to fix https://github.com/flutter/flutter/issues/113377.
// For overlapping UIAccessibilityElements (e.g. a stack) in IOS, the focus goes to the smallest
// object before IOS 16, but to the top-left object in IOS 16.
// Overrides this method to focus the first eligiable semantics object in hit test order.
// iOS uses this method to determine the hittest results when users touch
// explore in VoiceOver.
//
// For overlapping UIAccessibilityElements (e.g. a stack) in IOS, the focus
// goes to the smallest object before IOS 16, but to the top-left object in
// IOS 16. Overrides this method to focus the first eligiable semantics
// object in hit test order.
- (id)_accessibilityHitTest:(CGPoint)point withEvent:(UIEvent*)event {
return [self search:point];
}

// iOS calls this method when this item is swipe-to-focusd in VoiceOver.
- (BOOL)accessibilityScrollToVisible {
[self showOnScreen];
return YES;
}

// iOS calls this method when this item is swipe-to-focusd in VoiceOver.
- (BOOL)accessibilityScrollToVisibleWithChild:(id)child {
if ([child isKindOfClass:[SemanticsObject class]]) {
[child showOnScreen];
return YES;
}
return NO;
}

- (NSAttributedString*)accessibilityAttributedLabel {
NSString* label = [self accessibilityLabel];
if (label.length == 0) {
Expand Down Expand Up @@ -750,7 +772,7 @@ - (void)accessibilityElementDidBecomeFocused {
[self bridge]->AccessibilityObjectDidBecomeFocused([self uid]);
if ([self node].HasFlag(flutter::SemanticsFlags::kIsHidden) ||
[self node].HasFlag(flutter::SemanticsFlags::kIsHeader)) {
[self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kShowOnScreen);
[self showOnScreen];
}
if ([self node].HasAction(flutter::SemanticsAction::kDidGainAccessibilityFocus)) {
[self bridge]->DispatchSemanticsAction([self uid],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ @interface SemanticsObjectTest : XCTestCase
@end

@interface SemanticsObject (Tests)

- (BOOL)accessibilityScrollToVisible;
- (BOOL)accessibilityScrollToVisibleWithChild:(id)child;
- (id)_accessibilityHitTest:(CGPoint)point withEvent:(UIEvent*)event;
@end

Expand Down Expand Up @@ -202,6 +203,42 @@ - (void)testAccessibilityHitTestNoFocusableItem {
XCTAssertNil(hitTestResult);
}

- (void)testAccessibilityScrollToVisible {
fml::WeakPtrFactory<flutter::MockAccessibilityBridge> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];

flutter::SemanticsNode node3;
node3.id = 3;
node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
[object3 setSemanticsNode:&node3];

[object3 accessibilityScrollToVisible];

XCTAssertTrue(bridge->observations.size() == 1);
XCTAssertTrue(bridge->observations[0].id == 3);
XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
}

- (void)testAccessibilityScrollToVisibleWithChild {
fml::WeakPtrFactory<flutter::MockAccessibilityBridge> factory(
new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];

flutter::SemanticsNode node3;
node3.id = 3;
node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
[object3 setSemanticsNode:&node3];

[object3 accessibilityScrollToVisibleWithChild:object3];

XCTAssertTrue(bridge->observations.size() == 1);
XCTAssertTrue(bridge->observations[0].id == 3);
XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
}

- (void)testAccessibilityHitTestOutOfRect {
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
new flutter::MockAccessibilityBridge());
Expand Down

0 comments on commit aac0919

Please sign in to comment.