Skip to content

Tell the client when an inset other than the top moves - #34

Merged
defiantnerd merged 1 commit into
mainfrom
fix/ios-metrics-inset-gate
Sep 20, 2026
Merged

defiantnerd merged 1 commit into
mainfrom
fix/ios-metrics-inset-gate

Conversation

@defiantnerd

Copy link
Copy Markdown
Owner

Both iOS hosts gated reportResizeIfChanged on the bounds and the top inset alone:

if (w == _last_w && h == _last_h && top_inset == _last_top_inset) return;

so a change confined to the left, right or bottom inset raised nothing at all. That was defensible while the top inset was the only one anybody acted on — it is the one get_client_rect subtracts — but safe_area_insets reports all four, and docs/host-ios.md tells every iOS client to subtract the other three by hand. A client that does had no way to hear that they moved.

What changed

Both hosts track all four edges and split the two notifications:

  • RESIZE fires exactly where it did. The top inset is folded into the rect get_client_rect reports, so a top-only change is a resize to a client even when the bounds held still. platform_ios.mm already said so in a comment; that behaviour is untouched.
  • The other three raise METRICS_CHANGED on their own. They are not in the client rect, so calling them a resize would be a lie.

The insets are read off the same view each host's safe_area_insets seam reads ([self contentView] / [self neuiView]), so the gate and the answer cannot drift apart.

What this does not fix

The case that prompted it was launching straight into landscape — the idea being that the bounds arrive first and the inset resolve is then swallowed. It does not reproduce. Measured on an iPhone 17 Pro / iOS 26.5, one client binary, only these two files differing:

with the change without it
landscape launch, clearance each side 74.0 pt 74.0 pt

UIKit resolves the safe area before the first viewDidLayoutSubviews, so the opening RESIZE already carries the right insets. The hole is real, but I have no reproduction of it biting. The concrete trigger is set_home_indicator_auto_hidden, which NEUI_API_IOS ships: it moves safeAreaInsets.bottom and changes no bounds.

Landing it on that basis rather than on an observed break is the call to make when reviewing.

Verified

  • Fresh iOS configure with -DNEUI_WERROR=ON — builds clean, no new warnings.
  • Suite on the simulator: 351 cases, 2107 checks, 0 failed.
  • Both hosts still link into one client binary (profiler-gigmode) and it builds and runs.

docs/host-ios.md already claimed METRICS_CHANGED fires on safe-area change, which was only true for the top inset. That line is now accurate and says which event you get for which edge.

Both iOS hosts gated reportResizeIfChanged on the bounds and the TOP inset
alone:

  if (w == _last_w && h == _last_h && top_inset == _last_top_inset) return;

so a change confined to the left, right or bottom inset raised nothing at all.
That was defensible while the top inset was the only one anybody acted on -- it
is the one get_client_rect subtracts -- but safe_area_insets reports all four,
and docs/host-ios.md tells every iOS client to subtract the other three by hand.
A client that does has no way to hear that they moved.

Both hosts now track all four edges and split the two notifications. RESIZE
fires exactly where it did: the top inset is folded into the rect
get_client_rect reports, so a top-only change is a resize to a client even when
the bounds held still, which is what the comment in platform_ios.mm already
said and is left alone. The other three are not in that rect, so they raise
METRICS_CHANGED on its own. The insets are read off the same view each host's
safe_area_insets seam reads, so the gate and the answer cannot drift apart.

On what this does NOT fix: the case that prompted it was launching straight
into landscape, the idea being that the bounds arrive first and the inset
resolve is then swallowed. That does not reproduce. Measured on an iPhone 17
Pro / iOS 26.5 with one client binary and only these two files differing, a
landscape launch lays out 74 pt clear on each side either way -- UIKit resolves
the safe area before the first viewDidLayoutSubviews, so the opening RESIZE
already carries the right insets. The hole is real but I have no reproduction
of it biting. The concrete trigger is set_home_indicator_auto_hidden, which
NEUI_API_IOS ships: it moves safeAreaInsets.bottom and changes no bounds.

Verified: fresh iOS configure with NEUI_WERROR=ON builds clean, and the suite
on the simulator is 351 cases / 2107 checks, 0 failed.
@defiantnerd
defiantnerd merged commit b8d0882 into main Sep 20, 2026
8 checks passed
@defiantnerd
defiantnerd deleted the fix/ios-metrics-inset-gate branch September 20, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant