刚换工作,接手新公司的一个RN项目,同事向我反映了一个问题,说我们的App的首页,滚动后顶部就出现了一个白条,仅在iOS端出现,Android上运行良好。我一听就猜想是ScrollView兼容出问题了,尝试用iOS 9和iOS 11手机分别运行了一下,在iOS 11的手机上重现了这个问题。于是检查代码,发现是iOS 11上UIScrollView增加了contentInsetAdjustmentBehavior属性。
/* Configure the behavior of adjustedContentInset.
Default is UIScrollViewContentInsetAdjustmentAutomatic.
*/
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior API_AVAILABLE(ios(11.0),tvos(11.0));
typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
UIScrollViewContentInsetAdjustmentAutomatic, // Similar to .scrollableAxes, but for backward compatibility will also adjust the top & bottom contentInset when the scroll view is owned by a view controller with automaticallyAdjustsScrollViewInsets = YES inside a navigation controller, regardless of whether the scroll view is scrollable
UIScrollViewContentInsetAdjustmentScrollableAxes, // Edges for scrollable axes are adjusted (i.e., contentSize.width/height > frame.size.width/height or alwaysBounceHorizontal/Vertical = YES)
UIScrollViewContentInsetAdjustmentNever, // contentInset is not adjusted
UIScrollViewContentInsetAdjustmentAlways, // contentInset is always adjusted by the scroll view's safeAreaInsets
} API_AVAILABLE(ios(11.0),tvos(11.0));
contentInsetAdjustmentBehavior
默认为automatic
时,它会根据屏幕上是否有UINavigationBar,UITabBar,可见状态栏等自动调整ContentInset(并覆盖任何手动设置的ContentInset)
刚好我们的主页上有UITabBar,于是就被自动调整了ContentInset,就出现了顶部的白条。
怎么解决
在新版本的React Native中已经修复了此问题,在ScrollView上新增了contentInsetAdjustmentBehavior
属性,且默认为never
,所以使用新版本React Native的童鞋一般是不会遇到这个问题的。但是我司App的React Native版本是0.38,因为历史原因也不能直接去升级React Native版本,所以就自己动手修改源码吧。
以下是修改位置和源码:(参考了React Native新版本的代码)
文件RCTScrollView.m
initWithEventDispatcher方法中增加:
#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
以下代码可在@implementation RCTScrollView任意位置加入,我是加在sendScrollEventWithName方法前的。
#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
文件RCTScrollViewManager.m
可在@implementation RCTScrollViewManager中任意位置加入以下代码,不过为了和其他代码统一,我们在RCT_EXPORT_VIEW_PROPERTY那堆代码后进行增加。
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
#endif
@implementation RCTConvert (UIScrollView)中增加
#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
文件ScrollView.js
在ScrollView的propTypes中增加contentInsetAdjustmentBehavior属性。
contentInsetAdjustmentBehavior: PropTypes.oneOf([
'automatic',
'scrollableAxes',
'never', // default
'always',
]),
修改完毕,运行后白条消失,不过这只是临时解决方案,接下来还是要想办法把React Native的版本升级一下,以免遇到更多的坑。