Skip to content

fix(ios): reset extendUnderStatusBar on recycle - #76

Open
giaBaoJS wants to merge 1 commit into
software-mansion-labs:mainfrom
giaBaoJS:fix/ios-recycled-extend-under-status-bar
Open

fix(ios): reset extendUnderStatusBar on recycle#76
giaBaoJS wants to merge 1 commit into
software-mansion-labs:mainfrom
giaBaoJS:fix/ios-recycled-extend-under-status-bar

Conversation

@giaBaoJS

Copy link
Copy Markdown
Contributor

Summary

On iOS, a recycled BottomSheetView can keep the previous sheet's
extendUnderStatusBar, so a sheet that never set the prop is allowed to run
under the status bar.

updateProps diffs extendUnderStatusBar against the _extendUnderStatusBar
ivar rather than oldProps, because Fabric keeps _props across recycling
(RCTViewComponentView.prepareForRecycle does not reset it):

https://github.com/software-mansion-labs/react-native-bottom-sheet/blob/d2bdb71/ios/BottomSheetComponentView.mm#L136-L141

prepareForRecycle resets that ivar, but nothing resets the value it is a
baseline for. resetSheetState clears springs, detents and layout state and
leaves BottomSheetHostingView.extendUnderStatusBar alone:

https://github.com/software-mansion-labs/react-native-bottom-sheet/blob/d2bdb71/ios/BottomSheetHostingView.swift#L444-L470

So when a sheet that set extendUnderStatusBar is unmounted and its view is
dequeued from the recycle pool for a sheet without the prop, the diff reads
false != NO, skips the setter, and the hosting view still holds YES.
resolvedMaxDetentHeight then returns the raw view height instead of
subtracting the status-bar overlap:

https://github.com/software-mansion-labs/react-native-bottom-sheet/blob/d2bdb71/ios/BottomSheetHostingView.swift#L1268-L1280

The ivar dates from when it was purely an Objective-C side cache used to
compute the cap locally. refactor: drop react-native-safe-area-context; measure all geometry natively (6614140) moved the flag onto the hosting view
but kept resetting only the ivar.

_nativeOverlay avoids this because prepareForRecycle calls
restoreInlinePresentation, which actually undoes the presentation its ivar
tracks.

Fix

Push the reset through to the hosting view in prepareForRecycle, so the ivar
and the value it is diffed against always agree. Placed after
resetSheetState so the didSet refresh runs against already-cleared state.

Repro

Example app, iPhone 17 Pro simulator (iOS 26.3), inline sheets:

  1. Open Numeric and percentage detents and tap Open to 100% (3). The
    card reports position: 812.0pt and the sheet stops below the status bar.
  2. Go back, open Under status bar, tap Open under status bar, close it
    and go back.
  3. Open Numeric and percentage detents again and tap Open to 100% (3).
before after
step 1 (cold) 812.0pt 812.0pt
step 3 (recycled view) 874.0pt 812.0pt

874 is the full screen height and 812 is that minus the 62pt top safe-area
inset. In step 3 before the change, the sheet header visibly overlaps the clock
and the Dynamic Island.

Test plan

  • Ran the repro above before and after the change on an iPhone 17 Pro
    simulator (iOS 26.3).
  • Confirmed Under status bar still extends under the status bar after the
    change, so the prop itself is unaffected.
  • bun run lint, bun run typecheck, bun run prepare.

`updateProps` diffs `extendUnderStatusBar` against the `_extendUnderStatusBar`
ivar rather than `oldProps`, because Fabric keeps `_props` across recycling.
`prepareForRecycle` resets that ivar but never pushes the reset down to the
hosting view, and `resetSheetState` leaves the flag alone. A view recycled
from a sheet that set `extendUnderStatusBar` therefore reads
`false != NO`, skips the setter, and keeps the flag on, so
`resolvedMaxDetentHeight` drops the status-bar overlap subtraction and a
full-height detent runs under the status bar for a sheet that never asked
for it.

Reset the hosting view alongside the ivar so the two stay in sync, the way
`restoreInlinePresentation` keeps `_nativeOverlay` in sync.
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