diff --git a/ios/RNCOnInsetsChangeEvent.h b/ios/RNCOnInsetsChangeEvent.h new file mode 100644 index 00000000..09184291 --- /dev/null +++ b/ios/RNCOnInsetsChangeEvent.h @@ -0,0 +1,12 @@ +#import +#import + +@interface RNCOnInsetsChangeEvent : NSObject + +- (instancetype)initWithEventName:(NSString *)eventName + reactTag:(NSNumber *)reactTag + insets:(UIEdgeInsets)insets + frame:(CGRect)frame + coalescingKey:(uint16_t)coalescingKey NS_DESIGNATED_INITIALIZER; + +@end diff --git a/ios/RNCOnInsetsChangeEvent.m b/ios/RNCOnInsetsChangeEvent.m new file mode 100644 index 00000000..1f973c72 --- /dev/null +++ b/ios/RNCOnInsetsChangeEvent.m @@ -0,0 +1,79 @@ +#import "RNCOnInsetsChangeEvent.h" +#import + +@implementation RNCOnInsetsChangeEvent { + UIEdgeInsets _insets; + CGRect _frame; + uint16_t _coalescingKey; +} + +@synthesize eventName = _eventName; +@synthesize viewTag = _viewTag; + +- (instancetype)initWithEventName:(NSString *)eventName + reactTag:(NSNumber *)reactTag + insets:(UIEdgeInsets)insets + frame:(CGRect)frame + coalescingKey:(uint16_t)coalescingKey +{ + RCTAssertParam(reactTag); + + if ((self = [super init])) { + _eventName = [eventName copy]; + _viewTag = reactTag; + _frame = frame; + _insets = insets; + _coalescingKey = coalescingKey; + } + + return self; +} + +RCT_NOT_IMPLEMENTED(-(instancetype)init) + +- (uint16_t)coalescingKey +{ + return _coalescingKey; +} + +- (NSDictionary *)body +{ + NSDictionary *body = @{ + @"insets" : @{ + @"top" : @(_insets.top), + @"right" : @(_insets.right), + @"bottom" : @(_insets.bottom), + @"left" : @(_insets.left), + }, + @"frame" : @{ + @"x" : @(_frame.origin.x), + @"y" : @(_frame.origin.y), + @"width" : @(_frame.size.width), + @"height" : @(_frame.size.height), + }, + }; + + return body; +} + +- (BOOL)canCoalesce +{ + return YES; +} + +- (RNCOnInsetsChangeEvent *)coalesceWithEvent:(RNCOnInsetsChangeEvent *)newEvent +{ + return newEvent; +} + ++ (NSString *)moduleDotMethod +{ + return @"RCTEventEmitter.receiveEvent"; +} + +- (NSArray *)arguments +{ + return @[ self.viewTag, RCTNormalizeInputEventName(self.eventName), [self body] ]; +} + +@end diff --git a/ios/RNCSafeAreaProvider.h b/ios/RNCSafeAreaProvider.h index 317efbeb..5f05c593 100644 --- a/ios/RNCSafeAreaProvider.h +++ b/ios/RNCSafeAreaProvider.h @@ -4,12 +4,18 @@ #import #endif +#import #import NS_ASSUME_NONNULL_BEGIN @interface RNCSafeAreaProvider : RCTView +- (instancetype)initWithEventDispatcher:(id)eventDispatcher NS_DESIGNATED_INITIALIZER; + +// NOTE: currently these event props are only declared so we can export the +// event names to JS - we don't call the blocks directly because events +// need to be coalesced before sending, for performance reasons. @property (nonatomic, copy) RCTBubblingEventBlock onInsetsChange; @end diff --git a/ios/RNCSafeAreaProvider.m b/ios/RNCSafeAreaProvider.m index 0e82f904..b5b7bef3 100644 --- a/ios/RNCSafeAreaProvider.m +++ b/ios/RNCSafeAreaProvider.m @@ -1,19 +1,32 @@ #import "RNCSafeAreaProvider.h" #import +#import #import +#import "RCTUIManagerObserverCoordinator.h" +#import "RNCOnInsetsChangeEvent.h" #import "RNCSafeAreaUtils.h" +@interface RNCSafeAreaProvider () + +@end + @implementation RNCSafeAreaProvider { + id _eventDispatcher; UIEdgeInsets _currentSafeAreaInsets; CGRect _currentFrame; BOOL _initialInsetsSent; } -- (instancetype)init +- (instancetype)initWithEventDispatcher:(id)eventDispatcher { - if ((self = [super init])) { + RCTAssertParam(eventDispatcher); + + if ((self = [super initWithFrame:CGRectZero])) { #if !TARGET_OS_TV && !TARGET_OS_OSX + + _eventDispatcher = eventDispatcher; + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(invalidateSafeAreaInsets) name:UIKeyboardDidShowNotification @@ -58,6 +71,7 @@ - (void)invalidateSafeAreaInsets safeAreaInsets = NSEdgeInsetsZero; } #endif + CGRect frame = [self convertRect:self.bounds toView:RNCParentViewController(self).view]; if (_initialInsetsSent && @@ -69,27 +83,19 @@ - (void)invalidateSafeAreaInsets CGRectEqualToRect(frame, _currentFrame)) { return; } - _initialInsetsSent = YES; _currentSafeAreaInsets = safeAreaInsets; _currentFrame = frame; [NSNotificationCenter.defaultCenter postNotificationName:RNCSafeAreaDidChange object:self userInfo:nil]; - self.onInsetsChange(@{ - @"insets" : @{ - @"top" : @(safeAreaInsets.top), - @"right" : @(safeAreaInsets.right), - @"bottom" : @(safeAreaInsets.bottom), - @"left" : @(safeAreaInsets.left), - }, - @"frame" : @{ - @"x" : @(frame.origin.x), - @"y" : @(frame.origin.y), - @"width" : @(frame.size.width), - @"height" : @(frame.size.height), - }, - }); + RNCOnInsetsChangeEvent *onInsetsChangeEvent = [[RNCOnInsetsChangeEvent alloc] initWithEventName:@"onInsetsChange" + reactTag:self.reactTag + insets:safeAreaInsets + frame:frame + coalescingKey:0]; + + [_eventDispatcher sendEvent:onInsetsChangeEvent]; } - (void)layoutSubviews @@ -99,4 +105,12 @@ - (void)layoutSubviews [self invalidateSafeAreaInsets]; } +RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) +RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder) + +- (void)dealloc +{ + [_eventDispatcher.bridge.uiManager.observerCoordinator removeObserver:self]; +} + @end diff --git a/ios/RNCSafeAreaProviderManager.m b/ios/RNCSafeAreaProviderManager.m index 569ecb49..eeeb1b8a 100644 --- a/ios/RNCSafeAreaProviderManager.m +++ b/ios/RNCSafeAreaProviderManager.m @@ -14,7 +14,7 @@ - (UIView *)view - (NSView *)view #endif { - return [RNCSafeAreaProvider new]; + return [[RNCSafeAreaProvider alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; } @end diff --git a/ios/RNSafeAreaContext.xcodeproj/project.pbxproj b/ios/RNSafeAreaContext.xcodeproj/project.pbxproj index 655adc17..e002de5b 100644 --- a/ios/RNSafeAreaContext.xcodeproj/project.pbxproj +++ b/ios/RNSafeAreaContext.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 0C7844F027C02D03001807FB /* RNCSafeAreaProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C7844ED27C02D03001807FB /* RNCSafeAreaProvider.m */; }; AA53A9EE2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.m in Sources */ = {isa = PBXBuildFile; fileRef = AA53A9ED2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.m */; }; C923EDBC220C2C1A00D3100F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C923EDBB220C2C1A00D3100F /* SystemConfiguration.framework */; }; + D697AA982D6F1D0A009C6433 /* RNCChangeEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = D697AA972D6F1D08009C6433 /* RNCChangeEvent.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -59,6 +60,8 @@ AA53A9EC2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCSafeAreaViewEdgeModes.h; sourceTree = ""; }; AA53A9ED2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCSafeAreaViewEdgeModes.m; sourceTree = ""; }; C923EDBB220C2C1A00D3100F /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + D697AA962D6F1CE5009C6433 /* RNCChangeEvent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCChangeEvent.h; sourceTree = ""; }; + D697AA972D6F1D08009C6433 /* RNCChangeEvent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCChangeEvent.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -84,6 +87,8 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( + D697AA972D6F1D08009C6433 /* RNCChangeEvent.m */, + D697AA962D6F1CE5009C6433 /* RNCChangeEvent.h */, AA53A9EC2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.h */, AA53A9ED2A321C01009AB3B2 /* RNCSafeAreaViewEdgeModes.m */, 0C7844EE27C02D03001807FB /* Fabric */, @@ -183,6 +188,7 @@ 0C7844E827C02CEE001807FB /* RNCSafeAreaView.m in Sources */, 0C7844E627C02CEE001807FB /* RNCSafeAreaViewEdges.m in Sources */, 0C7844E527C02CEE001807FB /* RNCSafeAreaViewManager.m in Sources */, + D697AA982D6F1D0A009C6433 /* RNCChangeEvent.m in Sources */, 0C7844EF27C02D03001807FB /* RNCSafeAreaContext.mm in Sources */, 0C7844E127C02CEE001807FB /* RNCSafeAreaViewLocalData.m in Sources */, 0C7844E227C02CEE001807FB /* RNCSafeAreaViewMode.m in Sources */,