forked from brentvatne/react-native-overlay
-
Notifications
You must be signed in to change notification settings - Fork 0
/
RNOverlay.m
114 lines (98 loc) · 3.89 KB
/
RNOverlay.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#import "RNOverlay.h"
#import "RCTUIManager.h"
#import "RCTView.h"
#import "RCTTouchHandler.h"
#import "UIView+React.h"
#import "RNClickThroughWindow.h"
@implementation RNOverlay {
RNClickThroughWindow *_overlayWindow;
UIViewController *_overlayViewController;
RCTView *_overlayBaseView;
RCTTouchHandler *_touchHandler;
BOOL _aboveStatusBar;
}
// Taken from react-native/React/Modules/RCTUIManager.m
// Since our view is not registered as a root view we have to manually
// iterate through the overlay's subviews and forward the `reactBridgeDidFinishTransaction` message
// If the function below would be a utility function we could just import, it would make
// things less dirty - maybe ask the react-native guys nicely?
typedef void (^react_view_node_block_t)(id<RCTComponent>);
static void RCTTraverseViewNodes(id<RCTComponent> view, react_view_node_block_t block)
{
if (view.reactTag) block(view);
for (id<RCTComponent> subview in view.reactSubviews) {
RCTTraverseViewNodes(subview, block);
}
}
- (id)initWithBridge:(RCTBridge *)bridge
{
if ((self = [super init])) {
_overlayViewController = [[UIViewController alloc] init];
_overlayBaseView = [[RCTView alloc] init];
/* Must register handler because we are in a new UIWindow and our
* overlayBaseView does not have a RCTRootView parent */
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge];
[_overlayBaseView addGestureRecognizer:_touchHandler];
_overlayViewController.view = _overlayBaseView;
}
return self;
}
- (void)setAboveStatusBar:(BOOL)aboveStatusBar {
_aboveStatusBar = aboveStatusBar;
[self applyWindowLevel];
}
- (void)applyWindowLevel {
if (_overlayWindow == nil) {
return;
}
if (_aboveStatusBar) {
_overlayWindow.windowLevel = UIWindowLevelStatusBar;
} else {
_overlayWindow.windowLevel = UIWindowLevelStatusBar - 1;
}
}
/* Every component has it is initializer called twice, once to create a base view
* with default props and another to actually create it and apply the props. We make
* this prop that is always true in order to not create UIWindow for the default props
* instance */
- (void)setIsVisible:(BOOL)isVisible {
_overlayWindow = [[RNClickThroughWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[self applyWindowLevel];
_overlayWindow.backgroundColor = [UIColor clearColor];
_overlayWindow.rootViewController = _overlayViewController;
_overlayWindow.userInteractionEnabled = YES;
_overlayWindow.hidden = NO;
/* We need to watch for app reload notifications to properly remove the overlay,
* removeFromSuperview does not properly propagate down without this */
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(removeFromSuperview)
name:@"RCTReloadNotification"
object:nil];
}
- (void)reactBridgeDidFinishTransaction {
// forward the `reactBridgeDidFinishTransaction` message to all our subviews
// in case their native representations do some logic in their handler
RCTTraverseViewNodes(_overlayBaseView, ^(id<RCTComponent> view) {
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
[view reactBridgeDidFinishTransaction];
}
});
}
- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
{
/* Add subviews to the overlay base view rather than self */
[_overlayBaseView insertReactSubview:view atIndex:atIndex];
}
/* We do not need to support unmounting, so I -think- that this cleanup code
* is safe to put here. */
- (void)removeFromSuperview
{
[_overlayBaseView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
_touchHandler = nil;
_overlayViewController = nil;
_overlayBaseView = nil;
_overlayWindow = nil;
[super removeFromSuperview];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end