-
Notifications
You must be signed in to change notification settings - Fork 24.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New LayoutContext API to access the frame and safe area of the root view #20999
Conversation
Cc @yungsters @shergin, what do you think of this approach? |
This is pretty cool. My main concern would be that when the values change, we must trigger a re-render. With this approach, my understanding is that when something causes the safe area insets to change (e.g. orientation change), we would have to force a re-render with the new values. Ideally, we would not have to necessitate a re-render in JavaScript. But in reality, I think this solves the problem very elegantly and 99% of the time, people will not experience consequences of my concern. @shergin, I know you had more thoughts than anyone else about the future of |
@yungsters I think in a lot of cases the re-render will get blocked by some pure component up in the tree, and even if it doesn't I guess re-rendering on orientation change is not a bad thing. |
The main difference between this API and This API has the advantage of exposing the inset values to JS with a synchronous API so they can be used during the first render, this allow more flexibility to choose how the insets are being applied to views. The main drawback is that it only considers the position of the I think the APIs are kind of complementary for now since they both have different tradeoffs. |
2391d61
to
69edd8d
Compare
Finally got some time to look at this again!
What's missing:
|
@yungsters This should address your concern about re-rendering. |
|
I've been meaning to have a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sahrens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
Quick update - there are a lot of flow errors internally trying to pull this in. I'll work through them soon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sahrens has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts after working with this internally a bit (and some from earlier comments that might have been forgotten):
- re: @shergin's concern with exposing {x: 0, y: 0} - is that information really necessary? Should we just remove it? I think the problem is that it could change due to some native parent changing without notifying JS, so if JS is using the values, they might not be correct.
- We should fix the naming -
LayoutContext
feels like it would matchonLayout
, but it's only about the root - should probably call itRootViewLayoutContext
. We should also make the filename match the export (RootViewLayout.js
) - We should export the Context itself so it's easy to use from class components and hooks. The consumer would be used as
<RootViewLayoutContext.Consumer>{ctx => ...}</...>
- would be nice to get it working in Fabric too, but we can do that in followups.
- Did you test the behavior with Modal's that originate from smaller embedded root views? I wonder if we should do something simpler that's more about the entire App rather than the rootView - it would basically just report the screen dimensions most of the time, but on orientation change or split screen multitasking it would change.
Also little update: I’ve shipped this to production for a few weeks and so far iOS is solid but I got a crash that happens kind of infrequently on Android that I need to investigate before we can ship. |
|
(1) I'm more concerned with people relying on x/y and having them change without an event firing as opposed to just the initial value in javascript. (3) can you update this PR? I still see this:
(4) Fabric decouples starting the app with the initial props from the root view so it will be tricky to link the two for a clean initial render. |
@sahrens I'm still not sure I get (1), x/y should get updated whenever it changes (in |
I got this working internally but unfortunately it really hurt our performance benchmarks on iOS. I’m guessing the delay of bundleFinishedLoading is the most likely issue - any ideas? |
By this:
I mean that the action position of a RootView depends on all views around it and seems there is no easy/cheap way to listen for those changes. :( |
Have you fixed the performance? |
@sahrens Can you give me more info about what the benchmark is and what it measures? |
@shergin In this case maybe we should just make x,y always 0,0 like UIKit |
@janicduplessis - sorry for the delay. The performance benchmark simply measures initial render time, that is, the time from the user tapping a button to the new content being visible on screen. The test in question is for the Marketplace tab in the Facebook app, so it's a brownfield cold start, and the "done" time is marked in native code via a special native component that listens for something like |
@sahrens Thanks for the info, would it be possible to re-run the tests without the change to delay of bundleFinishedLoading. This would help isolate the issue. I know @axe-fb did some work on perf measurement in OSS. Could be interesting to see if we can include that in RNTester and maybe integration tests. |
// do a best effort polyfill. | ||
// TODO: Use the DisplayCutout api when we target sdk 28+ | ||
RootViewInsets windowInsets; | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you have this version check?WindowInsets
is actually available from SDK level 20.
Any updates on this? |
Building something similar outside of core for now, gonna loop back if it makes sense to include at some point. https://github.com/th3rdwave/react-native-safe-area-context |
Motivation:
There is currently no way to access layout metrics from the root view that contains the component tree. This can be useful when you need to access these values from JS synchronously without having to rely on the
onLayout
event of a top level view which is async and can cause flickers.It is roughly the equivalent of the
Dimensions
module for screen / window size but instead based on theRootView
that the component is rendered in with a more modernContext
based api. This also be useful for hybrid apps where the root view might not cover the entire screen.Another goal of this is to be able to expose
safeAreaInset
values which cannot be accessed currently from JS.SafeAreaView
is sometimes not flexible enough if we want to apply the insets manually in JS (for example use margins instead of padding).Test Plan:
iOS RNTester
The red square is a view that has width and height set to the LayoutContext frame with padding equal to the LayoutContext safeAreaInsets. The blue square is a view that fills the content of its parent to show the safe areas.
Sample code from the RNTester example to illustrate this
iOS
Test that safe area insets and frame match the actual app frame for iOS 11 and iOS < 11 (polyfilled version)
Test insets on iPhone X with screen rotation
Test that showing and hiding the status bar updates the safe area. This doesn't work on iOS < 11 but also doesn't work for the SafeAreaView component. I couldn't find a way to make it work since the status bar frame changed event is not triggered when hiding / showing. Also kvo for the statusBarHidden prop did not work either for some reason... This isn't a big deal anyway.
Test toggling the on-call status bar
Android
Release Notes:
[General] [Added] - New LayoutContext API to access the frame and safe area of the root view