Apple TV support 1: existing Objective C code should compile for tvOS

Summary:
First commit for Apple TV support: changes to existing Objective-C code so that it will compile correctly for tvOS.
Closes https://github.com/facebook/react-native/pull/9649

Differential Revision: D3916021

Pulled By: javache

fbshipit-source-id: 34acc9daf3efff835ffe38c43ba5d4098a02c830
This commit is contained in:
Douglas Lowder 2016-09-27 06:19:45 -07:00 committed by Facebook Github Bot 5
parent 339531065f
commit d368ebfab2
29 changed files with 197 additions and 142 deletions

View File

@ -19,7 +19,9 @@
#import "RCTJavaScriptLoader.h"
#import "RCTLinkingManager.h"
#import "RCTRootView.h"
#if !TARGET_OS_TV
#import "RCTPushNotificationManager.h"
#endif
@interface AppDelegate() <RCTBridgeDelegate>
@ -79,6 +81,8 @@
# pragma mark - Push Notifications
#if !TARGET_OS_TV
// Required to register for notifications
- (void)application:(__unused UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
@ -109,4 +113,6 @@
[RCTPushNotificationManager didReceiveLocalNotification:notification];
}
#endif
@end

View File

@ -64,7 +64,9 @@ RCT_EXPORT_MODULE();
[_resizableRootView setSizeFlexibility:RCTRootViewSizeFlexibilityHeight];
_currentSizeTextView = [UITextView new];
#ifndef TARGET_OS_TV
_currentSizeTextView.editable = NO;
#endif
_currentSizeTextView.text = @"Resizable view has not been resized yet";
_currentSizeTextView.textColor = [UIColor blackColor];
_currentSizeTextView.backgroundColor = [UIColor whiteColor];

View File

@ -63,11 +63,13 @@
- (void)testFamily
{
#if !TARGET_OS_TV
{
UIFont *expected = [UIFont fontWithName:@"Cochin" size:14];
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin"}];
RCTAssertEqualFonts(expected, result);
}
#endif
{
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue" size:14];
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Helvetica Neue"}];
@ -135,6 +137,7 @@
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"HelveticaNeue-Bold", @"fontWeight": @"normal"}];
RCTAssertEqualFonts(expected, result);
}
#if !TARGET_OS_TV
{
UIFont *expected = [UIFont fontWithName:@"Cochin-Bold" size:14];
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin", @"fontWeight": @"700"}];
@ -145,6 +148,7 @@
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin", @"fontWeight": @"100"}];
RCTAssertEqualFonts(expected, result);
}
#endif
}
- (void)testFamilyAndStyle

View File

@ -96,12 +96,16 @@
_textView = [[RCTUITextView alloc] initWithFrame:CGRectZero];
_textView.backgroundColor = [UIColor clearColor];
_textView.textColor = [UIColor blackColor];
#if !TARGET_OS_TV
_textView.scrollsToTop = NO;
#endif
_textView.scrollEnabled = NO;
_textView.delegate = self;
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
#if !TARGET_OS_TV
_scrollView.scrollsToTop = NO;
#endif
[_scrollView addSubview:_textView];
[self addSubview:_scrollView];
@ -283,11 +287,13 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string)
if (_placeholder) {
_placeholderView = [[UITextView alloc] initWithFrame:self.bounds];
_placeholderView.editable = NO;
_placeholderView.userInteractionEnabled = NO;
_placeholderView.backgroundColor = [UIColor clearColor];
_placeholderView.scrollEnabled = NO;
#if !TARGET_OS_TV
_placeholderView.editable = NO;
_placeholderView.scrollsToTop = NO;
#endif
_placeholderView.attributedText =
[[NSAttributedString alloc] initWithString:_placeholder attributes:@{
NSFontAttributeName : (_textView.font ? _textView.font : [self defaultPlaceholderFont]),

View File

@ -70,8 +70,11 @@ RCT_EXPORT_MODULE()
[_socket setDelegateDispatchQueue:_jsQueue];
NSURL *startDevToolsURL = [NSURL URLWithString:@"/launch-js-devtools" relativeToURL:_url];
[NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:startDevToolsURL] delegate:nil];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:[NSURLRequest requestWithURL:startDevToolsURL]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){}];
[dataTask resume];
if (![self connectToProxy]) {
RCTLogError(@"Connection to %@ timed out. Are you running node proxy? If "
"you are running on the device, check if you have the right IP "

View File

@ -72,10 +72,14 @@ typedef NSURL RCTFileURL;
+ (UIKeyboardType)UIKeyboardType:(id)json;
+ (UIKeyboardAppearance)UIKeyboardAppearance:(id)json;
+ (UIReturnKeyType)UIReturnKeyType:(id)json;
#if !TARGET_OS_TV
+ (UIDataDetectorTypes)UIDataDetectorTypes:(id)json;
#endif
+ (UIViewContentMode)UIViewContentMode:(id)json;
#if !TARGET_OS_TV
+ (UIBarStyle)UIBarStyle:(id)json;
#endif
+ (CGFloat)CGFloat:(id)json;
+ (CGPoint)CGPoint:(id)json;

View File

@ -330,6 +330,7 @@ RCT_ENUM_CONVERTER(UIKeyboardType, (@{
@"numeric": @(UIKeyboardTypeDecimalPad),
}), UIKeyboardTypeDefault, integerValue)
#if !TARGET_OS_TV
RCT_MULTI_ENUM_CONVERTER(UIDataDetectorTypes, (@{
@"phoneNumber": @(UIDataDetectorTypePhoneNumber),
@"link": @(UIDataDetectorTypeLink),
@ -338,6 +339,7 @@ RCT_MULTI_ENUM_CONVERTER(UIDataDetectorTypes, (@{
@"none": @(UIDataDetectorTypeNone),
@"all": @(UIDataDetectorTypeAll),
}), UIDataDetectorTypePhoneNumber, unsignedLongLongValue)
#endif
RCT_ENUM_CONVERTER(UIKeyboardAppearance, (@{
@"default": @(UIKeyboardAppearanceDefault),
@ -379,10 +381,12 @@ RCT_ENUM_CONVERTER(UIViewContentMode, (@{
@"stretch": @(UIViewContentModeScaleToFill),
}), UIViewContentModeScaleAspectFill, integerValue)
#if !TARGET_OS_TV
RCT_ENUM_CONVERTER(UIBarStyle, (@{
@"default": @(UIBarStyleDefault),
@"black": @(UIBarStyleBlack),
}), UIBarStyleDefault, integerValue)
#endif
// TODO: normalise the use of w/width so we can do away with the alias values (#6566645)
static void RCTConvertCGStructValue(const char *type, NSArray *fields, NSDictionary *aliases, CGFloat *result, id json)

View File

@ -92,14 +92,6 @@ RCT_EXTERN UIViewController *__nullable RCTPresentedViewController(void);
// Does this device support force touch (aka 3D Touch)?
RCT_EXTERN BOOL RCTForceTouchAvailable(void);
// Return a UIAlertView initialized with the given values
// or nil if running in an app extension
RCT_EXTERN UIAlertView *__nullable RCTAlertView(NSString *title,
NSString *__nullable message,
id __nullable delegate,
NSString *__nullable cancelButtonTitle,
NSArray<NSString *> *__nullable otherButtonTitles);
// Create an NSError in the RCTErrorDomain
RCT_EXTERN NSError *RCTErrorWithMessage(NSString *message);

View File

@ -493,31 +493,6 @@ BOOL RCTForceTouchAvailable(void)
(RCTKeyWindow() ?: [UIView new]).traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable;
}
UIAlertView *__nullable RCTAlertView(NSString *title,
NSString *__nullable message,
id __nullable delegate,
NSString *__nullable cancelButtonTitle,
NSArray<NSString *> *__nullable otherButtonTitles)
{
if (RCTRunningInAppExtension()) {
RCTLogError(@"RCTAlertView is unavailable when running in an app extension");
return nil;
}
UIAlertView *alertView = [UIAlertView new];
alertView.title = title;
alertView.message = message;
alertView.delegate = delegate;
if (cancelButtonTitle != nil) {
[alertView addButtonWithTitle:cancelButtonTitle];
alertView.cancelButtonIndex = 0;
}
for (NSString *buttonTitle in otherButtonTitles) {
[alertView addButtonWithTitle:buttonTitle];
}
return alertView;
}
NSError *RCTErrorWithMessage(NSString *message)
{
NSDictionary<NSString *, id> *errorInfo = @{NSLocalizedDescriptionKey: message};

View File

@ -12,6 +12,14 @@
#import "RCTBridgeModule.h"
#import "RCTInvalidating.h"
typedef NS_ENUM(NSInteger, RCTAlertViewStyle) {
RCTAlertViewStyleDefault = 0,
RCTAlertViewStyleSecureTextInput,
RCTAlertViewStylePlainTextInput,
RCTAlertViewStyleLoginAndPasswordInput
};
@interface RCTAlertManager : NSObject <RCTBridgeModule, RCTInvalidating>
@end

View File

@ -16,22 +16,21 @@
@implementation RCTConvert (UIAlertViewStyle)
RCT_ENUM_CONVERTER(UIAlertViewStyle, (@{
@"default": @(UIAlertViewStyleDefault),
@"secure-text": @(UIAlertViewStyleSecureTextInput),
@"plain-text": @(UIAlertViewStylePlainTextInput),
@"login-password": @(UIAlertViewStyleLoginAndPasswordInput),
}), UIAlertViewStyleDefault, integerValue)
RCT_ENUM_CONVERTER(RCTAlertViewStyle, (@{
@"default": @(RCTAlertViewStyleDefault),
@"secure-text": @(RCTAlertViewStyleSecureTextInput),
@"plain-text": @(RCTAlertViewStylePlainTextInput),
@"login-password": @(RCTAlertViewStyleLoginAndPasswordInput),
}), RCTAlertViewStyleDefault, integerValue)
@end
@interface RCTAlertManager() <UIAlertViewDelegate>
@interface RCTAlertManager()
@end
@implementation RCTAlertManager
{
NSMutableArray<UIAlertView *> *_alerts;
NSMutableArray<UIAlertController *> *_alertControllers;
NSMutableArray<RCTResponseSenderBlock> *_alertCallbacks;
NSMutableArray<NSArray<NSString *> *> *_alertButtonKeys;
@ -46,9 +45,6 @@ RCT_EXPORT_MODULE()
- (void)invalidate
{
for (UIAlertView *alert in _alerts) {
[alert dismissWithClickedButtonIndex:0 animated:YES];
}
for (UIAlertController *alertController in _alertControllers) {
[alertController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}
@ -73,7 +69,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
{
NSString *title = [RCTConvert NSString:args[@"title"]];
NSString *message = [RCTConvert NSString:args[@"message"]];
UIAlertViewStyle type = [RCTConvert UIAlertViewStyle:args[@"type"]];
RCTAlertViewStyle type = [RCTConvert RCTAlertViewStyle:args[@"type"]];
NSArray<NSDictionary *> *buttons = [RCTConvert NSDictionaryArray:args[@"buttons"]];
NSString *defaultValue = [RCTConvert NSString:args[@"defaultValue"]];
NSString *cancelButtonKey = [RCTConvert NSString:args[@"cancelButtonKey"]];
@ -85,7 +81,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
}
if (buttons.count == 0) {
if (type == UIAlertViewStyleDefault) {
if (type == RCTAlertViewStyleDefault) {
buttons = @[@{@"0": RCTUIKitLocalizedString(@"OK")}];
cancelButtonKey = @"0";
} else {
@ -108,14 +104,14 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
message:nil
preferredStyle:UIAlertControllerStyleAlert];
switch (type) {
case UIAlertViewStylePlainTextInput: {
case RCTAlertViewStylePlainTextInput: {
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = NO;
textField.text = defaultValue;
}];
break;
}
case UIAlertViewStyleSecureTextInput: {
case RCTAlertViewStyleSecureTextInput: {
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = RCTUIKitLocalizedString(@"Password");
textField.secureTextEntry = YES;
@ -123,7 +119,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
}];
break;
}
case UIAlertViewStyleLoginAndPasswordInput: {
case RCTAlertViewStyleLoginAndPasswordInput: {
[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = RCTUIKitLocalizedString(@"Login");
textField.text = defaultValue;
@ -134,7 +130,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
}];
break;
}
case UIAlertViewStyleDefault:
case RCTAlertViewStyleDefault:
break;
}
@ -156,11 +152,11 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
style:buttonStyle
handler:^(__unused UIAlertAction *action) {
switch (type) {
case UIAlertViewStylePlainTextInput:
case UIAlertViewStyleSecureTextInput:
case RCTAlertViewStylePlainTextInput:
case RCTAlertViewStyleSecureTextInput:
callback(@[buttonKey, [alertController.textFields.firstObject text]]);
break;
case UIAlertViewStyleLoginAndPasswordInput: {
case RCTAlertViewStyleLoginAndPasswordInput: {
NSDictionary<NSString *, NSString *> *loginCredentials = @{
@"login": [alertController.textFields.firstObject text],
@"password": [alertController.textFields.lastObject text]
@ -168,7 +164,7 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
callback(@[buttonKey, loginCredentials]);
break;
}
case UIAlertViewStyleDefault:
case RCTAlertViewStyleDefault:
callback(@[buttonKey]);
break;
}
@ -183,37 +179,4 @@ RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args
[presentingController presentViewController:alertController animated:YES completion:nil];
}
#pragma mark - UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSUInteger index = [_alerts indexOfObject:alertView];
RCTAssert(index != NSNotFound, @"Dismissed alert was not recognised");
RCTResponseSenderBlock callback = _alertCallbacks[index];
NSArray<NSString *> *buttonKeys = _alertButtonKeys[index];
switch (alertView.alertViewStyle) {
case UIAlertViewStylePlainTextInput:
case UIAlertViewStyleSecureTextInput:
callback(@[buttonKeys[buttonIndex], [alertView textFieldAtIndex:0].text]);
break;
case UIAlertViewStyleLoginAndPasswordInput: {
NSDictionary<NSString *, NSString *> *loginCredentials = @{
@"login": [alertView textFieldAtIndex:0].text,
@"password": [alertView textFieldAtIndex:1].text,
};
callback(@[buttonKeys[buttonIndex], loginCredentials]);
break;
}
case UIAlertViewStyleDefault:
callback(@[buttonKeys[buttonIndex]]);
break;
}
[_alerts removeObjectAtIndex:index];
[_alertCallbacks removeObjectAtIndex:index];
[_alertButtonKeys removeObjectAtIndex:index];
}
@end

View File

@ -67,7 +67,11 @@ static NSString *RCTGetStorageDirectory()
static NSString *storageDirectory = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if TARGET_OS_TV
storageDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
#else
storageDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
#endif
storageDirectory = [storageDirectory stringByAppendingPathComponent:RCTStorageDirectory];
});
return storageDirectory;
@ -214,6 +218,10 @@ RCT_EXPORT_MODULE()
{
RCTAssertThread(RCTGetMethodQueue(), @"Must be executed on storage thread");
#if TARGET_OS_TV
RCTLogWarn(@"Persistent storage is not supported on tvOS, your data may be removed at any point.")
#endif
NSError *error = nil;
if (!RCTHasCreatedStorageDirectory) {
[[NSFileManager defaultManager] createDirectoryAtPath:RCTGetStorageDirectory()

View File

@ -76,8 +76,11 @@ RCT_EXPORT_METHOD(showMessage:(NSString *)message color:(UIColor *)color backgro
if (!self->_window && !RCTRunningInTestEnvironment()) {
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
self->_window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 22)];
#if TARGET_OS_TV
self->_window.windowLevel = UIWindowLevelNormal + 1;
#else
self->_window.windowLevel = UIWindowLevelStatusBar + 1;
#endif
// set a root VC so rotation is supported
self->_window.rootViewController = [UIViewController new];

View File

@ -120,7 +120,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
@end
@interface RCTDevMenu () <RCTBridgeModule, UIActionSheetDelegate, RCTInvalidating, RCTWebSocketProxyDelegate>
@interface RCTDevMenu () <RCTBridgeModule, RCTInvalidating, RCTWebSocketProxyDelegate>
@property (nonatomic, strong) Class executorClass;
@ -128,7 +128,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
@implementation RCTDevMenu
{
UIActionSheet *_actionSheet;
UIAlertController *_actionSheet;
NSUserDefaults *_defaults;
NSMutableDictionary *_settings;
NSURLSessionDataTask *_updateTask;
@ -409,7 +409,7 @@ RCT_EXPORT_MODULE()
{
_presentedItems = nil;
[_updateTask cancel];
[_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES];
[_actionSheet dismissViewControllerAnimated:YES completion:^(void){}];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@ -423,7 +423,7 @@ RCT_EXPORT_MODULE()
- (void)toggle
{
if (_actionSheet) {
[_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES];
[_actionSheet dismissViewControllerAnimated:YES completion:^(void){}];
_actionSheet = nil;
} else {
[self show];
@ -458,13 +458,11 @@ RCT_EXPORT_MODULE()
Class jsDebuggingExecutorClass = objc_lookUpClass("RCTWebSocketExecutor");
if (!jsDebuggingExecutorClass) {
[items addObject:[RCTDevMenuItem buttonItemWithTitle:[NSString stringWithFormat:@"%@ Debugger Unavailable", _webSocketExecutorName] handler:^{
UIAlertView *alert = RCTAlertView(
[NSString stringWithFormat:@"%@ Debugger Unavailable", self->_webSocketExecutorName],
[NSString stringWithFormat:@"You need to include the RCTWebSocket library to enable %@ debugging", self->_webSocketExecutorName],
nil,
@"OK",
nil);
[alert show];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"%@ Debugger Unavailable", self->_webSocketExecutorName]
message:[NSString stringWithFormat:@"You need to include the RCTWebSocket library to enable %@ debugging", self->_webSocketExecutorName]
preferredStyle:UIAlertControllerStyleAlert];
[RCTPresentedViewController() presentViewController:alertController animated:YES completion:NULL];
}]];
} else {
BOOL isDebuggingJS = _executorClass && _executorClass == jsDebuggingExecutorClass;
@ -514,55 +512,45 @@ RCT_EXPORT_METHOD(show)
return;
}
UIActionSheet *actionSheet = [UIActionSheet new];
actionSheet.title = @"React Native: Development";
actionSheet.delegate = self;
_actionSheet = [UIAlertController alertControllerWithTitle:@"React Native: Development"
message:@""
preferredStyle:UIAlertControllerStyleActionSheet];
NSArray<RCTDevMenuItem *> *items = [self menuItems];
for (RCTDevMenuItem *item in items) {
switch (item.type) {
case RCTDevMenuTypeButton: {
[actionSheet addButtonWithTitle:item.title];
[_actionSheet addAction:[UIAlertAction actionWithTitle:item.title
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
// Cancel button tappped.
[item callHandler];
}]];
break;
}
case RCTDevMenuTypeToggle: {
BOOL selected = [item.value boolValue];
[actionSheet addButtonWithTitle:selected? item.selectedTitle : item.title];
[_actionSheet addAction:[UIAlertAction actionWithTitle:(selected? item.selectedTitle : item.title)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
BOOL value = [self->_settings[item.key] boolValue];
[self updateSetting:item.key value:@(!value)]; // will call handler
}]];
break;
}
}
}
[actionSheet addButtonWithTitle:@"Cancel"];
actionSheet.cancelButtonIndex = actionSheet.numberOfButtons - 1;
[_actionSheet addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
}]];
actionSheet.actionSheetStyle = UIBarStyleBlack;
[actionSheet showInView:RCTKeyWindow().rootViewController.view];
_actionSheet = actionSheet;
_presentedItems = items;
[RCTPresentedViewController() presentViewController:_actionSheet animated:YES completion:^(void){}];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
_actionSheet = nil;
if (buttonIndex == actionSheet.cancelButtonIndex) {
return;
}
RCTDevMenuItem *item = _presentedItems[buttonIndex];
switch (item.type) {
case RCTDevMenuTypeButton: {
[item callHandler];
break;
}
case RCTDevMenuTypeToggle: {
BOOL value = [_settings[item.key] boolValue];
[self updateSetting:item.key value:@(!value)]; // will call handler
break;
}
}
return;
}
RCT_EXPORT_METHOD(reload)
{

View File

@ -19,6 +19,8 @@ RCT_EXPORT_MODULE()
- (void)startObserving
{
#if !TARGET_OS_TV
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
#define ADD_KEYBOARD_HANDLER(NAME, SELECTOR) \
@ -33,6 +35,8 @@ RCT_EXPORT_MODULE()
#undef ADD_KEYBOARD_HANDLER
#endif
}
- (NSArray<NSString *> *)supportedEvents
@ -94,6 +98,9 @@ static NSString *RCTAnimationNameForCurve(UIViewAnimationCurve curve)
static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification)
{
#if TARGET_OS_TV
return @{};
#else
NSDictionary *userInfo = notification.userInfo;
CGRect beginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
@ -106,4 +113,5 @@ static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification)
@"duration": @(duration * 1000.0), // ms
@"easing": RCTAnimationNameForCurve(curve),
};
#endif
}

View File

@ -60,8 +60,10 @@
_stackTraceTableView.delegate = self;
_stackTraceTableView.dataSource = self;
_stackTraceTableView.backgroundColor = [UIColor clearColor];
#if !TARGET_OS_TV
_stackTraceTableView.separatorColor = [UIColor colorWithWhite:1 alpha:0.3];
_stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
#endif
_stackTraceTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
[rootView addSubview:_stackTraceTableView];
@ -175,9 +177,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
[fullStackTrace appendFormat:@" %@\n", [self formatFrameSource:stackFrame]];
}
}
#if !TARGET_OS_TV
UIPasteboard *pb = [UIPasteboard generalPasteboard];
[pb setString:fullStackTrace];
#endif
}
- (NSString *)formatFrameSource:(RCTJSStackFrame *)stackFrame

View File

@ -99,6 +99,7 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
// `UIKeyboardWillChangeFrameNotification`s.
+ (void)initializeStatics
{
#if !TARGET_OS_TV
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[[NSNotificationCenter defaultCenter] addObserver:self
@ -106,12 +107,15 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
name:UIKeyboardWillChangeFrameNotification
object:nil];
});
#endif
}
+ (void)keyboardWillChangeFrame:(NSNotification *)notification
{
#if !TARGET_OS_TV
NSDictionary *userInfo = notification.userInfo;
_currentKeyboardAnimationCurve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
#endif
}
- (instancetype)initWithDuration:(NSTimeInterval)duration dictionary:(NSDictionary *)config
@ -225,8 +229,9 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim
NSDictionary *_componentDataByName;
NSMutableSet<id<RCTComponent>> *_bridgeTransactionListeners;
#if !TARGET_OS_TV
UIInterfaceOrientation _currentInterfaceOrientation;
#endif
}
@synthesize bridge = _bridge;
@ -244,6 +249,7 @@ RCT_EXPORT_MODULE()
- (void)interfaceOrientationWillChange:(NSNotification *)notification
{
#if !TARGET_OS_TV
UIInterfaceOrientation nextOrientation =
[notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] integerValue];
@ -260,6 +266,7 @@ RCT_EXPORT_MODULE()
}
_currentInterfaceOrientation = nextOrientation;
#endif
}
- (void)invalidate
@ -339,11 +346,13 @@ RCT_EXPORT_MODULE()
selector:@selector(didReceiveNewContentSizeMultiplier)
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
object:_bridge.accessibilityManager];
#if !TARGET_OS_TV
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(interfaceOrientationWillChange:)
name:UIApplicationWillChangeStatusBarOrientationNotification
object:nil];
#endif
[RCTAnimation initializeStatics];
}
@ -1510,7 +1519,9 @@ RCT_EXPORT_METHOD(clearJSResponder)
allJSConstants[name] = constantsNamespace;
}];
#if !TARGET_OS_TV
_currentInterfaceOrientation = [RCTSharedApplication() statusBarOrientation];
#endif
[allJSConstants addEntriesFromDictionary:@{
@"customBubblingEventTypes": bubblingEvents,
@"customDirectEventTypes": directEvents,

View File

@ -381,6 +381,7 @@ void RCTProfileUnhookModules(RCTBridge *bridge)
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
#if !TARGET_OS_TV
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[[NSURL fileURLWithPath:outFile]]
applicationActivities:nil];
activityViewController.completionHandler = ^(__unused NSString *activityType, __unused BOOL completed) {
@ -392,6 +393,7 @@ void RCTProfileUnhookModules(RCTBridge *bridge)
animated:YES
completion:nil];
});
#endif
});
} else {
RCTProfileInit(RCTProfilingBridge());
@ -753,6 +755,7 @@ void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *data)
encoding:NSUTF8StringEncoding];
if (message.length) {
#if !TARGET_OS_TV
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"Profile"
message:message
@ -760,6 +763,7 @@ void RCTProfileSendResult(RCTBridge *bridge, NSString *route, NSData *data)
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
});
#endif
}
}
}];

View File

@ -104,7 +104,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
UIView *view = [self.manager view];
view.reactTag = tag;
#if !TARGET_OS_TV
view.multipleTouchEnabled = YES;
#endif
view.userInteractionEnabled = YES; // required for touch handling
view.layer.allowsGroupOpacity = YES; // required for touch handling
return view;

View File

@ -149,6 +149,7 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
}
}
#if !TARGET_OS_TV
- (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view
didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState
@ -172,6 +173,7 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
}
}
}
#endif //TARGET_OS_TV
- (MKAnnotationView *)mapView:(RCTMap *)mapView
viewForAnnotation:(RCTMapAnnotation *)annotation
@ -280,7 +282,9 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
}
}
#if !TARGET_OS_TV
annotationView.draggable = annotation.draggable;
#endif
return annotationView;
}

View File

@ -25,7 +25,9 @@
RCTModalHostViewController *_modalViewController;
RCTTouchHandler *_touchHandler;
UIView *_reactSubview;
#if !TARGET_OS_TV
UIInterfaceOrientation _lastKnownOrientation;
#endif
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
@ -61,6 +63,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder)
- (void)notifyForOrientationChange
{
#if !TARGET_OS_TV
if (!_onOrientationChange) {
return;
}
@ -77,6 +80,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder)
@"orientation": isPortrait ? @"portrait" : @"landscape",
};
_onOrientationChange(eventPayload);
#endif
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
@ -119,7 +123,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder)
if (!_isPresented && self.window) {
RCTAssert(self.reactViewController, @"Can't present modal view controller without a presenting view controller");
#if !TARGET_OS_TV
_modalViewController.supportedInterfaceOrientations = [self supportedOrientationsMask];
#endif
if ([self.animationType isEqualToString:@"fade"]) {
_modalViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
} else if ([self.animationType isEqualToString:@"slide"]) {
@ -161,6 +167,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder)
_modalViewController.modalPresentationStyle = transparent ? UIModalPresentationOverFullScreen : UIModalPresentationFullScreen;
}
#if !TARGET_OS_TV
- (UIInterfaceOrientationMask)supportedOrientationsMask
{
if (_supportedOrientations.count == 0) {
@ -187,5 +194,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:coder)
}
return supportedOrientations;
}
#endif
@end

View File

@ -13,6 +13,8 @@
@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds);
#if !TARGET_OS_TV
@property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations;
#endif
@end

View File

@ -15,8 +15,10 @@
@implementation RCTModalHostViewController
{
CGRect _lastViewFrame;
#if !TARGET_OS_TV
UIStatusBarStyle _preferredStatusBarStyle;
BOOL _preferredStatusBarHidden;
#endif
}
- (instancetype)init
@ -25,8 +27,10 @@
return nil;
}
#if !TARGET_OS_TV
_preferredStatusBarStyle = [[UIApplication sharedApplication] statusBarStyle];
_preferredStatusBarHidden = [[UIApplication sharedApplication] isStatusBarHidden];
#endif
return self;
}
@ -41,6 +45,7 @@
}
}
#if !TARGET_OS_TV
- (UIStatusBarStyle)preferredStatusBarStyle
{
return _preferredStatusBarStyle;
@ -50,6 +55,7 @@
{
return _preferredStatusBarHidden;
}
#endif
#if RCT_DEV
- (UIInterfaceOrientationMask)supportedInterfaceOrientations

View File

@ -169,6 +169,7 @@ NSInteger kNeverProgressed = -10000;
*/
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
#if !TARGET_OS_TV
if (self.interactivePopGestureRecognizer.state == UIGestureRecognizerStateBegan) {
if (self.navigationLock == RCTNavigationLockNone) {
self.navigationLock = RCTNavigationLockNative;
@ -180,7 +181,9 @@ NSInteger kNeverProgressed = -10000;
// recognizer when we lock the navigation.
RCTAssert(NO, @"Should never receive gesture start while JS locks navigator");
}
} else {
} else
#endif //TARGET_OS_TV
{
if (self.navigationLock == RCTNavigationLockNone) {
// Must be coming from native interaction, lock it - it will be unlocked
// in `didMoveToNavigationController`
@ -348,19 +351,23 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)setInteractivePopGestureEnabled:(BOOL)interactivePopGestureEnabled
{
#if !TARGET_OS_TV
_interactivePopGestureEnabled = interactivePopGestureEnabled;
_navigationController.interactivePopGestureRecognizer.delegate = self;
_navigationController.interactivePopGestureRecognizer.enabled = interactivePopGestureEnabled;
_popGestureState = interactivePopGestureEnabled ? RCTPopGestureStateEnabled : RCTPopGestureStateDisabled;
#endif
}
- (void)dealloc
{
#if !TARGET_OS_TV
if (_navigationController.interactivePopGestureRecognizer.delegate == self) {
_navigationController.interactivePopGestureRecognizer.delegate = nil;
}
#endif
_navigationController.delegate = nil;
[_navigationController removeFromParentViewController];
}
@ -422,7 +429,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
{
if (_navigationController.navigationLock == RCTNavigationLockNone) {
_navigationController.navigationLock = RCTNavigationLockJavaScript;
#if !TARGET_OS_TV
_navigationController.interactivePopGestureRecognizer.enabled = NO;
#endif
return YES;
}
return NO;
@ -435,7 +444,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
// Unless the pop gesture has been explicitly disabled (RCTPopGestureStateDisabled),
// Set interactivePopGestureRecognizer.enabled to YES
// If the popGestureState is RCTPopGestureStateDefault the default behavior will be maintained
#if !TARGET_OS_TV
_navigationController.interactivePopGestureRecognizer.enabled = self.popGestureState != RCTPopGestureStateDisabled;
#endif
}
/**

View File

@ -15,7 +15,9 @@
RCT_ENUM_CONVERTER(UIProgressViewStyle, (@{
@"default": @(UIProgressViewStyleDefault),
#if !TARGET_OS_TV
@"bar": @(UIProgressViewStyleBar),
#endif
}), UIProgressViewStyleDefault, integerValue)
@end

View File

@ -14,7 +14,9 @@
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#if !TARGET_OS_TV
#import "RCTRefreshControl.h"
#endif
#import "RCTUIManager.h"
#import "RCTUtils.h"
#import "UIView+Private.h"
@ -140,7 +142,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
@property (nonatomic, assign) BOOL centerContent;
#if !TARGET_OS_TV
@property (nonatomic, strong) RCTRefreshControl *rctRefreshControl;
#endif
@end
@ -275,9 +279,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
CGFloat scrollTop = self.bounds.origin.y + self.contentInset.top;
// If the RefreshControl is refreshing, remove it's height so sticky headers are
// positioned properly when scrolling down while refreshing.
#if !TARGET_OS_TV
if (_rctRefreshControl != nil && _rctRefreshControl.refreshing) {
scrollTop -= _rctRefreshControl.frame.size.height;
}
#endif
// Find the section headers that need to be docked
__block UIView *previousHeader = nil;
@ -358,6 +364,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
return [super hitTest:point withEvent:event];
}
#if !TARGET_OS_TV
- (void)setRctRefreshControl:(RCTRefreshControl *)refreshControl
{
if (_rctRefreshControl) {
@ -366,6 +373,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
_rctRefreshControl = refreshControl;
[self addSubview:_rctRefreshControl];
}
#endif //TARGET_OS_TV
@end
@ -423,9 +431,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
{
[super insertReactSubview:view atIndex:atIndex];
#if !TARGET_OS_TV
if ([view isKindOfClass:[RCTRefreshControl class]]) {
[_scrollView setRctRefreshControl:(RCTRefreshControl *)view];
} else {
} else
#endif
{
RCTAssert(_contentView == nil, @"RCTScrollView may only contain a single subview");
_contentView = view;
[_scrollView addSubview:view];
@ -435,9 +446,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)removeReactSubview:(UIView *)subview
{
[super removeReactSubview:subview];
#if !TARGET_OS_TV
if ([subview isKindOfClass:[RCTRefreshControl class]]) {
[_scrollView setRctRefreshControl:nil];
} else {
} else
#endif
{
RCTAssert(_contentView == subview, @"Attempted to remove non-existent subview");
_contentView = nil;
}
@ -491,11 +505,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
_scrollView.frame = self.bounds;
_scrollView.contentOffset = originalOffset;
#if !TARGET_OS_TV
// Adjust the refresh control frame if the scrollview layout changes.
RCTRefreshControl *refreshControl = _scrollView.rctRefreshControl;
if (refreshControl && refreshControl.refreshing) {
refreshControl.frame = (CGRect){_scrollView.contentOffset, {_scrollView.frame.size.width, refreshControl.frame.size.height}};
}
#endif
[self updateClippedSubviews];
}
@ -926,9 +942,11 @@ RCT_SET_AND_PRESERVE_OFFSET(setIndicatorStyle, indicatorStyle, UIScrollViewIndic
RCT_SET_AND_PRESERVE_OFFSET(setKeyboardDismissMode, keyboardDismissMode, UIScrollViewKeyboardDismissMode)
RCT_SET_AND_PRESERVE_OFFSET(setMaximumZoomScale, maximumZoomScale, CGFloat)
RCT_SET_AND_PRESERVE_OFFSET(setMinimumZoomScale, minimumZoomScale, CGFloat)
RCT_SET_AND_PRESERVE_OFFSET(setPagingEnabled, isPagingEnabled, BOOL)
RCT_SET_AND_PRESERVE_OFFSET(setScrollEnabled, isScrollEnabled, BOOL)
#if !TARGET_OS_TV
RCT_SET_AND_PRESERVE_OFFSET(setPagingEnabled, isPagingEnabled, BOOL)
RCT_SET_AND_PRESERVE_OFFSET(setScrollsToTop, scrollsToTop, BOOL)
#endif
RCT_SET_AND_PRESERVE_OFFSET(setShowsHorizontalScrollIndicator, showsHorizontalScrollIndicator, BOOL)
RCT_SET_AND_PRESERVE_OFFSET(setShowsVerticalScrollIndicator, showsVerticalScrollIndicator, BOOL)
RCT_SET_AND_PRESERVE_OFFSET(setZoomScale, zoomScale, CGFloat);

View File

@ -151,12 +151,18 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (UITabBarItemPositioning)itemPositoning
{
#if TARGET_OS_TV
return 0;
#else
return _tabController.tabBar.itemPositioning;
#endif
}
- (void)setItemPositioning:(UITabBarItemPositioning)itemPositioning
{
#if !TARGET_OS_TV
_tabController.tabBar.itemPositioning = itemPositioning;
#endif
}
#pragma mark - UITabBarControllerDelegate

View File

@ -32,7 +32,9 @@ RCT_ENUM_CONVERTER(UITabBarSystemItem, (@{
@end
@implementation RCTTabBarItem
@implementation RCTTabBarItem{
UITapGestureRecognizer *_selectRecognizer;
}
@synthesize barItem = _barItem;
@ -84,7 +86,7 @@ RCT_ENUM_CONVERTER(UITabBarSystemItem, (@{
_barItem.selectedImage = oldItem.selectedImage;
_barItem.badgeValue = oldItem.badgeValue;
}
if (_renderAsOriginal) {
self.barItem.image = [_icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
} else {
@ -95,7 +97,7 @@ RCT_ENUM_CONVERTER(UITabBarSystemItem, (@{
- (void)setSelectedIcon:(UIImage *)selectedIcon
{
_selectedIcon = selectedIcon;
if (_renderAsOriginal) {
self.barItem.selectedImage = [_selectedIcon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
} else {

View File

@ -124,7 +124,9 @@ static UIView *RCTFindNavBarShadowViewInView(UIView *view)
UINavigationItem *item = self.navigationItem;
item.title = _navItem.title;
item.titleView = _navItem.titleImageView;
#if !TARGET_OS_TV
item.backBarButtonItem = _navItem.backButtonItem;
#endif //TARGET_OS_TV
item.leftBarButtonItem = _navItem.leftButtonItem;
item.rightBarButtonItem = _navItem.rightButtonItem;
}