Skip to content

iOS: status bar style resets to default when keyboard animates in/out #1495

Description

@davor-bauk-sh

📜 Description

When a screen uses KeyboardControllerView (via KeyboardProvider or useKeyboardHandler), the iOS status bar style is reset to its default whenever the keyboard animates in or out. On a light-themed screen configured with dark-content (dark icons), the icons turn white/invisible.

React Native's declarative <StatusBar barStyle="dark-content" /> does not self-correct because its internal _currentValues remains stale — _updatePropsStack only fires a native call when it sees a diff, and since _currentValues still shows dark-content, it skips the native call even though iOS has already reset the native state.

🔁 Steps to Reproduce

  1. Wrap a screen with KeyboardProvider or use useKeyboardHandler in a component.
  2. Set <StatusBar barStyle="dark-content" /> (light theme / dark icons).
  3. Focus a TextInput to show the keyboard.
  4. Observe: status bar icons become white (reset to light-content) while the keyboard animates in.
  5. Dismiss the keyboard — same reset occurs on the way out.

🔍 Root Cause

KeyboardControllerView.didMoveToWindow() calls keyboardTrackingView.attachToTopmostView(toWindow:), which adds a KeyboardTrackingView subview to the app's top view controller's view hierarchy during keyboard animation.

This view hierarchy mutation causes iOS to re-evaluate status bar appearance. iOS queries preferredStatusBarStyle from the key window's root view controller. During keyboard animation, the relevant window is UIRemoteKeyboardWindow (the private system keyboard input window), whose root view controller does not override preferredStatusBarStyle — so iOS falls back to .default / light-content.

React Native's StatusBar stack is not re-applied because it only calls the native module when _currentValues differs from the merged stack result, and the stale _currentValues hides the discrepancy.

✅ Current Workaround

Re-assert the style imperatively on every keyboard transition using useKeyboardHandler:

const reassertStatusBarStyle = useCallback(() => {
  StatusBar.setBarStyle(statusBarStyle, true);
}, [statusBarStyle]);

const reassertStatusBarStyleWorklet = useCallback(() => {
  'worklet';
  if (Platform.OS === 'ios') {
    scheduleOnRN(reassertStatusBarStyle);
  }
}, [reassertStatusBarStyle]);

useKeyboardHandler(
  { onStart: reassertStatusBarStyleWorklet, onEnd: reassertStatusBarStyleWorklet },
  [reassertStatusBarStyleWorklet],
);

This works but is boilerplate every consumer has to add and shouldn't be necessary.

💡 Suggested Fix

After attachToTopmostView() triggers a keyboard transition, call setNeedsStatusBarAppearanceUpdate() on the app's root view controller to force iOS to re-query preferredStatusBarStyle from the correct (app) view controller rather than the keyboard window's.

Alternatively, consider whether attachToTopmostView can be done in a way that avoids triggering iOS's status bar re-evaluation.

📋 Environment

react-native-keyboard-controller 1.21.7 (confirmed present on 1.21.11)
Platform iOS
RN architecture New Architecture (Fabric)

📸 Impact

Any app using KeyboardControllerView on a screen with a light theme and dark-content status bar will see the status bar icons disappear while the keyboard is animating.

Metadata

Metadata

Assignees

Labels

🍎 iOSiOS specific🐛 bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions