Permalink
Browse files

Add 'contentInsetAdjustmentBehavior' (new in iOS 11) to ScrollView

Summary:
In iOS11, Apple added a new layout feature called "Safe Areas" (this blog post talks a bit about it: https://www.bignerdranch.com/blog/wwdc-2017-large-titles-and-safe-area-layout-guides/).

UIScrollView is one component that is affected by this change in Apple's API. When the `contentInsetAdjustmentBehavior` is set to `automatic`, for example, it will adjust the insets (and override any manually set insets) automatically based on whether or not there's a UINavigationBar, a UITabBar, a visible status bar, etc on the screen. Frustratingly, Apple decided to default to `Automatic` for this behavior, which will cause any apps that set contentInset/contentContainerStyle padding to have their values offset by, at the very least, the size of the status bar, when they compile their app for iOS 11. Here's more information about this behavior: https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior?language=objc

Mostly, this is a really straightforward change -- it simply adds a new iOS-only prop to ScrollView that allows setting `contentInsetAdjustmentBehavior`. But I did decide to default the behavior to `never`, so that it mimics the behavior we've seen in iOS < 11. I think it's good to keep something as crucial as scrollview content insets non-magical, and also keep it behaving similarly between platforms.
Closes #15023

Differential Revision: D5441491

Pulled By: shergin

fbshipit-source-id: 7b56ea290f7f6eca5f1d996ff8488f40b866c2e6
  • Loading branch information...
Adam Miskiewicz authored and facebook-github-bot committed Jul 25, 2017
1 parent fca3000 commit 6e28b39d78d15da1159811b547ea04e3b36ca9d2
Showing with 48 additions and 0 deletions.
  1. +12 −0 Libraries/Components/ScrollView/ScrollView.js
  2. +24 −0 React/Views/RCTScrollView.m
  3. +12 −0 React/Views/RCTScrollViewManager.m
@@ -360,6 +360,18 @@ const ScrollView = createReactClass({
* @platform ios
*/
zoomScale: PropTypes.number,
/**
* This property specifies how the safe area insets are used to modify the
* content area of the scroll view. The default value of this property is
* "never". Available on iOS 11 and later.
* @platform ios
*/
contentInsetAdjustmentBehavior: PropTypes.oneOf([
'automatic',
'scrollableAxes',
'never', // default
'always',
]),
/**
* A RefreshControl component, used to provide pull-to-refresh
@@ -354,10 +354,22 @@ - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
if ((self = [super initWithFrame:CGRectZero])) {
_eventDispatcher = eventDispatcher;
_scrollView = [[RCTCustomScrollView alloc] initWithFrame:CGRectZero];
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_scrollView.delegate = self;
_scrollView.delaysContentTouches = NO;
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
// `contentInsetAdjustmentBehavior` is only available since iOS 11.
// We set the default behavior to "never" so that iOS
// doesn't do weird things to UIScrollView insets automatically
// and keeps it as an opt-in behavior.
if ([_scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
_scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
#endif
_automaticallyAdjustContentInsets = YES;
_DEPRECATED_sendUpdatedChildFrames = NO;
_contentInset = UIEdgeInsetsZero;
@@ -901,6 +913,18 @@ - (type)getter \
RCT_SET_AND_PRESERVE_OFFSET(setZoomScale, zoomScale, CGFloat);
RCT_SET_AND_PRESERVE_OFFSET(setScrollIndicatorInsets, scrollIndicatorInsets, UIEdgeInsets);
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
- (void)setContentInsetAdjustmentBehavior:(UIScrollViewContentInsetAdjustmentBehavior)behavior
{
// `contentInsetAdjustmentBehavior` is available since iOS 11.
if ([_scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
CGPoint contentOffset = _scrollView.contentOffset;
_scrollView.contentInsetAdjustmentBehavior = behavior;
_scrollView.contentOffset = contentOffset;
}
}
#endif
- (void)sendScrollEventWithName:(NSString *)eventName
scrollView:(UIScrollView *)scrollView
userData:(NSDictionary *)userData
@@ -36,6 +36,15 @@ @implementation RCTConvert (UIScrollView)
@"white": @(UIScrollViewIndicatorStyleWhite),
}), UIScrollViewIndicatorStyleDefault, integerValue)
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
RCT_ENUM_CONVERTER(UIScrollViewContentInsetAdjustmentBehavior, (@{
@"automatic": @(UIScrollViewContentInsetAdjustmentAutomatic),
@"scrollableAxes": @(UIScrollViewContentInsetAdjustmentScrollableAxes),
@"never": @(UIScrollViewContentInsetAdjustmentNever),
@"always": @(UIScrollViewContentInsetAdjustmentAlways),
}), UIScrollViewContentInsetAdjustmentNever, integerValue)
#endif
@end
@implementation RCTScrollViewManager
@@ -80,6 +89,9 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollBegin, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(DEPRECATED_sendUpdatedChildFrames, BOOL)
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
#endif
// overflow is used both in css-layout as well as by react-native. In css-layout
// we always want to treat overflow as scroll but depending on what the overflow

0 comments on commit 6e28b39

Please sign in to comment.