SafeAreaView rendering children under unsafe area
Description
SafeAreaView from doesn't respect safe areas on iOS when used alongside an animated view with {translateY: 0} in the same screen. Using a basic view and padding it with insets from useSafeAreaInsets seems to work normally.
This was originally reported here: https://github.com/kirillzyusko/react-native-keyboard-controller/issues/809
Steps to reproduce
- Mount a screen that has both SafeAreaView and an animated view with translateY = 0
- SafeAreaView child renders under "Unsafe" area.
Snack or a link to a repository
https://snack.expo.dev/@rodsar/safeareaview-issue
Safe Area Context version
5.2.0
React Native version
0.76
Platforms
iOS
Architecture
None
Build type
None
Device
iOS simulator
Device model
No response
Acknowledgements
Yes
You're not importing SafeAreaView from this library - it's coming from react-native. Was that just a typo in the example, or is this an actual issue?
Sorry for that, yeah it was a typo, i edited the example and got the same behavior
What happens if you change the <> fragment into <View style={{ flex:1 }}>?
Yeah i just tried adding a parent view and got the same result
I can see the issue in the expo snack you provided. I suspect there's some dodgy interaction with reanimated that's breaking the layout and stopping the padding being applied to the SafeAreaView. I'm not 100% sure it's this side - as far as I'm aware we're doing everything correctly
I'll leave this issue open in case it is our side - but to set some expectations, I don't think it will get looked at. You're welcome to have an attempt at debugging it, we're good at merging PRs people send us
I just created issue on Reanimated repository. https://github.com/software-mansion/react-native-reanimated/issues/7220
We have noticed something similar happening for our app without using a reanimated View directly. It seems to happen randomly and reinstalling the app fixes it.
We have a nested SafeAreaView where the top edge of the child doesn't get applied. Something like this:
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
className="flex-1">
<SafeAreaView edges={['left', 'right', 'bottom']}>
<SafeAreaView edges={['top']}>...</SafeAreaView> // Top doesn't get applied
...
</SafeAreaView>
</KeyboardAvoidingView>
I'm have been able to replicate it once locally on a simulator (iPhone 15 Pro - iOS 17.2), and by replacing the inner SafeAreaView with paddings from useSafeAreaInsets fixes it.
I'm sorry for being a bit vague but the issue seems to have come out of thin air.
Try to remove <GestureHandlerRootView
Same issue here. I had to switch to using useSafeAreaInsets. I'm not using an animated view though. I think this is just a general iOS issue. You can see how I can make changes to the CSS and it starts working, but then reverts right back on reloading the app in thius video.
https://github.com/user-attachments/assets/260e5c31-5687-49bc-aca1-12c22aeeaf42
Same issue here. I had to switch to using useSafeAreaInsets. I'm not using an animated view though. I think this is just a general iOS issue. You can see how I can make changes to the CSS and it starts working, but then reverts right back on reloading the app in thius video.
issuewithSafeAreaView.mov
Same issue. React native version: 0.81.4 react-native-safe-area-context: 5.6.1
As far as I can tell, there is some kind of bug on iOS+Fabric where <SafeAreaView> intermittently fails to set up its safe area insets correctly during initialization, leaving them set to zero for the lifetime of the component. After reproducing this (by setting up my app to delete and recreate a view repeatedly and observing that the behavior is inconsistent), I poked around and managed to make the problem disappear with the following patch:
diff --git a/ios/Fabric/RNCSafeAreaViewComponentView.mm b/ios/Fabric/RNCSafeAreaViewComponentView.mm
index 827fa4d..c0b091c 100644
--- a/ios/Fabric/RNCSafeAreaViewComponentView.mm
+++ b/ios/Fabric/RNCSafeAreaViewComponentView.mm
@@ -93,6 +93,9 @@ using namespace facebook::react;
- (void)updateStateIfNecessary
{
+ if (_providerView == nil) {
+ _providerView = [self findNearestProvider];
+ }
if (_providerView == nil) {
return;
}
@@ -149,6 +152,7 @@ using namespace facebook::react;
- (void)updateState:(State::Shared const &)state oldState:(State::Shared const &)oldState
{
_state = std::static_pointer_cast<RNCSafeAreaViewShadowNode::ConcreteState const>(state);
+ [self updateStateIfNecessary];
}
- (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask
However, I think this is very much addressing a symptom rather than the cause, and I’m pretty sure this change just causes other bugs (in particular, since the behavior is that _providerView isn’t being updated, it might get out of sync in more subtle ways)—it seems like something about the lifecycle management in this component view needs to be adjusted, but this is where I ran out of diagostic abilities and I don’t understand react-native internals nearly well enough to have a suggestion.
@jacobp100 it seems like you have some context here—based on the above information can you point me in the right direction for a good fix?
actually, I think this bug report might be two unrelated issues: it sounds like @RodSarhan and @kdwkr’s problem with animated views is consistent, whereas everyone else’s problems with non-animated views is intermittent?
I'm experiencing something similar on 5.6.1 and react-native 0.82.0. I have two Modals (react-native Modal object) with SafeAreaView. On one, i had changed to this lib SafeAreaView instead of the react-native one as i saw the notice of deprecation, on the other i had not at first. The one with this lib would have the same issue as @NorseGaud if started first whereas the old way one had no issue. If i open the modal with the old way first then no bug in the new way one. If i restart the app and go on new way first, the bug happen again.
If i use old way for both, they both have no issue. If i use new way for both, they both have the issue. I have a SafeAreaView with the new way for the main part of the app that seems to work as intended.
Same thing here — adding Animated.View to the screen breaks SafeAreaView.
The interesting thing though — I'm using two separate SafeAreaViews, and the one that actually contains the animated view inside of it is working fine, but the other one, that have nothing to do with it, breaks.