1 2#import "EXStatusBarManager.h" 3#import "EXUnversioned.h" 4 5#import <React/RCTEventDispatcher.h> 6#import <React/RCTLog.h> 7#import <React/RCTUtils.h> 8 9#if !TARGET_OS_TV 10@implementation RCTConvert (EXStatusBar) 11 12RCT_ENUM_CONVERTER(UIStatusBarStyle, (@{ 13 @"default": @(UIStatusBarStyleDefault), 14 @"light-content": @(UIStatusBarStyleLightContent), 15 @"dark-content": @(UIStatusBarStyleDefault), 16}), UIStatusBarStyleDefault, integerValue); 17 18RCT_ENUM_CONVERTER(UIStatusBarAnimation, (@{ 19 @"none": @(UIStatusBarAnimationNone), 20 @"fade": @(UIStatusBarAnimationFade), 21 @"slide": @(UIStatusBarAnimationSlide), 22}), UIStatusBarAnimationNone, integerValue); 23 24@end 25#endif 26 27@interface EXStatusBarManager () 28 29@property (nonatomic, strong) NSMutableDictionary *capturedStatusBarProperties; 30 31@end 32 33@implementation EXStatusBarManager 34 35+ (NSString *)moduleName { return @"RCTStatusBarManager"; } 36 37- (NSArray<NSString *> *)supportedEvents 38{ 39 return @[@"statusBarFrameDidChange", 40 @"statusBarFrameWillChange"]; 41} 42 43#if !TARGET_OS_TV 44 45- (void)setBridge:(RCTBridge *)bridge 46{ 47 [super setBridge:bridge]; 48 _capturedStatusBarProperties = [[self _currentStatusBarProperties] mutableCopy]; 49 [[NSNotificationCenter defaultCenter] addObserver:self 50 selector:@selector(_bridgeDidForeground:) 51 name:EX_UNVERSIONED(@"EXKernelBridgeDidForegroundNotification") 52 object:self.bridge]; 53 54 [[NSNotificationCenter defaultCenter] addObserver:self 55 selector:@selector(_bridgeDidBackground:) 56 name:EX_UNVERSIONED(@"EXKernelBridgeDidBackgroundNotification") 57 object:self.bridge]; 58} 59 60- (void)dealloc 61{ 62 [self stopObserving]; 63} 64 65- (void)startObserving 66{ 67 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 68 [nc addObserver:self selector:@selector(applicationDidChangeStatusBarFrame:) name:UIApplicationDidChangeStatusBarFrameNotification object:nil]; 69 [nc addObserver:self selector:@selector(applicationWillChangeStatusBarFrame:) name:UIApplicationWillChangeStatusBarFrameNotification object:nil]; 70} 71 72- (void)stopObserving 73{ 74 [[NSNotificationCenter defaultCenter] removeObserver:self]; 75} 76 77- (dispatch_queue_t)methodQueue 78{ 79 return dispatch_get_main_queue(); 80} 81 82- (void)emitEvent:(NSString *)eventName forNotification:(NSNotification *)notification 83{ 84 CGRect frame = [notification.userInfo[UIApplicationStatusBarFrameUserInfoKey] CGRectValue]; 85 NSDictionary *event = @{ 86 @"frame": @{ 87 @"x": @(frame.origin.x), 88 @"y": @(frame.origin.y), 89 @"width": @(frame.size.width), 90 @"height": @(frame.size.height), 91 }, 92 }; 93 [self sendEventWithName:eventName body:event]; 94} 95 96- (void)applicationDidChangeStatusBarFrame:(NSNotification *)notification 97{ 98 [self emitEvent:@"statusBarFrameDidChange" forNotification:notification]; 99} 100 101- (void)applicationWillChangeStatusBarFrame:(NSNotification *)notification 102{ 103 [self emitEvent:@"statusBarFrameWillChange" forNotification:notification]; 104} 105 106RCT_EXPORT_METHOD(getHeight:(RCTResponseSenderBlock)callback) 107{ 108 callback(@[@{ 109 @"height": @([UIApplication sharedApplication].statusBarFrame.size.height), 110 }]); 111} 112 113RCT_EXPORT_METHOD(setStyle:(UIStatusBarStyle)statusBarStyle 114 animated:(BOOL)animated) 115{ 116 if ([[self class] _viewControllerBasedStatusBarAppearance]) { 117 RCTLogError(@"RCTStatusBarManager module requires that the \ 118 UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO"); 119 } else { 120#pragma clang diagnostic push 121#pragma clang diagnostic ignored "-Wdeprecated-declarations" 122 [RCTSharedApplication() setStatusBarStyle:statusBarStyle 123 animated:animated]; 124 _capturedStatusBarProperties[@"style"] = @(statusBarStyle); 125#pragma clang diagnostic pop 126 } 127} 128 129RCT_EXPORT_METHOD(setHidden:(BOOL)hidden 130 withAnimation:(UIStatusBarAnimation)animation) 131{ 132 if ([[self class] _viewControllerBasedStatusBarAppearance]) { 133 RCTLogError(@"RCTStatusBarManager module requires that the \ 134 UIViewControllerBasedStatusBarAppearance key in the Info.plist is set to NO"); 135 } else { 136#pragma clang diagnostic push 137#pragma clang diagnostic ignored "-Wdeprecated-declarations" 138 [RCTSharedApplication() setStatusBarHidden:hidden 139 withAnimation:animation]; 140 _capturedStatusBarProperties[@"hidden"] = @(hidden); 141#pragma clang diagnostic pop 142 } 143} 144 145RCT_EXPORT_METHOD(setNetworkActivityIndicatorVisible:(BOOL)visible) 146{ 147 RCTSharedApplication().networkActivityIndicatorVisible = visible; 148 _capturedStatusBarProperties[@"networkActivityIndicatorVisible"] = @(visible); 149} 150 151/** 152 * Used by the expo menu to restore status bar state between bridges. 153 * Normally nobody should use this method because it bypasses the JS state used by the StatusBar component. 154 */ 155RCT_REMAP_METHOD(_captureProperties, 156 _capturePropertiesWithResolver:(RCTPromiseResolveBlock)resolve 157 rejecter:(RCTPromiseRejectBlock)reject) 158{ 159 resolve([self _currentStatusBarProperties]); 160} 161 162/** 163 * Used by the expo menu to restore status bar state between bridges. 164 * Normally nobody should use this method because it bypasses the JS state used by the StatusBar component. 165 */ 166RCT_EXPORT_METHOD(_applyPropertiesAndForget:(NSDictionary *)properties) 167{ 168 [self _applyCapturedProperties:properties]; 169} 170 171#pragma mark - internal 172 173+ (BOOL)_viewControllerBasedStatusBarAppearance 174{ 175 static BOOL value; 176 static dispatch_once_t onceToken; 177 dispatch_once(&onceToken, ^{ 178 value = [[[NSBundle mainBundle] objectForInfoDictionaryKey: 179 @"UIViewControllerBasedStatusBarAppearance"] ?: @YES boolValue]; 180 }); 181 182 return value; 183} 184 185- (NSDictionary *)_currentStatusBarProperties 186{ 187 UIApplication *currentApplication = RCTSharedApplication(); 188 return @{ 189 @"style": @(currentApplication.statusBarStyle), 190 @"networkActivityIndicatorVisible": @(currentApplication.isNetworkActivityIndicatorVisible), 191 @"hidden": @(currentApplication.isStatusBarHidden), 192 }; 193} 194 195- (void)_applyCapturedProperties:(NSDictionary *)properties 196{ 197 UIApplication *currentApplication = RCTSharedApplication(); 198 if (![[self class] _viewControllerBasedStatusBarAppearance]) { 199#pragma clang diagnostic push 200#pragma clang diagnostic ignored "-Wdeprecated-declarations" 201 [currentApplication setStatusBarStyle:(UIStatusBarStyle)[properties[@"style"] integerValue] animated:NO]; 202 [currentApplication setStatusBarHidden:[properties[@"hidden"] boolValue] withAnimation:UIStatusBarAnimationNone]; 203#pragma clang diagnostic pop 204 } 205 currentApplication.networkActivityIndicatorVisible = [properties[@"networkActivityIndicatorVisible"] boolValue]; 206} 207 208- (void)_bridgeDidForeground:(__unused NSNotification *)notif 209{ 210 [self _applyCapturedProperties:_capturedStatusBarProperties]; 211} 212 213- (void)_bridgeDidBackground:(__unused NSNotification *)notif 214{ 215 _capturedStatusBarProperties = [[self _currentStatusBarProperties] mutableCopy]; 216} 217 218#endif //TARGET_OS_TV 219 220@end 221