Skip to content

Fix iOS RefreshControl appearance after native navigation - #58694

Open
camchis wants to merge 1 commit into
react:mainfrom
camchis:fix/ios-refresh-control-appearance
Open

camchis wants to merge 1 commit into
react:mainfrom
camchis:fix/ios-refresh-control-appearance

Conversation

@camchis

@camchis camchis commented Sep 26, 2026 •

Copy link
Copy Markdown

Summary:

On iOS, RefreshControl applies style.backgroundColor to its hidden component view instead of the visible UIRefreshControl. Additionally, navigating to a native screen that changes UIRefreshControl.appearance() and returning can replace the existing RN control's background, tint and attributed title. Because the React props have not changed, ordinary prop diffing does not restore them.

Forward background colour updates to the native refresh control and reapply explicitly supplied appearance props after the control moves into a window. This restores backgroundColor, tintColor, title and titleColor after native navigation while preserving UIKit defaults for unspecified appearance. It does not require remounting or changing JS props.

Add four native regression tests and register them in RNTesterUnitTests. The tests cover background updates/removal, recycling, returning to a window with unchanged props, and preserving unspecified native appearance.

Changelog:

[IOS] [FIXED] - Respect RefreshControl backgroundColor and preserve its explicit colours and title after returning from native screens.

Test Plan:

Public, self-contained reproduction: react-native-refresh-control-repro.

The README includes build instructions, native navigation steps and expected values for all four appearance props. Both versions include the native regression test source.

Reproduced on RN 0.87.1, then tested the same native source change in a standalone source-built Release app on iOS 26.1. The affected upstream main component was also checked and still contains the original implementation.

Manual verification on iOS:

  1. Render a RefreshControl with a yellow background, blue tint, RN refresh title and magenta title colour.
  2. Push a native UIKit screen and perform a real pull to refresh. Its refresh callback sets a green background, orange tint and different attributed title through UIRefreshControl.appearance().
  3. Return to the existing RN screen and pull to refresh again. All four explicit RN values remain correct.
  4. Change all four RN props and repeat, including remounting the ScrollView. The updated values remain correct.

Before the change, the initial visible background was unset and all four values were replaced after the native appearance change. Styling only the native instance did not reproduce the override. After the change, both the visible UI and inspection of the actual UIKit properties match the RN props. The original reporter also verified the fix.

Four XCTest cases passed with zero failures using a local reproduction harness that compiles the added test source against patched RN 0.87.1. From reproductions/RefreshControlRepro:

xcodebuild test \
  -workspace ios/RefreshControlRepro.xcworkspace \
  -scheme RefreshControlNativeTests -configuration Release \
  -destination 'id=852CF6ED-E842-4BEB-97B5-98EC78D6B3E2' \
  -derivedDataPath ios/build/DerivedData \
  CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES -parallel-testing-enabled NO

Result: TEST SUCCEEDED, 4 tests, 0 failures, on iPhone 16 Pro Max simulator / iOS 26.1. This harness is available in the linked reproduction; its README gives the equivalent command using a simulator name.

From the repository root, both commands passed:

git diff --check
/opt/homebrew/opt/llvm/bin/clang-format --dry-run --Werror \
  packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTPullToRefreshViewComponentView.mm \
  packages/react-native/React/Tests/Mounting/RCTPullToRefreshViewComponentViewTests.mm

Before and after: each video shows the RN screen, navigation to the native UIKit screen, a native pull to refresh, navigation back, and the RN refresh appearance.

Before — the native green background and appearance override the RN values on return:

unpatched-native-roundtrip.mp4

After — RN retains refreshControl props on return from native screen:

patched-native-roundtrip.mp4

@meta-cla

meta-cla Bot commented Sep 26, 2026

Copy link
Copy Markdown

Hi @camchis!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla

meta-cla Bot commented Sep 26, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 26, 2026
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Sep 26, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant