diff --git a/BlueprintUIAccessibilityCore/Sources/AccessibilityComposition.swift b/BlueprintUIAccessibilityCore/Sources/AccessibilityComposition.swift index cf6d9b357..0d03bec62 100644 --- a/BlueprintUIAccessibilityCore/Sources/AccessibilityComposition.swift +++ b/BlueprintUIAccessibilityCore/Sources/AccessibilityComposition.swift @@ -407,6 +407,7 @@ extension AccessibilityComposition { } private func updateAccessibility() { + guard window != nil else { return } let sorting = customSorting ?? Accessibility.frameSort( direction: layoutDirection, root: self, @@ -438,6 +439,7 @@ extension AccessibilityComposition.CombinableView { public override var isAccessibilityElement: Bool { get { + guard window != nil else { return false } if needsAccessibilityUpdate { updateAccessibility() } @@ -486,6 +488,16 @@ extension AccessibilityComposition.CombinableView { set { super.accessibilityIdentifier = newValue } } + public override var accessibilityTraits: UIAccessibilityTraits { + get { + if needsAccessibilityUpdate { + updateAccessibility() + } + return super.accessibilityTraits + } + set { super.accessibilityTraits = newValue } + } + public override var accessibilityElements: [Any]? { get { if needsAccessibilityUpdate { diff --git a/BlueprintUIAccessibilityCore/Sources/AccessibilityDeferral.swift b/BlueprintUIAccessibilityCore/Sources/AccessibilityDeferral.swift index 0935b9a4d..a5ec86ed3 100644 --- a/BlueprintUIAccessibilityCore/Sources/AccessibilityDeferral.swift +++ b/BlueprintUIAccessibilityCore/Sources/AccessibilityDeferral.swift @@ -271,8 +271,6 @@ extension AccessibilityDeferral { public func backingViewDescription(with context: BlueprintUI.ViewDescriptionContext) -> BlueprintUI.ViewDescription? { ReceiverContainerView.describe { config in config.apply { view in - view.isAccessibilityElement = true - view.needsAccessibilityUpdate = true view.layoutDirection = context.environment.layoutDirection view.element = wrappedElement } @@ -291,7 +289,6 @@ extension AccessibilityDeferral { override init(frame: CGRect) { super.init(frame: frame) - isAccessibilityElement = true mergeInteractiveSingleChild = false blueprintView.backgroundColor = .clear @@ -302,6 +299,14 @@ extension AccessibilityDeferral { fatalError("init(coder:) has not been implemented") } + override func didMoveToWindow() { + super.didMoveToWindow() + isAccessibilityElement = (window != nil) + if window != nil { + needsAccessibilityUpdate = true + } + } + override func layoutSubviews() { super.layoutSubviews() blueprintView.frame = bounds @@ -335,6 +340,7 @@ extension AccessibilityDeferral { } public func updateDeferredAccessibility(frameProvider: FrameProvider?) { + guard window != nil else { return } needsAccessibilityUpdate = true self.frameProvider = frameProvider @@ -420,6 +426,7 @@ extension AccessibilityDeferral { } private func updateAccessibility() { + guard window != nil else { return } needsAccessibilityUpdate = false blueprintView.layoutIfNeeded() let elements = blueprintView.recursiveAccessibleElements() diff --git a/BlueprintUIAccessibilityCore/Tests/AccessibilityCompositionTests.swift b/BlueprintUIAccessibilityCore/Tests/AccessibilityCompositionTests.swift index 71255adf0..99ee76c8c 100644 --- a/BlueprintUIAccessibilityCore/Tests/AccessibilityCompositionTests.swift +++ b/BlueprintUIAccessibilityCore/Tests/AccessibilityCompositionTests.swift @@ -331,7 +331,9 @@ class AccessibilityCompositionTests: XCTestCase { } func test_blockWhenNotAccessible_givenPopulatedAccessibility() { + let window = UIWindow() let view = AccessibilityComposition.CombinableView() + window.addSubview(view) view.blockWhenNotAccessible = true (1...3) .map { int in @@ -358,7 +360,9 @@ class AccessibilityCompositionTests: XCTestCase { } func test_blockWhenNotAccessible_givenEmptyAccessibility() { + let window = UIWindow() let view = AccessibilityComposition.CombinableView() + window.addSubview(view) view.blockWhenNotAccessible = true (1...3) .map { int in @@ -384,6 +388,39 @@ class AccessibilityCompositionTests: XCTestCase { XCTAssertFalse(view.isAccessibilityElement, "The view should not be accessible.") } + func test_updateAccessibility_skipsWhenNotInWindow() { + let view = AccessibilityComposition.CombinableView() + view.blockWhenNotAccessible = true + let subview = UIView() + subview.isAccessibilityElement = true + subview.accessibilityLabel = "Should not appear" + view.addSubview(subview) + + view.setNeedsLayout() + view.layoutIfNeeded() + + XCTAssertNil(view.accessibilityLabel) + XCTAssertTrue(view.needsAccessibilityUpdate) + } + + func test_updateAccessibility_runsAfterMovingToWindow() { + let view = AccessibilityComposition.CombinableView() + view.blockWhenNotAccessible = true + let subview = UIView() + subview.isAccessibilityElement = true + subview.accessibilityLabel = "Now visible" + view.addSubview(subview) + + view.setNeedsLayout() + view.layoutIfNeeded() + XCTAssertNil(view.accessibilityLabel) + XCTAssertTrue(view.needsAccessibilityUpdate) + + let window = UIWindow() + window.addSubview(view) + XCTAssertEqual(view.accessibilityLabel, "Now visible") + XCTAssertFalse(view.needsAccessibilityUpdate) + } func test_applyAccessibility() {