剛換工作,接手新公司的一個RN項目,同事向我反映了一個問題,說我們的App的首頁,滾動后頂部就出現(xiàn)了一個白條,僅在iOS端出現(xiàn),Android上運行良好。我一聽就猜想是ScrollView兼容出問題了,嘗試用iOS 9和iOS 11手機分別運行了一下,在iOS 11的手機上重現(xiàn)了這個問題。于是檢查代碼,發(fā)現(xiàn)是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時,它會根據(jù)屏幕上是否有UINavigationBar,UITabBar,可見狀態(tài)欄等自動調整ContentInset(并覆蓋任何手動設置的ContentInset)
剛好我們的主頁上有UITabBar,于是就被自動調整了ContentInset,就出現(xiàn)了頂部的白條。
怎么解決
在新版本的React Native中已經(jīng)修復了此問題,在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中任意位置加入以下代碼,不過為了和其他代碼統(tǒng)一,我們在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的版本升級一下,以免遇到更多的坑。