Skip to content
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

Add support for viewOffset and viewPosition to maintainVisibleContentPosition #44863

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,8 @@ export interface ScrollViewPropsIOS {
| {
autoscrollToTopThreshold?: number | null | undefined;
minIndexForVisible: number;
viewOffset?: number | null | undefined;
viewPosition?: number | null | undefined;
}
| undefined;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ export type Props = $ReadOnly<{|
maintainVisibleContentPosition?: ?$ReadOnly<{|
minIndexForVisible: number,
autoscrollToTopThreshold?: ?number,
viewOffset?: ?number,
viewPosition?: ?number,
|}>,
/**
* Called when the momentum scroll starts (scroll which occurs as the ScrollView glides to a stop).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export type ScrollViewNativeProps = $ReadOnly<{
maintainVisibleContentPosition?: ?$ReadOnly<{
minIndexForVisible: number,
autoscrollToTopThreshold?: ?number,
viewOffset?: ?number,
viewPosition?: ?number,
}>,
maximumZoomScale?: ?number,
minimumZoomScale?: ?number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2037,6 +2037,8 @@ exports[`public API should not change unintentionally Libraries/Components/Scrol
maintainVisibleContentPosition?: ?$ReadOnly<{
minIndexForVisible: number,
autoscrollToTopThreshold?: ?number,
viewOffset?: ?number,
viewPosition?: ?number,
}>,
maximumZoomScale?: ?number,
minimumZoomScale?: ?number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,8 @@ - (void)_adjustForMaintainVisibleContentPosition
}

std::optional<int> autoscrollThreshold = props.maintainVisibleContentPosition.value().autoscrollToTopThreshold;
int viewOffset = props.maintainVisibleContentPosition.value().viewOffset;
float viewPosition = props.maintainVisibleContentPosition.value().viewPosition;
BOOL horizontal = _scrollView.contentSize.width > self.frame.size.width;
// TODO: detect and handle/ignore re-ordering
if (horizontal) {
Expand All @@ -821,7 +823,8 @@ - (void)_adjustForMaintainVisibleContentPosition
if (autoscrollThreshold) {
// If the offset WAS within the threshold of the start, animate to the start.
if (x <= autoscrollThreshold.value()) {
[self scrollToOffset:CGPointMake(0, _scrollView.contentOffset.y) animated:YES];
CGFloat offset = MAX(0, deltaX - self.frame.size.width) * viewPosition - viewOffset;
[self scrollToOffset:CGPointMake(offset, _scrollView.contentOffset.y) animated:YES];
}
}
}
Expand All @@ -835,7 +838,8 @@ - (void)_adjustForMaintainVisibleContentPosition
if (autoscrollThreshold) {
// If the offset WAS within the threshold of the start, animate to the start.
if (y <= autoscrollThreshold.value()) {
[self scrollToOffset:CGPointMake(_scrollView.contentOffset.x, 0) animated:YES];
CGFloat offset = MAX(0, deltaY - self.frame.size.height) * viewPosition - viewOffset;
[self scrollToOffset:CGPointMake(_scrollView.contentOffset.x, offset) animated:YES];
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions packages/react-native/React/Views/ScrollView/RCTScrollView.m
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,8 @@ - (void)uiManagerWillPerformMounting:(RCTUIManager *)manager
return; // The prop might have changed in the previous UIBlocks, so need to abort here.
}
NSNumber *autoscrollThreshold = self->_maintainVisibleContentPosition[@"autoscrollToTopThreshold"];
NSNumber *viewOffset = self->_maintainVisibleContentPosition[@"viewOffset"];
NSNumber *viewPosition = self->_maintainVisibleContentPosition[@"viewPosition"];
// TODO: detect and handle/ignore re-ordering
if ([self isHorizontal:self->_scrollView]) {
CGFloat deltaX = self->_firstVisibleView.frame.origin.x - self->_prevFirstVisibleFrame.origin.x;
Expand All @@ -976,7 +978,8 @@ - (void)uiManagerWillPerformMounting:(RCTUIManager *)manager
if (autoscrollThreshold != nil) {
// If the offset WAS within the threshold of the start, animate to the start.
if (x <= [autoscrollThreshold integerValue]) {
[self scrollToOffset:CGPointMake(-leftInset, self->_scrollView.contentOffset.y) animated:YES];
CGFloat offset = MAX(0, deltaX - self.frame.size.width) * [viewPosition floatValue] - [viewOffset floatValue] - leftInset;
[self scrollToOffset:CGPointMake(offset, self->_scrollView.contentOffset.y) animated:YES];
}
}
}
Expand All @@ -992,7 +995,8 @@ - (void)uiManagerWillPerformMounting:(RCTUIManager *)manager
if (autoscrollThreshold != nil) {
// If the offset WAS within the threshold of the start, animate to the start.
if (y <= [autoscrollThreshold integerValue]) {
[self scrollToOffset:CGPointMake(self->_scrollView.contentOffset.x, -bottomInset) animated:YES];
CGFloat offset = MAX(0, deltaY - self.frame.size.height) * [viewPosition floatValue] - [viewOffset floatValue] - bottomInset;
[self scrollToOffset:CGPointMake(self->_scrollView.contentOffset.x, offset) animated:YES];
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,14 @@ public static class Config {

public final int minIndexForVisible;
public final @Nullable Integer autoScrollToTopThreshold;
public final int viewOffset;
public final int viewPosition;

Config(int minIndexForVisible, @Nullable Integer autoScrollToTopThreshold) {
Config(int minIndexForVisible, @Nullable Integer autoScrollToTopThreshold, int viewOffset, int viewPosition) {
this.minIndexForVisible = minIndexForVisible;
this.autoScrollToTopThreshold = autoScrollToTopThreshold;
this.viewOffset = viewOffset;
this.viewPosition = viewPosition;
}

static Config fromReadableMap(ReadableMap value) {
Expand All @@ -58,7 +62,15 @@ static Config fromReadableMap(ReadableMap value) {
value.hasKey("autoscrollToTopThreshold")
? value.getInt("autoscrollToTopThreshold")
: null;
return new Config(minIndexForVisible, autoScrollToTopThreshold);
int viewOffset =
value.hasKey("viewOffset")
? value.getInt("viewOffset")
: 0;
int viewPosition =
value.hasKey("viewPosition")
? value.getInt("viewPosition")
: 0;
return new Config(minIndexForVisible, autoScrollToTopThreshold, viewOffset, viewPosition);
}
}

Expand Down Expand Up @@ -122,7 +134,8 @@ private void updateScrollPositionInternal() {
mPrevFirstVisibleFrame = newFrame;
if (mConfig.autoScrollToTopThreshold != null
&& scrollX <= mConfig.autoScrollToTopThreshold) {
mScrollView.reactSmoothScrollTo(0, mScrollView.getScrollY());
int offset = Math.max(0, deltaX - mScrollView.getWidth()) * mConfig.viewPosition - mConfig.viewOffset;
mScrollView.reactSmoothScrollTo(offset, mScrollView.getScrollY());
}
}
} else {
Expand All @@ -133,7 +146,8 @@ private void updateScrollPositionInternal() {
mPrevFirstVisibleFrame = newFrame;
if (mConfig.autoScrollToTopThreshold != null
&& scrollY <= mConfig.autoScrollToTopThreshold) {
mScrollView.reactSmoothScrollTo(mScrollView.getScrollX(), 0);
int offset = Math.max(0, deltaY - mScrollView.getHeight()) * mConfig.viewPosition - mConfig.viewOffset;
mScrollView.reactSmoothScrollTo(mScrollView.getScrollX(), offset);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ inline void fromRawValue(
autoscrollToTopThreshold->second,
result.autoscrollToTopThreshold);
}
auto viewOffset = map.find("viewOffset");
if (viewOffset != map.end()) {
fromRawValue(context, viewOffset->second, result.viewOffset);
}
auto viewPosition = map.find("viewPosition");
if (viewPosition != map.end()) {
fromRawValue(context, viewPosition->second, result.viewPosition);
}
}

inline std::string toString(const ScrollViewSnapToAlignment& value) {
Expand Down Expand Up @@ -174,7 +182,9 @@ inline std::string toString(
}
return "{minIndexForVisible: " + toString(value.value().minIndexForVisible) +
", autoscrollToTopThreshold: " +
toString(value.value().autoscrollToTopThreshold) + "}";
toString(value.value().autoscrollToTopThreshold) +
", viewOffset: " + toString(value.value().viewOffset) +
", viewPosition: " + toString(value.value().viewPosition) + "}";
}

#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,20 @@ class ScrollViewMaintainVisibleContentPosition final {
public:
int minIndexForVisible{0};
std::optional<int> autoscrollToTopThreshold{};
int viewOffset{0};
float viewPosition{0};

bool operator==(const ScrollViewMaintainVisibleContentPosition& rhs) const {
return std::tie(this->minIndexForVisible, this->autoscrollToTopThreshold) ==
std::tie(rhs.minIndexForVisible, rhs.autoscrollToTopThreshold);
return std::tie(
this->minIndexForVisible,
this->autoscrollToTopThreshold,
this->viewOffset,
this->viewPosition) ==
std::tie(
rhs.minIndexForVisible,
rhs.autoscrollToTopThreshold,
rhs.viewOffset,
rhs.viewPosition);
}

bool operator!=(const ScrollViewMaintainVisibleContentPosition& rhs) const {
Expand Down
Loading