Prune views when either frame axis is degenerate#341
Draft
RoyalPineapple wants to merge 6 commits into
Draft
Conversation
The pruning gate in recursiveAccessibilityHierarchy previously only fired when the view's frame size was fully `.zero`, so slivers with one axis at zero (e.g. width 0, height 100) slipped through and produced spurious elements with no visible footprint. Check each axis independently against a sub-pixel threshold (0.001) to catch float-layout slivers in addition to exact zero. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
elementB previously had width 0 — incidental to what the test exercises (vertical separation sort order), but now pruned by the degenerate-frame gate. Give it a real 10pt width; positions still don't overlap A/C/D and the sort-order assertion is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous revision broadened the pruning gate to drop any view with a single-axis-degenerate frame when it clipped, was an accessibility element, or exposed accessibility children. SwiftUI's bridging layers produce intermediate wrapper views with single-axis-degenerate frames that clip but contain real, visible children — those were being pruned, eliminating the entire ScrollView content of PathShapesDemo (verified via the failing testPathShapesDemo snapshot diff on iOS 18 / 26: only the DemoSection's combined element survived, the 12 shape views disappeared). Keep the original strict frame.size == .zero pruning for clipping wrappers and accessibility containers, and apply the per-axis sub-pixel threshold only to isAccessibilityElement views — which is what the user actually wanted filtered out as spurious targets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extract isInvisible, isCollapsedWrapper, and isDegenerateElement into named locals so each prune rule is self-documenting. Use abs(...) <= 0.001 to mirror VoiceOver's degenerate-frame check (handles negative-width edge cases that can arise from pathological transforms or direct frame assignment). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the abs/threshold width/height checks with view.frame.isEmpty (true when either dimension is <= 0). Same behavior for the degenerate- axis case, plus consistent handling of negative-dimension pathology, and the leaf rule now matches the wrapper rule's frame check. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The view-pruning gate in
recursiveAccessibilityHierarchycheckedframe.size == .zero, which only matched when both width and height were zero. Slivers with one axis at zero (e.g. width 0, height 100) slipped through and surfaced as elements with no visible footprint.Switch the check to compare each axis independently — prune when either
frame.widthorframe.heightis below a sub-pixel threshold (0.001). This catches single-axis collapses and float-layout slivers in addition to exact zero.Test plan