Skip to content

fix(ios): show a page that SwiftUI hosts again after setPage - #1203

Draft
KisaneNeko wants to merge 1 commit into
callstack:mainfrom
KisaneNeko:fix/ios-blank-page-after-setpage
Draft

KisaneNeko wants to merge 1 commit into
callstack:mainfrom
KisaneNeko:fix/ios-blank-page-after-setpage

Conversation

@KisaneNeko

Copy link
Copy Markdown
Contributor

Summary

Fixes #1202. It surfaced while reviewing #1193, whose repro screen triggers it, but it happens on 9.0.10 without that PR.

On iOS, a page shown by setPage can be blank. The pager reports the page as selected, but the page has no content and stays empty after the pager stops.

reproduction

Before (9.0.10) After (this PR)

What happened

  • Each page is one React Native view. PageChildViewController adds it to its own view once, in viewDidLoad.
  • SwiftUI can keep two controllers alive for the same page. A view has one parent, so the newer controller takes the page view from the older one.
  • When SwiftUI removes the newer controller, the page view is left with no parent. When the page shows again, SwiftUI can use the older controller, which does not add the view again. The cell is on screen but empty.

I traced this with logging: in the blank state, the visible cell's controller wraps the right page view, but that view has no superview.

Fix

PagerScrollDelegate now handles collectionView(_:willDisplay:forItemAt:). Before a cell shows, its PageChildViewController checks its page view. If the view has no parent, the controller takes it back.

  • The "no parent" check matters. At willDisplay a reused cell can still hold an older controller while SwiftUI is about to show a newer one. Taking the view unconditionally blanked page 0 in the basic example, which the basic-pager e2e tests caught.
  • viewDidLoad is unchanged.

Test Plan

What's required for testing (prerequisites)?

iOS simulator or device. The new example screen, which re-renders the event log above the pager on every page, focus and keyboard event.

What are the steps to reproduce (after prerequisites)?

Added Issue #1202 Blank Page Repro to the example app and e2e/tests/issues/issue-1202-blank-page.e2e.ts:

  1. On page 0, tap the input, type, and tap Go 1.
  2. Do the same on page 1 (Go 2), page 2 (Go 1), page 1 (Go 0) and page 0 (Go 1).
  3. Tap Go 2.
  4. Before: the status says Selected: 2, but the pager area is empty.
  5. After: page 2 shows.

Verified

  • iPhone 17 Pro Max simulator, iOS 26.5, release build.
  • The new e2e test fails 3/3 without the fix (page 2's label is missing while the status says Selected: 2) and passes 3/3 with it.
  • Full iOS e2e suite with the fix: 22/22 tests pass.
  • typecheck (root, example and e2e), lint (no warnings in the changed files), test and prettier pass.
  • Not tested on iOS 15 or visionOS, or on a physical device.

Android is untouched.

Compatibility

OS Implemented
iOS ✅
Android n/a

Checklist

  • Tested on a device and a simulator: simulator only (iPhone 17 Pro Max, iOS 26.5), not on physical hardware
  • I added the documentation in README.md: not applicable
  • I updated the typed files (TS and Flow): not applicable

🤖 Generated with Claude Code

Each page is one React Native view, which PageChildViewController adds to
its own view once, in viewDidLoad. SwiftUI can keep two controllers alive
for the same page. The newer one takes the page view, and when SwiftUI
removes it the view is left with no parent. If SwiftUI then shows the older
controller again, nothing adds the view back, and the page is blank.

When the collection view is about to display a cell, its controller now takes
the page view back if the view has no parent. A view that a newer controller
holds is left alone.

Fixes callstack#1202
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.

[iOS] Page is blank after setPage when the screen re-renders on pager events

1 participant