Skip to content

Commit 00a192f

Browse files
committed
fix(core): iOS scrollToVerticalOffset lands past the offset when there is a content inset
Both scrollTo*Offset methods asked UIKit to scroll a viewport-sized rect into view. With a bottom (or right) content inset that rect cannot fit in the visible area, so UIKit aligned its far edge instead and the scroll ended contentInset.bottom past the requested offset — a chat scrolling to its last message with a docked composer inset overshot by the inset every time. The offset is now set directly, clamped to the range a user scroll can reach, which also matches what Android does.
1 parent 4db0e64 commit 00a192f

1 file changed

Lines changed: 16 additions & 6 deletions

File tree

packages/core/ui/scroll-view/index.ios.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,27 @@ export class ScrollView extends ScrollViewBase {
153153
this.updateContentInsetAdjustmentBehavior(value);
154154
}
155155

156+
// The offset is set directly rather than through scrollRectToVisible with a
157+
// viewport-sized rect: that rect cannot fit inside a content inset, so UIKit
158+
// would land contentInset.bottom (or .right) past the requested offset. The
159+
// value is clamped to the range a user scroll can reach.
156160
public scrollToVerticalOffset(value: number, animated: boolean) {
157-
if (this.nativeViewProtected && this.orientation === 'vertical' && this.isScrollEnabled) {
158-
const bounds = this.nativeViewProtected.bounds.size;
159-
this.nativeViewProtected.scrollRectToVisibleAnimated(CGRectMake(0, value, bounds.width, bounds.height), animated);
161+
const nativeView = this.nativeViewProtected;
162+
if (nativeView && this.orientation === 'vertical' && this.isScrollEnabled) {
163+
const inset = nativeView.adjustedContentInset;
164+
const min = -inset.top;
165+
const max = Math.max(min, nativeView.contentSize.height + inset.bottom - nativeView.bounds.size.height);
166+
nativeView.setContentOffsetAnimated(CGPointMake(nativeView.contentOffset.x, Math.min(Math.max(value, min), max)), animated);
160167
}
161168
}
162169

163170
public scrollToHorizontalOffset(value: number, animated: boolean) {
164-
if (this.nativeViewProtected && this.orientation === 'horizontal' && this.isScrollEnabled) {
165-
const bounds = this.nativeViewProtected.bounds.size;
166-
this.nativeViewProtected.scrollRectToVisibleAnimated(CGRectMake(value, 0, bounds.width, bounds.height), animated);
171+
const nativeView = this.nativeViewProtected;
172+
if (nativeView && this.orientation === 'horizontal' && this.isScrollEnabled) {
173+
const inset = nativeView.adjustedContentInset;
174+
const min = -inset.left;
175+
const max = Math.max(min, nativeView.contentSize.width + inset.right - nativeView.bounds.size.width);
176+
nativeView.setContentOffsetAnimated(CGPointMake(Math.min(Math.max(value, min), max), nativeView.contentOffset.y), animated);
167177
}
168178
}
169179

0 commit comments

Comments
 (0)