diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.h b/shell/platform/darwin/ios/framework/Source/SemanticsObject.h index b2202f0e619f2..33fbcdeb3c526 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObject.h +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.h @@ -61,11 +61,6 @@ constexpr float kScrollExtentMaxForInf = 1000; */ @property(nonatomic, strong) NSArray* children; -/** - * Used if this SemanticsObject is for a platform view. - */ -@property(strong, nonatomic) FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer; - /** * The UIAccessibility that represents this object. * @@ -159,16 +154,14 @@ constexpr float kScrollExtentMaxForInf = 1000; * * `SemanticsObject` for the other type of semantics objects. * * `FlutterSemanticsObject` for default implementation of `SemanticsObject`. */ -@interface FlutterPlatformViewSemanticsContainer : UIAccessibilityElement - -/** - * The position inside an accessibility container. - */ -@property(nonatomic) NSInteger index; +@interface FlutterPlatformViewSemanticsContainer : SemanticsObject -- (instancetype)init __attribute__((unavailable("Use initWithAccessibilityContainer: instead"))); +- (instancetype)initWithBridge:(fml::WeakPtr)bridge + uid:(int32_t)uid NS_UNAVAILABLE; -- (instancetype)initWithSemanticsObject:(SemanticsObject*)object; +- (instancetype)initWithBridge:(fml::WeakPtr)bridge + uid:(int32_t)uid + platformView:(UIView*)platformView NS_DESIGNATED_INITIALIZER; @end diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm index db666b82d098d..f627f2de9e81c 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm @@ -300,7 +300,6 @@ - (void)dealloc { [_children release]; _parent = nil; _container.get().semanticsObject = nil; - [_platformViewSemanticsContainer release]; _inDealloc = YES; [super dealloc]; } @@ -319,9 +318,6 @@ - (void)setChildren:(NSArray*)children { } - (BOOL)hasChildren { - if (_node.IsPlatformViewNode()) { - return YES; - } return [self.children count] != 0; } @@ -753,31 +749,16 @@ - (UIAccessibilityTraits)accessibilityTraits { @end @interface FlutterPlatformViewSemanticsContainer () -@property(nonatomic, assign) SemanticsObject* semanticsObject; @property(nonatomic, strong) UIView* platformView; @end @implementation FlutterPlatformViewSemanticsContainer -// Method declared as unavailable in the interface -- (instancetype)init { - [self release]; - [super doesNotRecognizeSelector:_cmd]; - return nil; -} - -- (instancetype)initWithSemanticsObject:(SemanticsObject*)object { - FML_CHECK(object); - // Initialize with the UIView as the container. - // The UIView will not necessarily be accessibility parent for this object. - // The bridge informs the OS of the actual structure via - // `accessibilityContainer` and `accessibilityElementAtIndex`. - if (self = [super initWithAccessibilityContainer:object.bridge->view()]) { - _semanticsObject = object; - auto controller = object.bridge->GetPlatformViewsController(); - if (controller) { - _platformView = [controller->GetPlatformViewByID(object.node.platformViewId) retain]; - } +- (instancetype)initWithBridge:(fml::WeakPtr)bridge + uid:(int32_t)uid + platformView:(nonnull UIView*)platformView { + if (self = [super initWithBridge:bridge uid:uid]) { + _platformView = [platformView retain]; } return self; } @@ -790,47 +771,8 @@ - (void)dealloc { #pragma mark - UIAccessibilityContainer overrides -- (NSInteger)accessibilityElementCount { - // This container should only contain 2 elements: - // 1. The semantic object that represents this container. - // 2. The platform view object. - return 2; -} - -- (nullable id)accessibilityElementAtIndex:(NSInteger)index { - FML_DCHECK(index < 2); - if (index == 0) { - return _semanticsObject.nativeAccessibility; - } else { - return _platformView; - } -} - -- (NSInteger)indexOfAccessibilityElement:(id)element { - FML_DCHECK(element == _semanticsObject || element == _platformView); - if (element == _semanticsObject) { - return 0; - } else { - return 1; - } -} - -#pragma mark - UIAccessibilityElement overrides - -- (CGRect)accessibilityFrame { - return _semanticsObject.accessibilityFrame; -} - -- (BOOL)isAccessibilityElement { - return NO; -} - -- (id)accessibilityContainer { - return [_semanticsObject accessibilityContainer]; -} - -- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { - return [_platformView accessibilityScroll:direction]; +- (NSArray*)accessibilityElements { + return @[ _platformView ]; } @end @@ -882,12 +824,6 @@ - (nullable id)accessibilityElementAtIndex:(NSInteger)index { SemanticsObject* child = [_semanticsObject children][index - 1]; - // Swap the original `SemanticsObject` to a `PlatformViewSemanticsContainer` - if (child.node.IsPlatformViewNode()) { - child.platformViewSemanticsContainer.index = index; - return child.platformViewSemanticsContainer; - } - if ([child hasChildren]) return [child accessibilityContainer]; return child.nativeAccessibility; @@ -897,11 +833,6 @@ - (NSInteger)indexOfAccessibilityElement:(id)element { if (element == _semanticsObject) return 0; - // FlutterPlatformViewSemanticsContainer is always the last element of its parent. - if ([element isKindOfClass:[FlutterPlatformViewSemanticsContainer class]]) { - return ((FlutterPlatformViewSemanticsContainer*)element).index; - } - NSArray* children = [_semanticsObject children]; for (size_t i = 0; i < [children count]; i++) { SemanticsObject* child = children[i]; diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm index 6b8940448c567..019fea6cf5b5e 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm @@ -690,16 +690,25 @@ - (void)testShouldDispatchShowOnScreenActionForHidden { XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen); } -- (void)testSemanticsObjectAndPlatformViewSemanticsContainerDontHaveRetainCycle { +- (void)testFlutterPlatformViewSemanticsContainer { fml::WeakPtrFactory factory( new flutter::MockAccessibilityBridge()); fml::WeakPtr bridge = factory.GetWeakPtr(); - SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1]; - object.platformViewSemanticsContainer = - [[FlutterPlatformViewSemanticsContainer alloc] initWithSemanticsObject:object]; - __weak SemanticsObject* weakObject = object; - object = nil; - XCTAssertNil(weakObject); + __weak UIView* weakPlatformView; + @autoreleasepool { + UIView* platformView = [[UIView alloc] init]; + + FlutterPlatformViewSemanticsContainer* container = + [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge + uid:1 + platformView:platformView]; + XCTAssertEqualObjects(container.accessibilityElements, @[ platformView ]); + weakPlatformView = platformView; + XCTAssertNotNil(weakPlatformView); + } + // Check if there's no more strong references to `platformView` after container and platformView + // are released. + XCTAssertNil(weakPlatformView); } - (void)testFlutterSwitchSemanticsObjectMatchesUISwitch { diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 0e7a4ac01d542..48f8d97652b44 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -126,15 +126,6 @@ void PostAccessibilityNotification(UIAccessibilityNotifications notification, object.accessibilityCustomActions = accessibilityCustomActions; } - if (object.node.IsPlatformViewNode()) { - auto controller = GetPlatformViewsController(); - if (controller) { - object.platformViewSemanticsContainer = [[[FlutterPlatformViewSemanticsContainer alloc] - initWithSemanticsObject:object] autorelease]; - } - } else if (object.platformViewSemanticsContainer) { - object.platformViewSemanticsContainer = nil; - } if (needsAnnouncement) { // Try to be more polite - iOS 11+ supports // UIAccessibilitySpeechAttributeQueueAnnouncement which should avoid @@ -268,6 +259,12 @@ static void ReplaceSemanticsObject(SemanticsObject* oldObject, } else if (node.HasFlag(flutter::SemanticsFlags::kHasImplicitScrolling)) { return [[[FlutterScrollableSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id] autorelease]; + } else if (node.IsPlatformViewNode()) { + return [[[FlutterPlatformViewSemanticsContainer alloc] + initWithBridge:weak_ptr + uid:node.id + platformView:weak_ptr->GetPlatformViewsController()->GetPlatformViewByID( + node.platformViewId)] autorelease]; } else { return [[[FlutterSemanticsObject alloc] initWithBridge:weak_ptr uid:node.id] autorelease]; }