1111#import < UIKit/UIKit.h>
1212
1313#include " flutter/fml/logging.h"
14+ #include " flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
1415#include " flutter/shell/platform/darwin/ios/platform_view_ios.h"
1516
1617namespace {
@@ -127,6 +128,7 @@ - (void)dealloc {
127128 [_children release ];
128129 _parent = nil ;
129130 _container.get ().semanticsObject = nil ;
131+ [_platformViewSemanticsContainer release ];
130132 [super dealloc ];
131133}
132134
@@ -152,6 +154,9 @@ - (BOOL)nodeWillCauseScroll:(const blink::SemanticsNode*)node {
152154}
153155
154156- (BOOL )hasChildren {
157+ if (_node.IsPlatformViewNode ()) {
158+ return YES ;
159+ }
155160 return [self .children count ] != 0 ;
156161}
157162
@@ -165,6 +170,7 @@ - (BOOL)isAccessibilityElement {
165170 // We enforce in the framework that no other useful semantics are merged with these nodes.
166171 if ([self node ].HasFlag (blink::SemanticsFlags::kScopesRoute ))
167172 return false ;
173+
168174 return ([self node ].flags != 0 &&
169175 [self node ].flags != static_cast <int32_t >(blink::SemanticsFlags::kIsHidden )) ||
170176 ![self node ].label .empty () || ![self node ].value .empty () || ![self node ].hint .empty () ||
@@ -396,6 +402,25 @@ - (UIAccessibilityTraits)accessibilityTraits {
396402
397403@end
398404
405+ @implementation FlutterPlatformViewSemanticsContainer
406+
407+ // Method declared as unavailable in the interface
408+ - (instancetype )init {
409+ [self release ];
410+ [super doesNotRecognizeSelector: _cmd ];
411+ return nil ;
412+ }
413+
414+ - (instancetype )initWithAccessibilityContainer : (id )container {
415+ FML_CHECK (container);
416+ if (self = [super initWithAccessibilityContainer: container]) {
417+ self.isAccessibilityElement = NO ;
418+ }
419+ return self;
420+ }
421+
422+ @end
423+
399424@implementation SemanticsObjectContainer {
400425 SemanticsObject* _semanticsObject;
401426 fml::WeakPtr<shell::AccessibilityBridge> _bridge;
@@ -426,7 +451,12 @@ - (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject
426451#pragma mark - UIAccessibilityContainer overrides
427452
428453- (NSInteger )accessibilityElementCount {
429- return [[_semanticsObject children ] count ] + 1 ;
454+ NSInteger count = [[_semanticsObject children ] count ] + 1 ;
455+ // Need to create an additional child that acts as accessibility container for the platform view.
456+ if (_semanticsObject.node .IsPlatformViewNode ()) {
457+ count++;
458+ }
459+ return count;
430460}
431461
432462- (nullable id )accessibilityElementAtIndex : (NSInteger )index {
@@ -435,7 +465,16 @@ - (nullable id)accessibilityElementAtIndex:(NSInteger)index {
435465 if (index == 0 ) {
436466 return _semanticsObject;
437467 }
468+
469+ // Return the additional child acts as a container of platform view. The
470+ // platformViewSemanticsContainer was created and cached in the updateSemantics path.
471+ if (_semanticsObject.node .IsPlatformViewNode () && index == [self accessibilityElementCount ] - 1 ) {
472+ FML_CHECK (_semanticsObject.platformViewSemanticsContainer != nil );
473+ return _semanticsObject.platformViewSemanticsContainer ;
474+ }
475+
438476 SemanticsObject* child = [_semanticsObject children ][index - 1 ];
477+
439478 if ([child hasChildren ])
440479 return [child accessibilityContainer ];
441480 return child;
@@ -444,6 +483,12 @@ - (nullable id)accessibilityElementAtIndex:(NSInteger)index {
444483- (NSInteger )indexOfAccessibilityElement : (id )element {
445484 if (element == _semanticsObject)
446485 return 0 ;
486+
487+ // FlutterPlatformViewSemanticsContainer is always the last element of its parent.
488+ if ([element isKindOfClass: [FlutterPlatformViewSemanticsContainer class ]]) {
489+ return [self accessibilityElementCount ] - 1 ;
490+ }
491+
447492 NSMutableArray <SemanticsObject*>* children = [_semanticsObject children ];
448493 for (size_t i = 0 ; i < [children count ]; i++) {
449494 SemanticsObject* child = children[i];
@@ -485,9 +530,12 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
485530
486531namespace shell {
487532
488- AccessibilityBridge::AccessibilityBridge (UIView* view, PlatformViewIOS* platform_view)
533+ AccessibilityBridge::AccessibilityBridge (UIView* view,
534+ PlatformViewIOS* platform_view,
535+ FlutterPlatformViewsController* platform_views_controller)
489536 : view_(view),
490537 platform_view_ (platform_view),
538+ platform_views_controller_(platform_views_controller),
491539 objects_([[NSMutableDictionary alloc ] init ]),
492540 weak_factory_(this ),
493541 previous_route_id_(0 ),
@@ -525,7 +573,7 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
525573 layoutChanged = layoutChanged || [object nodeWillCauseLayoutChange: &node];
526574 scrollOccured = scrollOccured || [object nodeWillCauseScroll: &node];
527575 [object setSemanticsNode: &node];
528- const NSUInteger newChildCount = node.childrenInTraversalOrder .size ();
576+ NSUInteger newChildCount = node.childrenInTraversalOrder .size ();
529577 NSMutableArray * newChildren =
530578 [[[NSMutableArray alloc ] initWithCapacity: newChildCount] autorelease ];
531579 for (NSUInteger i = 0 ; i < newChildCount; ++i) {
@@ -555,6 +603,20 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction {
555603 }
556604 object.accessibilityCustomActions = accessibilityCustomActions;
557605 }
606+
607+ if (object.node .IsPlatformViewNode ()) {
608+ shell::FlutterPlatformViewsController* controller = GetPlatformViewsController ();
609+ if (controller) {
610+ object.platformViewSemanticsContainer = [[FlutterPlatformViewSemanticsContainer alloc ]
611+ initWithAccessibilityContainer: [object accessibilityContainer ]];
612+ UIView* platformView = [controller->GetPlatformViewByID (object.node.platformViewId) view ];
613+ if (platformView) {
614+ object.platformViewSemanticsContainer .accessibilityElements = @[ platformView ];
615+ }
616+ }
617+ } else if (object.platformViewSemanticsContainer ) {
618+ [object.platformViewSemanticsContainer release ];
619+ }
558620 }
559621
560622 SemanticsObject* root = objects_.get ()[@(kRootNodeId )];
0 commit comments