Skip to content

Commit 8874509

Browse files
committed
Add pinWindowTo prop for iOS.
1 parent d9b16e9 commit 8874509

4 files changed

Lines changed: 20 additions & 0 deletions

File tree

Libraries/Components/ScrollView/ScrollView.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,15 @@ type IOSProps = $ReadOnly<{|
248248
* @platform ios
249249
*/
250250
snapToAlignment?: ?('start' | 'center' | 'end'),
251+
/**
252+
* Pins the currently scrolled window to either the top of its content or
253+
* the bottom. The default behavior is "top". That means when the content in
254+
* the scroll view changes we will keep the offset of the scroll view from
255+
* the top of the content the same. If we set this prop to "bottom" then
256+
* when the content of the scroll view changes we will keep the offset from
257+
* the bottom of the content the same.
258+
*/
259+
pinToButton?: ?('top' | 'bottom'),
251260
/**
252261
* The current scale of the scroll view content. The default value is 1.0.
253262
* @platform ios

React/Views/ScrollView/RCTScrollView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
@property (nonatomic, assign) BOOL snapToStart;
5151
@property (nonatomic, assign) BOOL snapToEnd;
5252
@property (nonatomic, copy) NSString *snapToAlignment;
53+
@property (nonatomic, copy) NSString *pinWindowTo;
5354

5455
// NOTE: currently these event props are only declared so we can export the
5556
// event names to JS - we don't call the blocks directly because scroll events

React/Views/ScrollView/RCTScrollView.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,20 +966,29 @@ - (CGPoint)calculateOffsetForContentSize:(CGSize)newContentSize
966966
CGSize oldContentSize = _scrollView.contentSize;
967967
CGSize viewportSize = [self _calculateViewportSize];
968968

969+
BOOL updatedOffsetY = false;
969970
BOOL fitsinViewportY = oldContentSize.height <= viewportSize.height && newContentSize.height <= viewportSize.height;
970971
if (newContentSize.height < oldContentSize.height && !fitsinViewportY) {
971972
CGFloat offsetHeight = oldOffset.y + viewportSize.height;
972973
if (oldOffset.y < 0) {
973974
// overscrolled on top, leave offset alone
975+
updatedOffsetY = true;
974976
} else if (offsetHeight > oldContentSize.height) {
975977
// overscrolled on the bottom, preserve overscroll amount
976978
newOffset.y = MAX(0, oldOffset.y - (oldContentSize.height - newContentSize.height));
979+
updatedOffsetY = true;
977980
} else if (offsetHeight > newContentSize.height) {
978981
// offset falls outside of bounds, scroll back to end of list
979982
newOffset.y = MAX(0, newContentSize.height - viewportSize.height);
983+
updatedOffsetY = true;
980984
}
981985
}
982986

987+
if (!updatedOffsetY && [self.pinWindowTo isEqualToString:@"bottom"]) {
988+
CGFloat oldOffsetBottom = oldContentSize.height - (oldOffset.y + viewportSize.height);
989+
newOffset.y = newContentSize.height - viewportSize.height - oldOffsetBottom;
990+
}
991+
983992
BOOL fitsinViewportX = oldContentSize.width <= viewportSize.width && newContentSize.width <= viewportSize.width;
984993
if (newContentSize.width < oldContentSize.width && !fitsinViewportX) {
985994
CGFloat offsetHeight = oldOffset.x + viewportSize.width;

React/Views/ScrollView/RCTScrollViewManager.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ - (UIView *)view
8686
RCT_EXPORT_VIEW_PROPERTY(snapToStart, BOOL)
8787
RCT_EXPORT_VIEW_PROPERTY(snapToEnd, BOOL)
8888
RCT_EXPORT_VIEW_PROPERTY(snapToAlignment, NSString)
89+
RCT_EXPORT_VIEW_PROPERTY(pinWindowTo, NSString)
8990
RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint)
9091
RCT_EXPORT_VIEW_PROPERTY(onScrollBeginDrag, RCTDirectEventBlock)
9192
RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)

0 commit comments

Comments
 (0)