Skip to content

Commit

Permalink
Make SafeAreaView to work on iOS < 11 (#18534)
Browse files Browse the repository at this point in the history
Summary:
Currently `SafeAreaView` works only on iOS 11, because implemented in terms of native safeArea API, that not exists in older iOS versions. But this make it hard to use the component in real applications, because content will be under top bars on older versions of iOS and no reliable way to workaround this in js. More motivation in #17638

This changeset emulate safe area in terms of `UIViewController` layout guides API if safeArea not available.

Fixes #17638, #18255

I run RNTester with these simulators: iPhone6 (9.3), iPhone6 (10.0), iPhone6 (11.2), iPhoneX (11.2)
- Start RNTester application
- Look on top header, it should not overlap status bar
- Go to the `<SafeAreaView>` example, open modal
- Modal area should not overlap status bar

<img src="http://vovkasm.skitch.vovkasm.org/iPhone6_10_20662C5B.png" width="40%"> <img src="http://vovkasm.skitch.vovkasm.org/iPhone6_11_20662CC8.png" width="40%">

<img src="http://vovkasm.skitch.vovkasm.org/iPhone6_10_pr_20662DE6.png" width="40%"> <img src="http://vovkasm.skitch.vovkasm.org/iPhone6_11_pr_20662DA8.png" width="40%">

[IOS] [BUGFIX] [SafeAreaView] - Make SafeAreaView to work on iOS < 11
Pull Request resolved: #18534

Reviewed By: PeteTheHeat, shergin

Differential Revision: D9166052

Pulled By: hramos

fbshipit-source-id: c086e1ae4af13110a7453b770ca75b6e0d5321ea
  • Loading branch information
vovkasm authored and facebook-github-bot committed Aug 23, 2018
1 parent 9747289 commit 3949e93
Showing 1 changed file with 37 additions and 7 deletions.
44 changes: 37 additions & 7 deletions React/Views/SafeAreaView/RCTSafeAreaView.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)decoder)
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */

static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIEdgeInsets insets2, CGFloat threshold) {
return
ABS(insets1.left - insets2.left) <= threshold &&
Expand All @@ -39,25 +37,57 @@ static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIE
ABS(insets1.bottom - insets2.bottom) <= threshold;
}

#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */

- (void)safeAreaInsetsDidChange
{
if (![self respondsToSelector:@selector(safeAreaInsets)]) {
return;
}

UIEdgeInsets safeAreaInsets = self.safeAreaInsets;
[self setSafeAreaInsets:self.safeAreaInsets];
}

#endif

// Emulate safe area for iOS < 11
- (void)layoutSubviews
{
[super layoutSubviews];
if ([self respondsToSelector:@selector(safeAreaInsets)]) {
return;
}
UIViewController* vc = self.reactViewController;
if (!vc) {
return;
}
CGFloat topLayoutOffset = vc.topLayoutGuide.length;
CGFloat bottomLayoutOffset = vc.bottomLayoutGuide.length;
CGRect safeArea = vc.view.bounds;
safeArea.origin.y += topLayoutOffset;
safeArea.size.height -= topLayoutOffset + bottomLayoutOffset;
CGRect localSafeArea = [vc.view convertRect:safeArea toView:self];
UIEdgeInsets safeAreaInsets = UIEdgeInsetsMake(0, 0, 0, 0);
if (CGRectGetMinY(localSafeArea) > CGRectGetMinY(self.bounds)) {
safeAreaInsets.top = CGRectGetMinY(localSafeArea) - CGRectGetMinY(self.bounds);
}
if (CGRectGetMaxY(localSafeArea) < CGRectGetMaxY(self.bounds)) {
safeAreaInsets.bottom = CGRectGetMaxY(self.bounds) - CGRectGetMaxY(localSafeArea);
}

[self setSafeAreaInsets:safeAreaInsets];
}

- (void)setSafeAreaInsets:(UIEdgeInsets)safeAreaInsets
{
if (UIEdgeInsetsEqualToEdgeInsetsWithThreshold(safeAreaInsets, _currentSafeAreaInsets, 1.0 / RCTScreenScale())) {
return;
}

_currentSafeAreaInsets = safeAreaInsets;

RCTSafeAreaViewLocalData *localData =
[[RCTSafeAreaViewLocalData alloc] initWithInsets:safeAreaInsets];
RCTSafeAreaViewLocalData *localData = [[RCTSafeAreaViewLocalData alloc] initWithInsets:safeAreaInsets];
[_bridge.uiManager setLocalData:localData forView:self];
}

#endif

@end

0 comments on commit 3949e93

Please sign in to comment.