1#import "RNCSafeAreaView.h"
2
3#import <React/RCTBridge.h>
4#import <React/RCTUIManager.h>
5
6#import "RNCSafeAreaProvider.h"
7#import "RNCSafeAreaUtils.h"
8#import "RNCSafeAreaViewEdges.h"
9#import "RNCSafeAreaViewLocalData.h"
10#import "RNCSafeAreaViewMode.h"
11
12@implementation RNCSafeAreaView {
13  __weak RCTBridge *_bridge;
14  UIEdgeInsets _currentSafeAreaInsets;
15  RNCSafeAreaViewMode _mode;
16  RNCSafeAreaViewEdges _edges;
17  __weak RNCSafeAreaProvider *_Nullable _providerView;
18}
19
20- (instancetype)initWithBridge:(RCTBridge *)bridge
21{
22  if (self = [super initWithFrame:CGRectZero]) {
23    _bridge = bridge;
24    // Defaults
25    _mode = RNCSafeAreaViewModePadding;
26    _edges = RNCSafeAreaViewEdgesMake(
27        RNCSafeAreaViewEdgeModeOff, RNCSafeAreaViewEdgeModeOff, RNCSafeAreaViewEdgeModeOff, RNCSafeAreaViewEdgeModeOff);
28  }
29
30  return self;
31}
32
33RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)decoder)
34RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
35
36- (NSString *)description
37{
38  NSString *superDescription = [super description];
39
40  // Cutting the last `>` character.
41  if (superDescription.length > 0 && [superDescription characterAtIndex:superDescription.length - 1] == '>') {
42    superDescription = [superDescription substringToIndex:superDescription.length - 1];
43  }
44
45  return [NSString stringWithFormat:@"%@; RNCSafeAreaInsets = %@; appliedRNCSafeAreaInsets = %@>",
46                                    superDescription,
47                                    NSStringFromUIEdgeInsets(_providerView.safeAreaInsets),
48                                    NSStringFromUIEdgeInsets(_currentSafeAreaInsets)];
49}
50
51- (void)didMoveToWindow
52{
53  UIView *previousProviderView = _providerView;
54  _providerView = [self findNearestProvider];
55
56  [self invalidateSafeAreaInsets];
57
58  if (previousProviderView != _providerView) {
59    if (previousProviderView != nil) {
60      [NSNotificationCenter.defaultCenter removeObserver:self name:RNCSafeAreaDidChange object:previousProviderView];
61    }
62
63    if (_providerView != nil) {
64      [NSNotificationCenter.defaultCenter addObserver:self
65                                             selector:@selector(safeAreaProviderInsetsDidChange:)
66                                                 name:RNCSafeAreaDidChange
67                                               object:_providerView];
68    }
69  }
70}
71
72- (void)safeAreaProviderInsetsDidChange:(NSNotification *)notification
73{
74  [self invalidateSafeAreaInsets];
75}
76
77- (void)invalidateSafeAreaInsets
78{
79  if (_providerView == nil) {
80    return;
81  }
82  UIEdgeInsets safeAreaInsets = _providerView.safeAreaInsets;
83
84  if (UIEdgeInsetsEqualToEdgeInsetsWithThreshold(safeAreaInsets, _currentSafeAreaInsets, 1.0 / RCTScreenScale())) {
85    return;
86  }
87
88  _currentSafeAreaInsets = safeAreaInsets;
89  [self updateLocalData];
90}
91
92- (nullable RNCSafeAreaProvider *)findNearestProvider
93{
94  UIView *current = self.reactSuperview;
95  while (current != nil) {
96    if ([current isKindOfClass:RNCSafeAreaProvider.class]) {
97      return (RNCSafeAreaProvider *)current;
98    }
99    current = current.reactSuperview;
100  }
101  return nil;
102}
103
104- (void)updateLocalData
105{
106  if (_providerView == nil) {
107    return;
108  }
109  RNCSafeAreaViewLocalData *localData = [[RNCSafeAreaViewLocalData alloc] initWithInsets:_currentSafeAreaInsets
110                                                                                    mode:_mode
111                                                                                   edges:_edges];
112  [_bridge.uiManager setLocalData:localData forView:self];
113}
114
115- (void)setMode:(RNCSafeAreaViewMode)mode
116{
117  _mode = mode;
118  [self updateLocalData];
119}
120
121- (void)setEdges:(RNCSafeAreaViewEdges)edges
122{
123  _edges = edges;
124  [self updateLocalData];
125}
126
127@end
128