Skip to content

[iOS] New Arch: PagerView renders a blank page when reactViewController() is nil on first didMoveToWindow #1089

Description

@IsaacIsrael

Environment

Package Version
react-native-pager-view 8.0.0 – 8.0.2 (current master)
react-native 0.85.x
Architecture New Architecture (Fabric), iOS

Description

On the New Architecture iOS path, PagerView intermittently renders a permanently blank page — no content is visible even though children are mounted with valid frames, and switching pages does not recover it.

It is timing-dependent, so it reproduces inconsistently — most often when the pager is mounted as part of a screen that is still being attached (e.g. behind a navigation transition, inside a freshly-pushed screen or a tab).

Root cause

In ios/PagerViewProvider.swift, setupView() assigns self.hostingController before confirming that reactViewController() is non-nil:

private func setupView() {
  if self.hostingController != nil {
    return
  }

  self.hostingController = UIHostingController(   // assigned unconditionally
    rootView: PagerView(props: props, delegate: delegate),
    ignoreSafeArea: true
  )
  if let hostingController, let parentViewController = reactViewController() {
    // addChild / addSubview / pinEdges happen ONLY here
  }
}

setupView() is called from didMoveToWindow(). On the first pass reactViewController() can be nil (the view is in a window but its owning view controller isn't attached yet). When that happens:

  1. The if let … reactViewController() block is skipped, so the SwiftUI hosting view is never added as a subview and never pinned → its frame stays .zero → the page is blank.
  2. self.hostingController is now non-nil, so the next didMoveToWindow() (when the VC is available) hits the if self.hostingController != nil { return } early-return and setup never retries.

Net result: a blank pager for the lifetime of the view.

Evidence (device logs)

With temporary logging in PagerViewProvider / PageChildViewController:

setupView creating hostingController ... reactVC=0          // reactViewController() was nil
didMoveToWindow hasWindow=0 ...                              // detached
didMoveToWindow hasWindow=1 provider.bounds={390,754} ...    // re-attached, VC now exists
setupView SKIPPED (hostingController already exists)         // never retries
provider.layoutSubviews bounds={390,653} hostingView.frame={0,0,0,0}   // host never sized
provider.layoutSubviews bounds={390,700} hostingView.frame={0,0,0,0}   // stays blank

provider.bounds is correct while hostingController.view.frame stays {0,0,0,0}.

Reproducible steps

  1. iOS, New Architecture enabled.
  2. Render a PagerView inside a screen that is pushed/attached (so reactViewController() is nil on the first didMoveToWindow).
  3. The page area is blank (intermittently); switching pages does not help.

Proposed fix

Guard on reactViewController() before creating/assigning the hosting controller (so a failed pass leaves hostingController nil and can retry), and retry setupView() from layoutSubviews() so attachment happens as soon as the VC hierarchy is ready.

PR: #1088

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions