Skip to content

Commit 4a4424f

Browse files
committed
[ScrollView][iOS] Change padding to use max of safe area inset/container value
1 parent d5e5bd0 commit 4a4424f

4 files changed

Lines changed: 36 additions & 21 deletions

File tree

‎packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollContentViewShadowNode.cpp‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "ScrollContentViewShadowNode.h"
99

10+
#include <algorithm>
1011
#include <array>
1112

1213
namespace facebook::react {
@@ -78,16 +79,18 @@ StyleLength resolvedPadding(
7879
}
7980

8081
/*
81-
* A percentage padding keeps the edge as the style set it, with no inset
82-
* added, since Yoga holds one length per edge and a percentage has no point
83-
* value to add to.
82+
* The safe area is a floor on the padding, as it is on UIKit's layout
83+
* margins: the edge takes the larger of its style padding and the inset. A
84+
* percentage padding keeps the edge as the style set it, with no inset
85+
* applied, since Yoga holds one length per edge and a percentage has no point
86+
* value to compare.
8487
*/
8588
StyleLength padded(StyleLength value, Float inset) {
8689
if (inset == 0 || value.isPercent()) {
8790
return value;
8891
}
89-
auto base = value.isPoints() ? value.value().unwrap() : 0.0f;
90-
return StyleLength::points(base + inset);
92+
auto base = value.isPoints() ? Float{value.value().unwrap()} : Float{0};
93+
return StyleLength::points(std::max(base, inset));
9194
}
9295

9396
} // namespace

‎packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollContentViewShadowNode.h‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ extern const char ScrollContentViewComponentName[];
2121

2222
/*
2323
* `ShadowNode` for the <ScrollContentView> component, the content container of
24-
* a <ScrollView>. It pads its content by the safe area in its state, on top of
25-
* the padding its style sets.
24+
* a <ScrollView>. It pads its content by at least the safe area in its state,
25+
* keeping any larger padding its style sets.
2626
*/
2727
class ScrollContentViewShadowNode final : public ConcreteViewShadowNode<
2828
ScrollContentViewComponentName,
@@ -46,8 +46,8 @@ class ScrollContentViewShadowNode final : public ConcreteViewShadowNode<
4646
void applySafeAreaPadding();
4747

4848
/*
49-
* Pads `style` by `state.safeAreaPadding`, on top of the padding `style`
50-
* resolves to on each edge, following the precedence Yoga gives the padding
49+
* Raises the padding `style` resolves to on each edge to at least
50+
* `state.safeAreaPadding`, following the precedence Yoga gives the padding
5151
* edges for `layoutDirection`. An edge with a percentage padding keeps it
5252
* and gets no inset.
5353
*/

‎packages/react-native/ReactCommon/react/renderer/components/scrollview/tests/ScrollContentViewTest.cpp‎

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,28 +47,39 @@ TEST(ScrollContentViewShadowNodeTest, leavesStyleWithoutInsets) {
4747
EXPECT_FALSE(style.padding(Edge::Start).isDefined());
4848
}
4949

50-
TEST(ScrollContentViewShadowNodeTest, padsRowOnTopOfResolvedPadding) {
50+
TEST(ScrollContentViewShadowNodeTest, raisesRowPaddingToTheInset) {
5151
yoga::Style style;
5252
style.setPadding(Edge::Horizontal, StyleLength::points(16));
5353

5454
ScrollContentViewShadowNode::padForSafeArea(
5555
style, stateWith({.left = 0, .top = 0, .right = 84, .bottom = 0}), kLTR);
5656

5757
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(16));
58-
EXPECT_EQ(style.padding(Edge::End), StyleLength::points(100));
58+
EXPECT_EQ(style.padding(Edge::End), StyleLength::points(84));
5959
EXPECT_FALSE(style.padding(Edge::Left).isDefined());
6060
EXPECT_FALSE(style.padding(Edge::Right).isDefined());
6161
}
6262

63+
TEST(ScrollContentViewShadowNodeTest, keepsPaddingLargerThanTheInset) {
64+
yoga::Style style;
65+
style.setPadding(Edge::Horizontal, StyleLength::points(100));
66+
67+
ScrollContentViewShadowNode::padForSafeArea(
68+
style, stateWith({.left = 0, .top = 0, .right = 84, .bottom = 0}), kLTR);
69+
70+
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(100));
71+
EXPECT_EQ(style.padding(Edge::End), StyleLength::points(100));
72+
}
73+
6374
TEST(ScrollContentViewShadowNodeTest, prefersLogicalOverPhysicalEdge) {
6475
yoga::Style style;
6576
style.setPadding(Edge::Left, StyleLength::points(4));
66-
style.setPadding(Edge::Start, StyleLength::points(12));
77+
style.setPadding(Edge::Start, StyleLength::points(40));
6778

6879
ScrollContentViewShadowNode::padForSafeArea(
6980
style, stateWith({.left = 20, .top = 0, .right = 0, .bottom = 0}), kLTR);
7081

71-
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(32));
82+
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(40));
7283
}
7384

7485
TEST(ScrollContentViewShadowNodeTest, keepsPercentageAndSkipsInsetOnThatEdge) {
@@ -92,7 +103,7 @@ TEST(ScrollContentViewShadowNodeTest, padsPointsEdgeBesideAPercentageEdge) {
92103
style, stateWith({.left = 20, .top = 0, .right = 84, .bottom = 0}), kLTR);
93104

94105
EXPECT_EQ(style.padding(Edge::Start), StyleLength::percent(10));
95-
EXPECT_EQ(style.padding(Edge::End), StyleLength::points(88));
106+
EXPECT_EQ(style.padding(Edge::End), StyleLength::points(84));
96107
}
97108

98109
TEST(ScrollContentViewShadowNodeTest, padsPhysicalEdgesInRTL) {
@@ -103,7 +114,7 @@ TEST(ScrollContentViewShadowNodeTest, padsPhysicalEdgesInRTL) {
103114
style, stateWith({.left = 0, .top = 0, .right = 84, .bottom = 0}), kRTL);
104115

105116
// In RTL the start edge is on the right, where the inset is.
106-
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(96));
117+
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(84));
107118
EXPECT_FALSE(style.padding(Edge::End).isDefined());
108119
}
109120

@@ -118,15 +129,15 @@ TEST(ScrollContentViewShadowNodeTest, readsSwappedPhysicalEdgesInRTL) {
118129
kRTL);
119130

120131
// Left is copied over start at layout, so it lands on the right, and the
121-
// inset on the left adds to the end edge.
132+
// inset on the left raises the end edge.
122133
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(8));
123-
EXPECT_EQ(style.padding(Edge::End), StyleLength::points(86));
134+
EXPECT_EQ(style.padding(Edge::End), StyleLength::points(84));
124135
EXPECT_FALSE(style.padding(Edge::Left).isDefined());
125136
}
126137

127138
TEST(ScrollContentViewShadowNodeTest, readsSwappedPhysicalEdgesInLTRSubtree) {
128139
yoga::Style style;
129-
style.setPadding(Edge::Left, StyleLength::points(8));
140+
style.setPadding(Edge::Left, StyleLength::points(100));
130141
style.setPadding(Edge::Start, StyleLength::points(2));
131142

132143
ScrollContentViewShadowNode::padForSafeArea(
@@ -136,7 +147,7 @@ TEST(ScrollContentViewShadowNodeTest, readsSwappedPhysicalEdgesInLTRSubtree) {
136147

137148
// The swap applies to the whole tree, so left is still copied over start,
138149
// which a node laid out left to right keeps on the left.
139-
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(92));
150+
EXPECT_EQ(style.padding(Edge::Start), StyleLength::points(100));
140151
EXPECT_FALSE(style.padding(Edge::End).isDefined());
141152
}
142153

@@ -147,8 +158,8 @@ TEST(ScrollContentViewShadowNodeTest, padsColumnForHorizontalScrollView) {
147158
ScrollContentViewShadowNode::padForSafeArea(
148159
style, stateWith({.left = 0, .top = 20, .right = 0, .bottom = 34}), kLTR);
149160

150-
EXPECT_EQ(style.padding(Edge::Top), StyleLength::points(24));
151-
EXPECT_EQ(style.padding(Edge::Bottom), StyleLength::points(38));
161+
EXPECT_EQ(style.padding(Edge::Top), StyleLength::points(20));
162+
EXPECT_EQ(style.padding(Edge::Bottom), StyleLength::points(34));
152163
EXPECT_EQ(style.padding(Edge::Vertical), StyleLength::points(4));
153164
}
154165

‎packages/rn-tester/js/examples/ScrollView/ScrollViewExample.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,7 @@ const styles = StyleSheet.create({
18321832
},
18331833
fullScreenContentContainer: {
18341834
backgroundColor: '#aae3b6',
1835+
paddingHorizontal: 16,
18351836
},
18361837
fullScreenControls: {
18371838
backgroundColor: '#eeeeee',

0 commit comments

Comments
 (0)