Skip to content

Commit cb454c6

Browse files
committed
fixed bugs and added safearea
1 parent d113aaa commit cb454c6

26 files changed

+698
-33
lines changed

ios/RNCommon.xcodeproj/project.pbxproj

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
/* Begin PBXBuildFile section */
1010
F558C8EC2038448900E2DAED /* HighlighterView.m in Sources */ = {isa = PBXBuildFile; fileRef = F558C8EB2038448900E2DAED /* HighlighterView.m */; };
1111
F558C8EE2038449C00E2DAED /* HighlighterViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F558C8ED2038449C00E2DAED /* HighlighterViewManager.m */; };
12+
F5D3465F205FA55C00AB1B2A /* SafeAreaManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F5D3465E205FA55C00AB1B2A /* SafeAreaManager.m */; };
13+
F5D34661205FA56D00AB1B2A /* SafeAreaSpacerShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = F5D34660205FA56D00AB1B2A /* SafeAreaSpacerShadowView.m */; };
14+
F5D34665205FA59500AB1B2A /* SafeAreaSpacerView.m in Sources */ = {isa = PBXBuildFile; fileRef = F5D34664205FA59500AB1B2A /* SafeAreaSpacerView.m */; };
15+
F5D34667205FA5AA00AB1B2A /* SafeAreaSpacerViewLocalData.m in Sources */ = {isa = PBXBuildFile; fileRef = F5D34666205FA5AA00AB1B2A /* SafeAreaSpacerViewLocalData.m */; };
16+
F5D3466B205FA5DA00AB1B2A /* SafeAreaSpacerViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F5D3466A205FA5DA00AB1B2A /* SafeAreaSpacerViewManager.m */; };
1217
/* End PBXBuildFile section */
1318

1419
/* Begin PBXCopyFilesBuildPhase section */
@@ -29,6 +34,16 @@
2934
F558C8EB2038448900E2DAED /* HighlighterView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HighlighterView.m; sourceTree = "<group>"; };
3035
F558C8ED2038449C00E2DAED /* HighlighterViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HighlighterViewManager.m; sourceTree = "<group>"; };
3136
F558C8EF203844A600E2DAED /* HighlighterViewManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HighlighterViewManager.h; sourceTree = "<group>"; };
37+
F5D3465D205FA54400AB1B2A /* SafeAreaManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SafeAreaManager.h; sourceTree = "<group>"; };
38+
F5D3465E205FA55C00AB1B2A /* SafeAreaManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SafeAreaManager.m; sourceTree = "<group>"; };
39+
F5D34660205FA56D00AB1B2A /* SafeAreaSpacerShadowView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SafeAreaSpacerShadowView.m; sourceTree = "<group>"; };
40+
F5D34662205FA57700AB1B2A /* SafeAreaSpacerShadowView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SafeAreaSpacerShadowView.h; sourceTree = "<group>"; };
41+
F5D34663205FA58600AB1B2A /* SafeAreaSpacerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SafeAreaSpacerView.h; sourceTree = "<group>"; };
42+
F5D34664205FA59500AB1B2A /* SafeAreaSpacerView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SafeAreaSpacerView.m; sourceTree = "<group>"; };
43+
F5D34666205FA5AA00AB1B2A /* SafeAreaSpacerViewLocalData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SafeAreaSpacerViewLocalData.m; sourceTree = "<group>"; };
44+
F5D34668205FA5B900AB1B2A /* SafeAreaSpacerViewLocalData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SafeAreaSpacerViewLocalData.h; sourceTree = "<group>"; };
45+
F5D34669205FA5CF00AB1B2A /* SafeAreaSpacerViewManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SafeAreaSpacerViewManager.h; sourceTree = "<group>"; };
46+
F5D3466A205FA5DA00AB1B2A /* SafeAreaSpacerViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SafeAreaSpacerViewManager.m; sourceTree = "<group>"; };
3247
/* End PBXFileReference section */
3348

3449
/* Begin PBXFrameworksBuildPhase section */
@@ -61,6 +76,7 @@
6176
F558C8E82038443900E2DAED /* reactnativecommon */ = {
6277
isa = PBXGroup;
6378
children = (
79+
F5D3465C205FA51E00AB1B2A /* safearea */,
6480
F558C8E92038444400E2DAED /* highlighterview */,
6581
);
6682
path = reactnativecommon;
@@ -77,6 +93,23 @@
7793
path = highlighterview;
7894
sourceTree = "<group>";
7995
};
96+
F5D3465C205FA51E00AB1B2A /* safearea */ = {
97+
isa = PBXGroup;
98+
children = (
99+
F5D3465D205FA54400AB1B2A /* SafeAreaManager.h */,
100+
F5D3465E205FA55C00AB1B2A /* SafeAreaManager.m */,
101+
F5D34662205FA57700AB1B2A /* SafeAreaSpacerShadowView.h */,
102+
F5D34660205FA56D00AB1B2A /* SafeAreaSpacerShadowView.m */,
103+
F5D34663205FA58600AB1B2A /* SafeAreaSpacerView.h */,
104+
F5D34664205FA59500AB1B2A /* SafeAreaSpacerView.m */,
105+
F5D34668205FA5B900AB1B2A /* SafeAreaSpacerViewLocalData.h */,
106+
F5D34666205FA5AA00AB1B2A /* SafeAreaSpacerViewLocalData.m */,
107+
F5D34669205FA5CF00AB1B2A /* SafeAreaSpacerViewManager.h */,
108+
F5D3466A205FA5DA00AB1B2A /* SafeAreaSpacerViewManager.m */,
109+
);
110+
path = safearea;
111+
sourceTree = "<group>";
112+
};
80113
/* End PBXGroup section */
81114

82115
/* Begin PBXNativeTarget section */
@@ -133,7 +166,12 @@
133166
isa = PBXSourcesBuildPhase;
134167
buildActionMask = 2147483647;
135168
files = (
169+
F5D34661205FA56D00AB1B2A /* SafeAreaSpacerShadowView.m in Sources */,
170+
F5D34665205FA59500AB1B2A /* SafeAreaSpacerView.m in Sources */,
136171
F558C8EC2038448900E2DAED /* HighlighterView.m in Sources */,
172+
F5D3465F205FA55C00AB1B2A /* SafeAreaManager.m in Sources */,
173+
F5D3466B205FA5DA00AB1B2A /* SafeAreaSpacerViewManager.m in Sources */,
174+
F5D34667205FA5AA00AB1B2A /* SafeAreaSpacerViewLocalData.m in Sources */,
137175
F558C8EE2038449C00E2DAED /* HighlighterViewManager.m in Sources */,
138176
);
139177
runOnlyForDeploymentPostprocessing = 0;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// SafeAreaManager.h
3+
// RNCommon
4+
//
5+
// Created by Reza on 3/19/18.
6+
// Copyright © 2018 kajoo. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
#import <React/RCTEventEmitter.h>
11+
12+
#ifndef SafeAreaManager_h
13+
#define SafeAreaManager_h
14+
15+
@interface SafeAreaManager : RCTEventEmitter
16+
17+
@end
18+
19+
#endif /* SafeAreaManager_h */
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//
2+
// SafeAreaManager.m
3+
// RNCommon
4+
//
5+
// Created by Reza on 3/19/18.
6+
// Copyright © 2018 kajoo. All rights reserved.
7+
//
8+
9+
#import "SafeAreaManager.h"
10+
#import <UIKit/UIKit.h>
11+
#import <objc/runtime.h>
12+
13+
static NSString *const UIWindowSafeAreaInsetsDidChangeNotification = @"SafeAreaManager.UIWindowSafeAreaInsetsDidChangeNotification";
14+
static NSString *const SafeAreaInsetsDidChangeEvent = @"SafeAreaInsetsDidChangeEvent";
15+
static id (*_swz_safeAreaInsetsDidChange_orig)(id self, SEL _cmd);
16+
17+
@interface SafeAreaManager () {
18+
UIEdgeInsets _cachedSafeAreaInsets;
19+
}
20+
@end
21+
22+
@implementation SafeAreaManager
23+
24+
RCT_EXPORT_MODULE()
25+
26+
- (dispatch_queue_t)methodQueue {
27+
return dispatch_get_main_queue();
28+
}
29+
30+
- (NSArray<NSString *> *)supportedEvents {
31+
return @[SafeAreaInsetsDidChangeEvent];
32+
}
33+
34+
- (instancetype)init {
35+
self = [super init];
36+
if (self) {
37+
_cachedSafeAreaInsets = [self getCurrentSafeAreaInsets];
38+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowSafeAreaDidChange) name:UIWindowSafeAreaInsetsDidChangeNotification object:nil];
39+
[self applySizzles];
40+
}
41+
return self;
42+
}
43+
44+
-(void)dealloc {
45+
[[NSNotificationCenter defaultCenter] removeObserver:self];
46+
}
47+
48+
- (void)windowSafeAreaDidChange {
49+
UIEdgeInsets currentSafeAreaInsets = [self getCurrentSafeAreaInsets];
50+
if (!UIEdgeInsetsEqualToEdgeInsets(currentSafeAreaInsets, _cachedSafeAreaInsets)) {
51+
_cachedSafeAreaInsets = currentSafeAreaInsets;
52+
53+
NSUInteger listenerCount = [[self valueForKey:@"_listenerCount"] unsignedIntegerValue];
54+
if (listenerCount > 0) {
55+
[self sendEventWithName:SafeAreaInsetsDidChangeEvent body:[self getResultDicFromSafeArea:_cachedSafeAreaInsets]];
56+
}
57+
}
58+
}
59+
60+
-(UIEdgeInsets)getCurrentSafeAreaInsets {
61+
UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
62+
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3
63+
if (@available(iOS 11.0, *)) {
64+
safeAreaInsets = [UIApplication sharedApplication].keyWindow.safeAreaInsets;
65+
}
66+
#endif
67+
return safeAreaInsets;
68+
}
69+
70+
#pragma mark - swizzles
71+
72+
- (void)applySizzles {
73+
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_10_3
74+
if (@available(iOS 11.0, *)) {
75+
static dispatch_once_t onceToken;
76+
dispatch_once(&onceToken, ^{
77+
Method originalMethod = class_getInstanceMethod([UIWindow class], @selector(safeAreaInsetsDidChange));
78+
Method replacementMethod = class_getInstanceMethod([SafeAreaManager class], @selector(_swz_safeAreaInsetsDidChange));
79+
_swz_safeAreaInsetsDidChange_orig = (void*)method_getImplementation(originalMethod);
80+
method_exchangeImplementations(originalMethod, replacementMethod);
81+
});
82+
}
83+
#endif
84+
}
85+
86+
- (void)_swz_safeAreaInsetsDidChange {
87+
_swz_safeAreaInsetsDidChange_orig(self, _cmd);
88+
[[NSNotificationCenter defaultCenter] postNotificationName:UIWindowSafeAreaInsetsDidChangeNotification object:nil];
89+
}
90+
91+
#pragma mark - helper methods
92+
93+
- (NSDictionary*)getResultDicFromSafeArea:(UIEdgeInsets)safeAreaInsets {
94+
return @{@"top": @(safeAreaInsets.top),
95+
@"left": @(safeAreaInsets.left),
96+
@"bottom": @(safeAreaInsets.bottom),
97+
@"right": @(safeAreaInsets.right)
98+
};
99+
}
100+
101+
#pragma mark - public API
102+
103+
RCT_EXPORT_METHOD(getSafeAreaInsets:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
104+
UIEdgeInsets safeAreaInsets = [self getCurrentSafeAreaInsets];
105+
resolve([self getResultDicFromSafeArea:safeAreaInsets]);
106+
}
107+
108+
@end
109+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// SafeAreaSpacerShadowView.h
3+
// RNCommon
4+
//
5+
// Created by Reza on 3/19/18.
6+
// Copyright © 2018 kajoo. All rights reserved.
7+
//
8+
9+
#import <React/RCTShadowView.h>
10+
11+
#ifndef SafeAreaSpacerShadowView_h
12+
#define SafeAreaSpacerShadowView_h
13+
14+
@interface SafeAreaSpacerShadowView : RCTShadowView
15+
16+
@end
17+
18+
#endif /* SafeAreaSpacerShadowView_h */
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// SafeAreaSpacerShadowView.m
3+
// RNCommon
4+
//
5+
// Created by Reza on 3/19/18.
6+
// Copyright © 2018 kajoo. All rights reserved.
7+
//
8+
9+
#import "SafeAreaSpacerShadowView.h"
10+
#import "SafeAreaSpacerViewLocalData.h"
11+
12+
@implementation SafeAreaSpacerShadowView
13+
14+
- (void)setLocalData:(SafeAreaSpacerViewLocalData *)localData
15+
{
16+
UIEdgeInsets insets = localData.insets;
17+
18+
super.height = (YGValue){insets.bottom + insets.top, YGUnitPoint};
19+
[self didSetProps:@[@"height"]];
20+
}
21+
22+
/**
23+
* Removing support for setting height from any outside code
24+
* to prevent interferring this with local data.
25+
*/
26+
- (void)setHeight:(YGValue)height {}
27+
28+
@end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// SafeAreaSpacerView.h
3+
// RNCommon
4+
//
5+
// Created by Reza on 3/19/18.
6+
// Copyright © 2018 kajoo. All rights reserved.
7+
//
8+
9+
#import <React/RCTView.h>
10+
11+
#ifndef SafeAreaSpacerView_h
12+
#define SafeAreaSpacerView_h
13+
14+
@class RCTBridge;
15+
16+
@interface SafeAreaSpacerView : RCTView
17+
- (instancetype)initWithBridge:(RCTBridge *)bridge;
18+
@end
19+
20+
#endif /* SafeAreaSpacerView_h */
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//
2+
// SafeAreaSpacerView.m
3+
// RNCommon
4+
//
5+
// Created by Reza on 3/19/18.
6+
// Copyright © 2018 kajoo. All rights reserved.
7+
//
8+
9+
#import "SafeAreaSpacerView.h"
10+
#import "SafeAreaSpacerViewLocalData.h"
11+
#import <React/RCTBridge.h>
12+
#import <React/RCTUIManager.h>
13+
14+
@implementation SafeAreaSpacerView
15+
{
16+
__weak RCTBridge *_bridge;
17+
UIEdgeInsets _currentSafeAreaInsets;
18+
//these flags are used to prevent an endless cycle of safe area insets update. setting the view height changes it's frame, and in some cases `reactSetFrame` causes more and more updates with varying sizes
19+
BOOL _ignoreNextInsetUpdate;
20+
BOOL _insetsWereUpdated;
21+
}
22+
23+
- (instancetype)initWithBridge:(RCTBridge *)bridge
24+
{
25+
if (self = [super initWithFrame:CGRectZero])
26+
{
27+
_bridge = bridge;
28+
_ignoreNextInsetUpdate = NO;
29+
_insetsWereUpdated = NO;
30+
}
31+
return self;
32+
}
33+
34+
- (void)reactSetFrame:(CGRect)frame
35+
{
36+
if (self.window != nil && _insetsWereUpdated)
37+
{
38+
_ignoreNextInsetUpdate = YES;
39+
dispatch_async(dispatch_get_main_queue(), ^{
40+
_ignoreNextInsetUpdate = NO;
41+
});
42+
}
43+
44+
[super reactSetFrame:frame];
45+
}
46+
47+
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
48+
49+
static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIEdgeInsets insets2, CGFloat threshold)
50+
{
51+
return
52+
ABS(insets1.left - insets2.left) <= threshold &&
53+
ABS(insets1.right - insets2.right) <= threshold &&
54+
ABS(insets1.top - insets2.top) <= threshold &&
55+
ABS(insets1.bottom - insets2.bottom) <= threshold;
56+
}
57+
58+
- (void)safeAreaInsetsDidChange
59+
{
60+
if (![self respondsToSelector:@selector(safeAreaInsets)] || _ignoreNextInsetUpdate)
61+
{
62+
_ignoreNextInsetUpdate = NO;
63+
_insetsWereUpdated = NO;
64+
return;
65+
}
66+
67+
UIEdgeInsets safeAreaInsets = self.safeAreaInsets;
68+
69+
if (UIEdgeInsetsEqualToEdgeInsetsWithThreshold(safeAreaInsets, _currentSafeAreaInsets, 1.0 / RCTScreenScale()))
70+
{
71+
return;
72+
}
73+
74+
_currentSafeAreaInsets = safeAreaInsets;
75+
_insetsWereUpdated = YES;
76+
77+
SafeAreaSpacerViewLocalData *localData = [[SafeAreaSpacerViewLocalData alloc] initWithInsets:safeAreaInsets];
78+
[_bridge.uiManager setLocalData:localData forView:self];
79+
}
80+
81+
#endif
82+
83+
@end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// SafeAreaSpacerViewLocalData.h
3+
// RNCommon
4+
//
5+
// Created by Reza on 3/19/18.
6+
// Copyright © 2018 kajoo. All rights reserved.
7+
//
8+
9+
#import <UIKit/UIKit.h>
10+
11+
#ifndef SafeAreaSpacerViewLocalData_h
12+
#define SafeAreaSpacerViewLocalData_h
13+
14+
@interface SafeAreaSpacerViewLocalData : NSObject
15+
16+
- (instancetype)initWithInsets:(UIEdgeInsets)insets;
17+
18+
@property (atomic, readonly) UIEdgeInsets insets;
19+
20+
@end
21+
22+
#endif /* SafeAreaSpacerViewLocalData_h */

0 commit comments

Comments
 (0)