Skip to content

fix(chat): finish the edit backdrop - #720

Merged
bmc08gt merged 5 commits into
mainfrom
fix/chat-edit-backdrop
Sep 3, 2026
Merged

fix(chat): finish the edit backdrop#720
bmc08gt merged 5 commits into
mainfrom
fix/chat-edit-backdrop

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Follow-on to #717. Five fixes to the blur an edit holds past the context menu, and to how the edited message reads against it.

The blur stopped at the composer. Its background gradient clears at its own top edge and its controls are glass, so both sampled sharp transcript. Held, the blur now moves into the composer's superview just below the bar and fills it, so the transcript stays soft to the bottom of the screen.

The floated copy vanished for about a second on the way into an edit. The clip's bottom edge tracks the composer bar as the keyboard returns, and for a beat the copy's last reported frame sat below an edge that had already moved — clipsToBounds cut the whole thing away rather than trimming it. Its origin is now clamped so it hugs the composer for that beat instead.

A swipe-back carried the screen away mid-edit. The blur and the copy are hosted by the navigation stack, so they survived the pop and sat on whatever screen it landed on, eating taps with nothing left to dismiss them. Every enabled pop pan on the navigation view is suspended for the length of an edit and exactly those restored — every pan, because a sheet-hosted stack carries a second one and it is the untouched twin that pops.

The backdrop read as near-black: a full-strength material on a 0.48 dim chosen to match the menu's dimming exactly. Android frosts the same screen with a 25dp Haze and no scrim, which is the target. UIBlurEffect exposes no radius, so the effect goes on through a UIViewPropertyAnimator paused at 0.4, and the dim drops to 0.2. One consequence: an edit is now lighter than the menu it comes from, so there is a slight brightening as the menu gives way — the menu's dimming is UIKit's and can't be lightened to meet it.

The same message rendered three different ways. A bubble's fill was translucent white, so it took the colour of whatever sat behind it, and that is not always the transcript: measured rgb 43 in the transcript, 34 under the menu, 57 in an edit. It now carries its own ground — an opaque base with the wash composited over it in its own layer — and reads 43 in all three.

Neither lifted state cast a shadow either. A UITargetedPreview with a clear background casts nothing, with or without shadowPath, so the dimmed transcript sat flat at rgb 17 right up to the lifted bubble's edge. The elevation is now set explicitly and shared by the menu's lift and the edit's floated copy, and taken off again when the menu goes so a recycled cell can't carry it back into the transcript.

The blur stopped at the top of the composer, so the bar drew over sharp
transcript: its background is a gradient that clears at its own top edge and
its controls are glass, both of which sampled unblurred bubbles.

Held, the blur now moves out of the navigation controller's view and into the
composer's superview, just below the bar, and fills that view. The transcript
stays soft to the bottom of the screen; the composer draws over the blur
instead of beside it; and the navigation bar still wins, because the screen the
blur moved into already sits under it. The clip the floated copy lives in keeps
the old job of stopping at the bar's top edge, so the copy can't slide under
the composer.
The floated copy appeared, vanished for about a second, then animated back in
from the wrong place. The clip's bottom edge tracks the composer bar, which
rises on its own animation as the keyboard returns, so for a beat the copy's
last reported frame sat below an edge that had already moved up — and
`clipsToBounds` cut the whole thing away rather than trimming it.

Clamp the frame's origin so the copy's bottom stays inside the clip. It hugs
the composer for that beat instead of disappearing, and snaps to its true
position on the next reflow.
An edit owns the whole screen — the blur takes every tap outside the message
and the composer is the only way out — but the pop gesture stayed live, so a
swipe carried the screen away underneath it. Worse, the blur and the floated
copy are hosted by the navigation stack rather than by this screen, so they
survived the pop: they stayed on whatever screen it landed on, eating taps,
with nothing left to dismiss them.

Suspend every enabled pop pan on the navigation view for the length of an edit
and restore exactly those, so one that was already off stays off. Every pan,
not just `interactivePopGestureRecognizer`: a sheet-hosted stack carries a
second one, and it is the untouched twin that pops (`EdgeOnlySwipeBack` gates
both for the same reason). `viewWillDisappear` also ends the edit, as a
backstop for any other way off the screen.
The edit backdrop read as near-black: a full-strength material stacked on a
0.48 dim chosen to match the context menu's own dimming exactly. Android frosts
the same screen with a regular Haze material at a 25dp radius and no scrim at
all, which is much lighter, and that is the look we want.

`UIBlurEffect` exposes no radius — every style is the same radius under a
different tint — so the effect goes on through a `UIViewPropertyAnimator` left
paused at 0.4, which is the only handle on its strength. The animator has to be
retained and stopped by hand, since one that deallocates while active traps.
With the effect only partly applied, the fades move from `effect` to `alpha`.
The dim drops to 0.2.

The transcript now reads as soft shapes rather than flat colour. The
consequence is that an edit is lighter than the menu it comes from, so there is
a slight brightening as the menu gives way — the menu's dimming is UIKit's and
can't be lightened to meet it.
A bubble's fill was translucent white, so it took the colour of whatever sat
behind it — and that is not always the transcript. The context menu dims what it
covers and an edit blurs it, and both showed straight through: the same message
measured rgb 43 in the transcript, 34 under the menu and 57 in an edit. It now
carries its own ground, an opaque base with the wash composited over it in its
own layer, and reads 43 in all three.

Neither lifted state cast a shadow either. A UITargetedPreview built with a clear
background casts nothing, with or without shadowPath, so the dimmed transcript
sat flat at rgb 17 right up to the lifted bubble's edge. The elevation is now set
explicitly and shared by the menu's lift and the edit's floated copy, and taken
off again when the menu goes so a recycled cell can't carry it back into the
transcript.
@bmc08gt bmc08gt self-assigned this Sep 3, 2026
@bmc08gt
bmc08gt merged commit 666de6c into main Sep 3, 2026
1 check passed
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