Unfork UIManager

This commit is contained in:
Nick Lockwood 2015-03-24 17:37:03 -07:00
parent 909e393f26
commit 94db4facf5
25 changed files with 868 additions and 244 deletions

View File

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
13ACB6741AC2117000FF4204 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13ACB6711AC2113600FF4204 /* libRCTAnimation.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
@ -16,6 +17,13 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
13ACB6701AC2113600FF4204 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTAnimation;
};
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
@ -33,6 +41,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = ../../Libraries/Animation/RCTAnimation.xcodeproj; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* 2048.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = 2048.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = 2048/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = 2048/AppDelegate.m; sourceTree = "<group>"; };
@ -49,6 +58,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
13ACB6741AC2117000FF4204 /* libRCTAnimation.a in Frameworks */,
8323482C1A77B59500B55238 /* libReactKit.a in Frameworks */,
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
);
@ -57,6 +67,14 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
13ACB66D1AC2113500FF4204 /* Products */ = {
isa = PBXGroup;
children = (
13ACB6711AC2113600FF4204 /* libRCTAnimation.a */,
);
name = Products;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* 2048 */ = {
isa = PBXGroup;
children = (
@ -75,6 +93,7 @@
children = (
834D32361A76971A00F38302 /* ReactKit.xcodeproj */,
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@ -153,6 +172,10 @@
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 13ACB66D1AC2113500FF4204 /* Products */;
ProjectRef = 13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */;
},
{
ProductGroup = 832341B11AAA6A8300B99B32 /* Products */;
ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
@ -170,6 +193,13 @@
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
13ACB6711AC2113600FF4204 /* libRCTAnimation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTAnimation.a;
remoteRef = 13ACB6701AC2113600FF4204 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;

View File

@ -0,0 +1,256 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
13BE3DEE1AC21097009241FE /* RCTAnimationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAnimationManager.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRCTAnimation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimation.a; sourceTree = BUILT_PRODUCTS_DIR; };
13BE3DEC1AC21097009241FE /* RCTAnimationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationManager.h; sourceTree = "<group>"; };
13BE3DED1AC21097009241FE /* RCTAnimationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationManager.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libRCTAnimation.a */,
);
name = Products;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
13BE3DEC1AC21097009241FE /* RCTAnimationManager.h */,
13BE3DED1AC21097009241FE /* RCTAnimationManager.m */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* RCTAnimation */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimation" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RCTAnimation;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libRCTAnimation.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimation" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RCTAnimation */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13BE3DEE1AC21097009241FE /* RCTAnimationManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../ReactKit/**",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/ReactKit/build/Debug-iphoneos",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTAnimation;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../ReactKit/**",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"/Users/nicklockwood/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/ReactKit/build/Debug-iphoneos",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RCTAnimation;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}

View File

@ -7,6 +7,7 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Accelerate/Accelerate.h>
#import <Foundation/Foundation.h>
#import "RCTBridgeModule.h"

View File

@ -9,7 +9,6 @@
#import "RCTAnimationManager.h"
#import <Accelerate/Accelerate.h>
#import <UIKit/UIKit.h>
#import "RCTSparseArray.h"
@ -54,10 +53,10 @@
return ^(CGFloat t) {
const CGFloat *delta = deltaData.bytes;
const CGFloat *fromArray = fromData.bytes;
const CGFloat *_fromArray = fromData.bytes;
CGFloat value[count];
CG_APPEND(vDSP_vma,,D)(delta, 1, &t, 0, fromArray, 1, value, 1, count);
CG_APPEND(vDSP_vma,,D)(delta, 1, &t, 0, _fromArray, 1, value, 1, count);
return [NSValue valueWithBytes:value objCType:typeName];
};
}
@ -84,10 +83,10 @@
} else if ([obj respondsToSelector:@selector(count)]) {
switch ([obj count]) {
case 2:
if ([obj respondsToSelector:@selector(objectForKey:)] && [obj objectForKey:@"w"]) {
toValue = [NSValue valueWithCGSize:[RCTConvert CGSize:obj]];
} else {
if ([obj respondsToSelector:@selector(objectForKeyedSubscript:)] && obj[@"x"]) {
toValue = [NSValue valueWithCGPoint:[RCTConvert CGPoint:obj]];
} else {
toValue = [NSValue valueWithCGSize:[RCTConvert CGSize:obj]];
}
break;
case 4:

View File

@ -50,9 +50,9 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification";
+ (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
#ifdef __IPHONE_8_0
[application registerForRemoteNotifications];
#endif
if ([application respondsToSelector:@selector(registerForRemoteNotifications)]) {
[application registerForRemoteNotifications];
}
}
+ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification
@ -112,13 +112,22 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification";
{
RCT_EXPORT();
#ifdef __IPHONE_8_0
UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert;
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
#else
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
// if we are targeting iOS 7, *and* the new UIUserNotificationSettings
// class is not available, then register using the old mechanism
if (![UIUserNotificationSettings class]) {
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert];
return;
}
#endif
UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert;
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
}
+ (void)checkPermissions:(RCTResponseSenderBlock)callback
@ -126,17 +135,11 @@ NSString *const RCTOpenURLNotification = @"RCTOpenURLNotification";
RCT_EXPORT();
NSMutableDictionary *permissions = [[NSMutableDictionary alloc] init];
#ifdef __IPHONE_8_0
UIUserNotificationType types = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];
permissions[@"alert"] = @((BOOL)(types & UIUserNotificationTypeAlert));
permissions[@"badge"] = @((BOOL)(types & UIUserNotificationTypeBadge));
permissions[@"sound"] = @((BOOL)(types & UIUserNotificationTypeSound));
#else
UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
permissions[@"alert"] = @((BOOL)(types & UIRemoteNotificationTypeAlert));
permissions[@"badge"] = @((BOOL)(types & UIRemoteNotificationTypeBadge));
permissions[@"sound"] = @((BOOL)(types & UIRemoteNotificationTypeSound));
#endif
callback(@[permissions]);
}

View File

@ -17,6 +17,8 @@
#import "SRWebSocket.h"
#import <Availability.h>
#if TARGET_OS_IPHONE
#define HAS_ICU
#endif
@ -110,14 +112,19 @@ static NSString *newSHA1String(const char *bytes, size_t length) {
assert(length >= 0);
assert(length <= UINT32_MAX);
CC_SHA1(bytes, (CC_LONG)length, md);
NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH];
if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
return [data base64EncodedStringWithOptions:0];
#if (__IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0) \
|| (__MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9)
if (![NSData instancesRespondToSelector:@selector(base64EncodedStringWithOptions:)]) {
return [data base64Encoding];
}
return [data base64Encoding];
#endif
return [data base64EncodedStringWithOptions:0];
}
@implementation NSData (SRWebSocket)
@ -212,19 +219,19 @@ typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data);
@implementation SRWebSocket {
NSInteger _webSocketVersion;
NSOperationQueue *_delegateOperationQueue;
dispatch_queue_t _delegateDispatchQueue;
dispatch_queue_t _workQueue;
NSMutableArray *_consumers;
NSInputStream *_inputStream;
NSOutputStream *_outputStream;
NSMutableData *_readBuffer;
NSUInteger _readBufferOffset;
NSMutableData *_outputBuffer;
NSUInteger _outputBufferOffset;
@ -233,18 +240,18 @@ typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data);
size_t _readOpCount;
uint32_t _currentStringScanPosition;
NSMutableData *_currentFrameData;
NSString *_closeReason;
NSString *_secKey;
BOOL _pinnedCertFound;
uint8_t _currentReadMaskKey[4];
size_t _currentReadMaskOffset;
BOOL _consumerStopped;
BOOL _closeWhenFinishedWriting;
BOOL _failed;
@ -252,18 +259,18 @@ typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data);
NSURLRequest *_urlRequest;
CFHTTPMessageRef _receivedHTTPHeaders;
BOOL _sentClose;
BOOL _didFail;
int _closeCode;
BOOL _isPumping;
NSMutableSet *_scheduledRunloops;
// We use this to retain ourselves.
__strong SRWebSocket *_selfRetain;
NSArray *_requestedProtocols;
SRIOConsumerPool *_consumerPool;
}
@ -287,12 +294,12 @@ static __strong NSData *CRLFCRLF;
assert(request.URL);
_url = request.URL;
_urlRequest = request;
_requestedProtocols = [protocols copy];
[self _SR_commonInit];
}
return self;
}
@ -314,39 +321,38 @@ static __strong NSData *CRLFCRLF;
- (void)_SR_commonInit;
{
NSString *scheme = _url.scheme.lowercaseString;
assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]);
if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) {
_secure = YES;
}
_readyState = SR_CONNECTING;
_consumerStopped = YES;
_webSocketVersion = 13;
_workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
// Going to set a specific on the queue so we can validate we're on the work queue
dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL);
_delegateDispatchQueue = dispatch_get_main_queue();
sr_dispatch_retain(_delegateDispatchQueue);
_readBuffer = [[NSMutableData alloc] init];
_outputBuffer = [[NSMutableData alloc] init];
_currentFrameData = [[NSMutableData alloc] init];
_consumers = [[NSMutableArray alloc] init];
_consumerPool = [[SRIOConsumerPool alloc] init];
_scheduledRunloops = [[NSMutableSet alloc] init];
[self _initializeStreams];
// default handlers
}
@ -362,15 +368,15 @@ static __strong NSData *CRLFCRLF;
[_inputStream close];
[_outputStream close];
sr_dispatch_release(_workQueue);
_workQueue = NULL;
if (_receivedHTTPHeaders) {
CFRelease(_receivedHTTPHeaders);
_receivedHTTPHeaders = NULL;
}
if (_delegateDispatchQueue) {
sr_dispatch_release(_delegateDispatchQueue);
_delegateDispatchQueue = NULL;
@ -499,17 +505,24 @@ static __strong NSData *CRLFCRLF;
{
SRFastLog(@"Connected");
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1);
// Set host first so it defaults
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host));
NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);
if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
_secKey = [keyBytes base64EncodedStringWithOptions:0];
} else {
#if (__IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0) \
|| (__MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9)
if (![NSData instancesRespondToSelector:@selector(base64EncodedStringWithOptions:)]) {
_secKey = [keyBytes base64Encoding];
} else
#endif
{
_secKey = [keyBytes base64EncodedStringWithOptions:0];
}
assert([_secKey length] == 24);

View File

@ -15,6 +15,4 @@
@property (nonatomic, assign) NSLineBreakMode lineBreakMode;
@property (nonatomic, assign) NSUInteger numberOfLines;
- (NSNumber *)reactTagAtPoint:(CGPoint)point;
@end

View File

@ -26,6 +26,9 @@
+ (float)float:(id)json;
+ (int)int:(id)json;
+ (int64_t)int64_t:(id)json;
+ (uint64_t)uint64_t:(id)json;
+ (NSInteger)NSInteger:(id)json;
+ (NSUInteger)NSUInteger:(id)json;
@ -70,6 +73,7 @@
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json size:(id)json weight:(id)json;
+ (NSArray *)NSStringArray:(id)json;
+ (NSArray *)NSURLArray:(id)json;
+ (NSArray *)NSNumberArray:(id)json;
+ (NSArray *)UIColorArray:(id)json;
+ (NSArray *)CGColorArray:(id)json;
@ -142,6 +146,14 @@ id RCTConvertValue(id target, NSString *keypath, id json);
#define RCT_CONVERTER(type, name, getter) \
RCT_CONVERTER_CUSTOM(type, name, [json getter])
/**
* This macro is similar to RCT_CONVERTER, but specifically geared towards
* numeric types. It will handle string input correctly, and provides more
* detailed error reporting if a wrong value is passed in.
*/
#define RCT_NUMBER_CONVERTER(type, getter) \
RCT_CONVERTER_CUSTOM(type, type, [[self NSNumber:json] getter])
/**
* This macro is used for creating converters for enum types.
*/
@ -177,7 +189,7 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
* This macro is used for creating converter functions for structs that consist
* of a number of CGFloat properties, such as CGPoint, CGRect, etc.
*/
#define RCT_CGSTRUCT_CONVERTER(type, values) \
#define RCT_CGSTRUCT_CONVERTER(type, values, _aliases) \
+ (type)type:(id)json \
{ \
@try { \
@ -194,12 +206,23 @@ RCT_CONVERTER_CUSTOM(type, name, [json getter])
RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json); \
} else { \
for (NSUInteger i = 0; i < count; i++) { \
((CGFloat *)&result)[i] = [json[i] doubleValue]; \
((CGFloat *)&result)[i] = [self CGFloat:json[i]]; \
} \
} \
} else if ([json isKindOfClass:[NSDictionary class]]) { \
NSDictionary *aliases = _aliases; \
if (aliases.count) { \
json = [json mutableCopy]; \
for (NSString *alias in aliases) { \
NSString *key = aliases[alias]; \
NSNumber *number = json[key]; \
if (number) { \
((NSMutableDictionary *)json)[key] = number; \
} \
} \
} \
for (NSUInteger i = 0; i < count; i++) { \
((CGFloat *)&result)[i] = [json[fields[i]] doubleValue]; \
((CGFloat *)&result)[i] = [self CGFloat:json[fields[i]]]; \
} \
} else if (json && json != [NSNull null]) { \
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \

View File

@ -13,25 +13,39 @@
#import "RCTLog.h"
CGFloat const RCTDefaultFontSize = 14;
NSString *const RCTDefaultFontName = @"HelveticaNeue";
NSString *const RCTDefaultFontWeight = @"normal";
NSString *const RCTBoldFontWeight = @"bold";
@implementation RCTConvert
RCT_CONVERTER(BOOL, BOOL, boolValue)
RCT_CONVERTER(double, double, doubleValue)
RCT_CONVERTER(float, float, floatValue)
RCT_CONVERTER(int, int, intValue)
RCT_NUMBER_CONVERTER(double, doubleValue)
RCT_NUMBER_CONVERTER(float, floatValue)
RCT_NUMBER_CONVERTER(int, intValue)
RCT_CONVERTER(NSInteger, NSInteger, integerValue)
RCT_CONVERTER_CUSTOM(NSUInteger, NSUInteger, [json unsignedIntegerValue])
RCT_NUMBER_CONVERTER(int64_t, longLongValue);
RCT_NUMBER_CONVERTER(uint64_t, unsignedLongLongValue);
RCT_NUMBER_CONVERTER(NSInteger, integerValue)
RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
RCT_CONVERTER_CUSTOM(NSArray *, NSArray, [NSArray arrayWithArray:json])
RCT_CONVERTER_CUSTOM(NSDictionary *, NSDictionary, [NSDictionary dictionaryWithDictionary:json])
RCT_CONVERTER(NSString *, NSString, description)
RCT_CONVERTER_CUSTOM(NSNumber *, NSNumber, @([json doubleValue]))
+ (NSNumber *)NSNumber:(id)json
{
if ([json isKindOfClass:[NSNumber class]]) {
return json;
} else if ([json isKindOfClass:[NSString class]]) {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
NSNumber *number = [formatter numberFromString:json];
if (!number) {
RCTLogError(@"JSON String '%@' could not be interpreted as a number", json);
}
return number;
} else if (json && json != [NSNull null]) {
RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a number", json, [json class]);
}
return nil;
}
+ (NSURL *)NSURL:(id)json
{
@ -47,7 +61,12 @@ RCT_CONVERTER_CUSTOM(NSNumber *, NSNumber, @([json doubleValue]))
}
else if ([path length])
{
return [NSURL URLWithString:path relativeToURL:[[NSBundle mainBundle] resourceURL]];
NSURL *URL = [NSURL URLWithString:path relativeToURL:[[NSBundle mainBundle] resourceURL]];
if ([URL isFileURL] &&![[NSFileManager defaultManager] fileExistsAtPath:[URL absoluteString]]) {
RCTLogWarn(@"The file '%@' does not exist", URL);
return nil;
}
return URL;
}
return nil;
}
@ -58,11 +77,11 @@ RCT_CONVERTER_CUSTOM(NSNumber *, NSNumber, @([json doubleValue]))
}
// JS Standard for time is milliseconds
RCT_CONVERTER_CUSTOM(NSDate *, NSDate, [NSDate dateWithTimeIntervalSince1970:[json doubleValue] / 1000.0])
RCT_CONVERTER_CUSTOM(NSTimeInterval, NSTimeInterval, [json doubleValue] / 1000.0)
RCT_CONVERTER_CUSTOM(NSDate *, NSDate, [NSDate dateWithTimeIntervalSince1970:[self double:json] / 1000.0])
RCT_CONVERTER_CUSTOM(NSTimeInterval, NSTimeInterval, [self double:json] / 1000.0)
// JS standard for time zones is minutes.
RCT_CONVERTER_CUSTOM(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[json doubleValue] * 60.0])
RCT_CONVERTER_CUSTOM(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0])
RCT_ENUM_CONVERTER(NSTextAlignment, (@{
@"auto": @(NSTextAlignmentNatural),
@ -90,11 +109,12 @@ RCT_ENUM_CONVERTER(UIKeyboardType, (@{
@"default": @(UIKeyboardTypeDefault),
}), UIKeyboardTypeDefault, integerValue)
RCT_CONVERTER(CGFloat, CGFloat, doubleValue)
RCT_CGSTRUCT_CONVERTER(CGPoint, (@[@"x", @"y"]))
RCT_CGSTRUCT_CONVERTER(CGSize, (@[@"w", @"h"]))
RCT_CGSTRUCT_CONVERTER(CGRect, (@[@"x", @"y", @"w", @"h"]))
RCT_CGSTRUCT_CONVERTER(UIEdgeInsets, (@[@"top", @"left", @"bottom", @"right"]))
// TODO: normalise the use of w/width so we can do away with the alias values (#6566645)
RCT_CONVERTER_CUSTOM(CGFloat, CGFloat, [self double:json])
RCT_CGSTRUCT_CONVERTER(CGPoint, (@[@"x", @"y"]), nil)
RCT_CGSTRUCT_CONVERTER(CGSize, (@[@"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
RCT_CGSTRUCT_CONVERTER(CGRect, (@[@"x", @"y", @"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
RCT_CGSTRUCT_CONVERTER(UIEdgeInsets, (@[@"top", @"left", @"bottom", @"right"]), nil)
RCT_ENUM_CONVERTER(CGLineJoin, (@{
@"miter": @(kCGLineJoinMiter),
@ -113,9 +133,9 @@ RCT_CGSTRUCT_CONVERTER(CATransform3D, (@[
@"m21", @"m22", @"m23", @"m24",
@"m31", @"m32", @"m33", @"m34",
@"m41", @"m42", @"m43", @"m44"
]))
]), nil)
RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]))
RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]), nil)
+ (UIColor *)UIColor:(id)json
{
@ -328,19 +348,19 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
} else {
// Color array
color = [UIColor colorWithRed:[json[0] doubleValue]
green:[json[1] doubleValue]
blue:[json[2] doubleValue]
alpha:[json count] > 3 ? [json[3] doubleValue] : 1];
color = [UIColor colorWithRed:[self double:json[0]]
green:[self double:json[1]]
blue:[self double:json[2]]
alpha:[json count] > 3 ? [self double:json[3]] : 1];
}
} else if ([json isKindOfClass:[NSDictionary class]]) {
// Color dictionary
color = [UIColor colorWithRed:[json[@"r"] doubleValue]
green:[json[@"g"] doubleValue]
blue:[json[@"b"] doubleValue]
alpha:[json[@"a"] ?: @1 doubleValue]];
color = [UIColor colorWithRed:[self double:json[@"r"]]
green:[self double:json[@"g"]]
blue:[self double:json[@"b"]]
alpha:[self double:json[@"a"] ?: @1]];
} else if (json && ![json isKindOfClass:[NSNull class]]) {
@ -415,6 +435,11 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family size:(id)size weight:(id)weight
{
CGFloat const RCTDefaultFontSize = 14;
NSString *const RCTDefaultFontName = @"HelveticaNeue";
NSString *const RCTDefaultFontWeight = @"normal";
NSString *const RCTBoldFontWeight = @"bold";
// Create descriptor
UIFontDescriptor *fontDescriptor = font.fontDescriptor ?: [UIFontDescriptor fontDescriptorWithName:RCTDefaultFontName size:RCTDefaultFontSize];
@ -427,7 +452,7 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
// Get font family
NSString *familyName = [self NSString:family];
if (familyName) {
if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
font = [UIFont fontWithName:familyName size:fontDescriptor.pointSize];
if (font) {
// It's actually a font name, not a font family name,
@ -437,11 +462,14 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
} else {
// Not a valid font or family
RCTLogError(@"Unrecognized font family '%@'", familyName);
familyName = [UIFont fontWithDescriptor:fontDescriptor size:0].familyName;
}
} else {
// Set font family
fontDescriptor = [fontDescriptor fontDescriptorWithFamily:familyName];
}
} else {
familyName = [UIFont fontWithDescriptor:fontDescriptor size:0].familyName;
}
// Get font weight
@ -451,29 +479,43 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
static NSSet *values;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
values = [NSSet setWithObjects:@"bold", @"normal", nil];
values = [NSSet setWithObjects:RCTDefaultFontWeight, RCTBoldFontWeight, nil];
});
if (fontWeight && ![values containsObject:fontWeight]) {
RCTLogError(@"Unrecognized font weight '%@', must be one of %@", fontWeight, values);
fontWeight = RCTDefaultFontWeight;
}
UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;
// this is hacky. we are appending the string -Medium because most fonts we currently use
// just need to have -Medium appended to get the bold we want. we're going to revamp this
// to make it easier to know which options are available in JS. t4996115
if ([fontWeight isEqualToString:RCTBoldFontWeight]) {
symbolicTraits |= UIFontDescriptorTraitBold;
} else {
symbolicTraits &= ~UIFontDescriptorTraitBold;
font = nil;
for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName]) {
if ([fontName hasSuffix:@"-Medium"]) {
font = [UIFont fontWithName:fontName size:fontDescriptor.pointSize];
break;
}
if ([fontName hasSuffix:@"-Bold"]) {
font = [UIFont fontWithName:fontName size:fontDescriptor.pointSize];
// But keep searching in case there's a medium option
}
}
if (font) {
fontDescriptor = font.fontDescriptor;
}
}
fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:symbolicTraits];
}
// TODO: font style
// Create font
return [UIFont fontWithDescriptor:fontDescriptor size:fontDescriptor.pointSize];
return [UIFont fontWithDescriptor:fontDescriptor size:0];
}
RCT_ARRAY_CONVERTER(NSString)
RCT_ARRAY_CONVERTER(NSURL)
RCT_ARRAY_CONVERTER(NSNumber)
RCT_ARRAY_CONVERTER(UIColor)
@ -729,6 +771,9 @@ static id RCTConvertValueWithExplicitEncoding(id target, NSString *key, id json,
@"extAlignment": ^(id val) {
return [RCTConvert NSTextAlignment:val];
},
@"ritingDirection": ^(id val) {
return [RCTConvert NSWritingDirection:val];
},
@"Cap": ^(id val) {
return [RCTConvert CGLineCap:val];
},

View File

@ -29,6 +29,7 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification";
RCTBridge *_bridge;
RCTTouchHandler *_touchHandler;
id<RCTJavaScriptExecutor> _executor;
BOOL _registered;
}
static Class _globalExecutorClass;
@ -36,7 +37,7 @@ static Class _globalExecutorClass;
+ (void)initialize
{
#if DEBUG
#if TARGET_IPHONE_SIMULATOR
// Register Cmd-R as a global refresh key
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"r"
@ -144,6 +145,7 @@ static Class _globalExecutorClass;
} else {
[_bridge.uiManager registerRootView:self];
_registered = YES;
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
@ -163,6 +165,14 @@ static Class _globalExecutorClass;
return;
}
// Clean up
[self removeGestureRecognizer:_touchHandler];
[_touchHandler invalidate];
[_executor invalidate];
[_bridge invalidate];
_registered = NO;
// Choose local executor if specified, followed by global, followed by default
_executor = [[_executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class] alloc] init];
_bridge = [[RCTBridge alloc] initWithExecutor:_executor moduleProvider:_moduleProvider];
@ -231,10 +241,10 @@ static Class _globalExecutorClass;
sourceCodeModule.scriptURL = _scriptURL;
sourceCodeModule.scriptText = rawText;
[_bridge enqueueApplicationScript:rawText url:_scriptURL onComplete:^(NSError *error) {
[_bridge enqueueApplicationScript:rawText url:_scriptURL onComplete:^(NSError *_error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (_bridge.isValid) {
[self bundleFinishedLoading:error];
[self bundleFinishedLoading:_error];
}
});
}];
@ -254,9 +264,12 @@ static Class _globalExecutorClass;
[self loadBundle];
}
- (BOOL)isReactRootView
- (void)layoutSubviews
{
return YES;
[super layoutSubviews];
if (_registered) {
[_bridge.uiManager setFrame:self.frame forRootView:self];
}
}
- (void)reload

View File

@ -129,13 +129,15 @@ typedef NS_ENUM(NSInteger, RCTTouchEventType) {
// Find closest React-managed touchable view
UIView *targetView = touch.view;
while (targetView) {
if (targetView.reactTag && targetView.userInteractionEnabled) { // TODO: implement respondsToTouch: mechanism
if (targetView.reactTag && targetView.userInteractionEnabled &&
[targetView reactRespondsToTouch:touch]) {
break;
}
targetView = targetView.superview;
}
if (!targetView.reactTag || !targetView.userInteractionEnabled) {
NSNumber *reactTag = [targetView reactTagAtPoint:[touch locationInView:targetView]];
if (!reactTag || !targetView.userInteractionEnabled) {
return;
}
@ -155,7 +157,7 @@ typedef NS_ENUM(NSInteger, RCTTouchEventType) {
// Create touch
NSMutableDictionary *reactTouch = [[NSMutableDictionary alloc] initWithCapacity:9];
reactTouch[@"target"] = [targetView reactTagAtPoint:[touch locationInView:targetView]];
reactTouch[@"target"] = reactTag;
reactTouch[@"identifier"] = @(touchID);
reactTouch[@"touches"] = [NSNull null]; // We hijack this touchObj to serve both as an event
reactTouch[@"changedTouches"] = [NSNull null]; // and as a Touch object, so making this JIT friendly.
@ -248,15 +250,15 @@ typedef NS_ENUM(NSInteger, RCTTouchEventType) {
if (_recordingInteractionTiming) {
[_bridgeInteractionTiming addObject:@{
@"timeSeconds": @(touch.originatingTime),
@"operation": @"taskOriginated",
@"taskID": @(touch.id),
}];
@"timeSeconds": @(touch.originatingTime),
@"operation": @"taskOriginated",
@"taskID": @(touch.id),
}];
[_bridgeInteractionTiming addObject:@{
@"timeSeconds": @(enqueueTime),
@"operation": @"taskEnqueuedPending",
@"taskID": @(touch.id),
}];
@"timeSeconds": @(enqueueTime),
@"operation": @"taskEnqueuedPending",
@"taskID": @(touch.id),
}];
}
}
@ -273,18 +275,18 @@ typedef NS_ENUM(NSInteger, RCTTouchEventType) {
if (_recordingInteractionTiming) {
for (RCTTouchEvent *touch in _pendingTouches) {
[_bridgeInteractionTiming addObject:@{
@"timeSeconds": @(sender.timestamp),
@"operation": @"frameAlignedDispatch",
@"taskID": @(touch.id),
}];
@"timeSeconds": @(sender.timestamp),
@"operation": @"frameAlignedDispatch",
@"taskID": @(touch.id),
}];
}
if (pendingCount > 0 || sender.timestamp - _mostRecentEnqueueJS < 0.1) {
[_bridgeInteractionTiming addObject:@{
@"timeSeconds": @(sender.timestamp),
@"operation": @"mainThreadDisplayLink",
@"taskID": @([RCTTouchEvent newID]),
}];
@"timeSeconds": @(sender.timestamp),
@"operation": @"mainThreadDisplayLink",
@"taskID": @([RCTTouchEvent newID]),
}];
}
}

View File

@ -129,7 +129,8 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
return [self initWithJavaScriptThread:javaScriptThread globalContextRef:NULL];
}
- (instancetype)initWithJavaScriptThread:(NSThread *)javaScriptThread globalContextRef:(JSGlobalContextRef)context
- (instancetype)initWithJavaScriptThread:(NSThread *)javaScriptThread
globalContextRef:(JSGlobalContextRef)context
{
if ((self = [super init])) {
_javaScriptThread = javaScriptThread;

View File

@ -184,7 +184,8 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
asGlobalObjectNamed:(NSString *)objectName
callback:(RCTJavaScriptCompleteBlock)onComplete
{
RCTAssert(!_objectsToInject[objectName], @"already injected object named %@", _objectsToInject[objectName]);
RCTAssert(!_objectsToInject[objectName],
@"already injected object named %@", _objectsToInject[objectName]);
_objectsToInject[objectName] = script;
onComplete(nil);
}

View File

@ -14,8 +14,6 @@
#import "RCTInvalidating.h"
#import "RCTViewManager.h"
@class RCTRootView;
@protocol RCTScrollableProtocol;
/**
@ -33,10 +31,15 @@
/**
* Register a root view with the RCTUIManager. Theoretically, a single manager
* can support multiple root views, however this feature is not currently exposed
* and may eventually be removed.
* can support multiple root views, however this feature is not currently exposed.
*/
- (void)registerRootView:(RCTRootView *)rootView;
- (void)registerRootView:(UIView *)rootView;
/**
* Update the frame of a root view. This might be in response to a screen rotation
* or some other layout event outsde of the React-managed view hierarchy.
*/
- (void)setFrame:(CGRect)frame forRootView:(UIView *)rootView;
/**
* Schedule a block to be executed on the UI thread. Useful if you need to execute

View File

@ -19,7 +19,6 @@
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#import "RCTNavigator.h"
#import "RCTRootView.h"
#import "RCTScrollableProtocol.h"
#import "RCTShadowView.h"
@ -55,7 +54,7 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
@implementation RCTAnimation
UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType type)
static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType type)
{
switch (type) {
case RCTAnimationTypeLinear:
@ -67,7 +66,7 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
case RCTAnimationTypeEaseInEaseOut:
return UIViewAnimationCurveEaseInOut;
default:
RCTCAssert(NO, @"Unsupported animation type %zd", type);
RCTLogError(@"Unsupported animation type %zd", type);
return UIViewAnimationCurveEaseInOut;
}
}
@ -157,6 +156,15 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
@end
@interface RCTUIManager ()
// NOTE: these are properties so that they can be accessed by unit tests
@property (nonatomic, strong) RCTSparseArray *viewManagerRegistry; // RCT thread only
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry; // RCT thread only
@property (nonatomic, strong) RCTSparseArray *viewRegistry; // Main thread only
@end
@implementation RCTUIManager
{
dispatch_queue_t _shadowQueue;
@ -174,11 +182,6 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
NSMutableDictionary *_defaultShadowViews; // RCT thread only
NSMutableDictionary *_defaultViews; // Main thread only
NSDictionary *_viewManagers;
// Keyed by React tag
RCTSparseArray *_viewManagerRegistry; // RCT thread only
RCTSparseArray *_shadowViewRegistry; // RCT thread only
RCTSparseArray *_viewRegistry; // Main thread only
}
@synthesize bridge =_bridge;
@ -277,11 +280,14 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
[_pendingUIBlocksLock unlock];
}
- (void)registerRootView:(RCTRootView *)rootView;
- (void)registerRootView:(UIView *)rootView;
{
RCTAssertMainThread();
NSNumber *reactTag = rootView.reactTag;
RCTAssert(RCTIsReactRootView(reactTag),
@"View %@ with tag #%@ is not a root view", rootView, reactTag);
UIView *existingView = _viewRegistry[reactTag];
RCTCAssert(existingView == nil || existingView == rootView,
@"Expect all root views to have unique tag. Added %@ twice", reactTag);
@ -291,7 +297,7 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
CGRect frame = rootView.frame;
// Register manager (TODO: should we do this, or leave it nil?)
_viewManagerRegistry[reactTag] = _viewManagers[@"View"];
_viewManagerRegistry[reactTag] = _viewManagers[@"RCTView"];
// Register shadow view
dispatch_async(_shadowQueue, ^{
@ -300,13 +306,31 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
shadowView.reactTag = reactTag;
shadowView.frame = frame;
shadowView.backgroundColor = [UIColor whiteColor];
shadowView.reactRootView = YES; // can this just be inferred from the fact that it has no superview?
_shadowViewRegistry[shadowView.reactTag] = shadowView;
[_rootViewTags addObject:reactTag];
});
}
- (void)setFrame:(CGRect)frame forRootView:(UIView *)rootView
{
RCTAssertMainThread();
NSNumber *reactTag = rootView.reactTag;
RCTAssert(RCTIsReactRootView(reactTag), @"Specified view %@ is not a root view", reactTag);
dispatch_async(_bridge.shadowQueue, ^{
RCTShadowView *rootShadowView = _shadowViewRegistry[reactTag];
RCTAssert(rootShadowView != nil, @"Could not locate root view with tag %@", reactTag);
rootShadowView.frame = frame;
[rootShadowView updateLayout];
RCTViewManagerUIBlock uiBlock = [self uiBlockWithLayoutUpdateForRootView:rootShadowView];
[self addUIBlock:uiBlock];
[self flushUIBlocks];
});
}
/**
* Unregisters views from registries
*/
@ -389,21 +413,6 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
UIView *view = viewRegistry[reactTag];
CGRect frame = [frames[ii] CGRectValue];
// These frames are in terms of anchorPoint = topLeft, but internally the
// views are anchorPoint = center for easier scale and rotation animations.
// Convert the frame so it works with anchorPoint = center.
CGPoint position = {CGRectGetMidX(frame), CGRectGetMidY(frame)};
CGRect bounds = {0, 0, frame.size};
// Avoid crashes due to nan coords
if (isnan(position.x) || isnan(position.y) ||
isnan(bounds.origin.x) || isnan(bounds.origin.y) ||
isnan(bounds.size.width) || isnan(bounds.size.height)) {
RCTLogError(@"Invalid layout for (%@)%@. position: %@. bounds: %@",
[view reactTag], self, NSStringFromCGPoint(position), NSStringFromCGRect(bounds));
continue;
}
void (^completion)(BOOL finished) = ^(BOOL finished) {
if (self->_layoutAnimation.callback) {
self->_layoutAnimation.callback(@[@(finished)]);
@ -415,21 +424,22 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
RCTAnimation *updateAnimation = isNew ? nil: _layoutAnimation.updateAnimation;
if (updateAnimation) {
[updateAnimation performAnimations:^{
view.layer.position = position;
view.layer.bounds = bounds;
[view reactSetFrame:frame];
for (RCTViewManagerUIBlock block in updateBlocks) {
block(self, _viewRegistry);
}
} withCompletionBlock:completion];
} else {
view.layer.position = position;
view.layer.bounds = bounds;
[view reactSetFrame:frame];
for (RCTViewManagerUIBlock block in updateBlocks) {
block(self, _viewRegistry);
}
completion(YES);
}
// TODO: deprecate this
[view reactSetBorders];
// Animate view creation
BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue];
RCTAnimation *createAnimation = _layoutAnimation.createAnimation;
@ -461,7 +471,7 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
* TODO: this is quite inefficient. If this was handled via the
* ViewManager instead, it could be done more efficiently.
*/
RCTRootView *rootView = _viewRegistry[rootViewTag];
UIView *rootView = _viewRegistry[rootViewTag];
RCTTraverseViewNodes(rootView, ^(id<RCTViewNodeProtocol> view) {
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
[view reactBridgeDidFinishTransaction];
@ -549,13 +559,15 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
RCTShadowView *rootShadowView = _shadowViewRegistry[rootReactTag];
RCTAssert(rootShadowView.superview == nil, @"root view cannot have superview (ID %@)", rootReactTag);
[self _purgeChildren:@[rootShadowView] fromRegistry:_shadowViewRegistry];
[self _purgeChildren:rootShadowView.reactSubviews fromRegistry:_shadowViewRegistry];
_shadowViewRegistry[rootReactTag] = nil;
[_rootViewTags removeObject:rootReactTag];
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
RCTCAssertMainThread();
UIView *rootView = viewRegistry[rootReactTag];
[uiManager _purgeChildren:@[rootView] fromRegistry:viewRegistry];
[uiManager _purgeChildren:rootView.reactSubviews fromRegistry:viewRegistry];
viewRegistry[rootReactTag] = nil;
}];
}
@ -701,7 +713,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
}];
// Update layout
[shadowView updateShadowViewLayout];
[shadowView updateLayout];
}
- (void)createAndRegisterViewWithReactTag:(NSNumber *)reactTag
@ -781,7 +793,9 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
if (!reactTag) return;
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
UIView *newResponder = viewRegistry[reactTag];
[newResponder reactWillMakeFirstResponder];
[newResponder becomeFirstResponder];
[newResponder reactDidMakeFirstResponder];
}];
}
@ -830,6 +844,13 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
_nextLayoutAnimation = nil;
}
[self flushUIBlocks];
}
- (void)flushUIBlocks
{
RCTAssert(![NSThread isMainThread], @"Should be called on shadow thread");
// First copy the previous blocks into a temporary variable, then reset the
// pending blocks to a new array. This guards against mutation while
// processing the pending blocks in another thread.
@ -858,7 +879,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
UIView *view = viewRegistry[reactTag];
if (!view) {
RCTLogError(@"measure cannot find view with tag %zd", reactTag);
RCTLogError(@"measure cannot find view with tag %@", reactTag);
return;
}
CGRect frame = view.frame;
@ -868,7 +889,8 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
rootView = rootView.superview;
}
RCTCAssert([rootView isReactRootView], @"React view not inside RCTRootView");
// TODO: this doesn't work because sometimes view is inside a modal window
// RCTCAssert([rootView isReactRootView], @"React view is not inside a react root view");
// By convention, all coordinates, whether they be touch coordinates, or
// measurement coordinates are with respect to the root view.
@ -885,16 +907,9 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
}];
}
/**
* TODO: This could be modified to accept any `RCTViewNodeProtocol`, if
* appropriate changes were made to that protocol to support `superview`
* traversal - which is possibly more difficult than it sounds since a
* `superview` is not a "react superview".
*/
+ (void)measureLayoutOnNodes:(RCTShadowView *)view
ancestor:(RCTShadowView *)ancestor
errorCallback:(RCTResponseSenderBlock)errorCallback
callback:(__unused RCTResponseSenderBlock)callback
static void RCTMeasureLayout(RCTShadowView *view,
RCTShadowView *ancestor,
RCTResponseSenderBlock callback)
{
if (!view) {
RCTLogError(@"Attempting to measure view that does not exist");
@ -904,7 +919,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
RCTLogError(@"Attempting to measure relative to ancestor that does not exist");
return;
}
CGRect result = [RCTShadowView measureLayout:view relativeTo:ancestor];
CGRect result = [view measureLayoutRelativeToAncestor:ancestor];
if (CGRectIsNull(result)) {
RCTLogError(@"view %@ (tag #%@) is not a decendant of %@ (tag #%@)",
view, view.reactTag, ancestor, ancestor.reactTag);
@ -918,7 +933,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
RCTLogError(@"Attempted to measure layout but offset or dimensions were NaN");
return;
}
callback(@[@(topOffset), @(leftOffset), @(width), @(height)]);
callback(@[@(leftOffset), @(topOffset), @(width), @(height)]);
}
/**
@ -937,7 +952,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
RCTShadowView *ancestorShadowView = _shadowViewRegistry[ancestorReactTag];
[RCTUIManager measureLayoutOnNodes:shadowView ancestor:ancestorShadowView errorCallback:errorCallback callback:callback];
RCTMeasureLayout(shadowView, ancestorShadowView, callback);
}
/**
@ -954,7 +969,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
RCT_EXPORT();
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
[RCTUIManager measureLayoutOnNodes:shadowView ancestor:[shadowView superview] errorCallback:errorCallback callback:callback];
RCTMeasureLayout(shadowView, shadowView.reactSuperview, callback);
}
/**
@ -981,7 +996,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
CGRect layoutRect = [RCTConvert CGRect:rect];
[childShadowViews enumerateObjectsUsingBlock:^(RCTShadowView *childShadowView, NSUInteger idx, BOOL *stop) {
CGRect childLayout = [RCTShadowView measureLayout:childShadowView relativeTo:shadowView];
CGRect childLayout = [childShadowView measureLayoutRelativeToAncestor:shadowView];
if (CGRectIsNull(childLayout)) {
RCTLogError(@"View %@ (tag #%@) is not a decendant of %@ (tag #%@)",
childShadowView, childShadowView.reactTag, shadowView, shadowView.reactTag);
@ -1183,9 +1198,9 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
},
} mutableCopy];
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
if (RCTClassOverridesClassMethod(cls, @selector(customBubblingEventTypes))) {
NSDictionary *eventTypes = [cls customBubblingEventTypes];
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTViewManager *manager, BOOL *stop) {
if (RCTClassOverridesInstanceMethod([manager class], @selector(customBubblingEventTypes))) {
NSDictionary *eventTypes = [manager customBubblingEventTypes];
for (NSString *eventName in eventTypes) {
RCTCAssert(!customBubblingEventTypesConfigs[eventName],
@"Event '%@' registered multiple times.", eventName);
@ -1235,9 +1250,9 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
},
} mutableCopy];
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
if (RCTClassOverridesClassMethod(cls, @selector(customDirectEventTypes))) {
NSDictionary *eventTypes = [cls customDirectEventTypes];
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTViewManager *manager, BOOL *stop) {
if (RCTClassOverridesInstanceMethod([manager class], @selector(customDirectEventTypes))) {
NSDictionary *eventTypes = [manager customDirectEventTypes];
for (NSString *eventName in eventTypes) {
RCTCAssert(!customDirectEventTypes[eventName], @"Event '%@' registered multiple times.", eventName);
}
@ -1266,7 +1281,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
},
@"modalFullscreenView": @{
@"width": @(RCTScreenSize().width),
@"height": @(RCTScreenSize().width),
@"height": @(RCTScreenSize().height),
},
},
@"StyleConstants": @{
@ -1312,10 +1327,10 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
},
} mutableCopy];
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, RCTViewManager *manager, BOOL *stop) {
// TODO: should these be inherited?
NSDictionary *constants = RCTClassOverridesClassMethod(cls, @selector(constantsToExport)) ? [cls constantsToExport] : nil;
if ([constants count]) {
NSDictionary *constants = RCTClassOverridesInstanceMethod([manager class], @selector(constantsToExport)) ? [manager constantsToExport] : nil;
if (constants.count) {
NSMutableDictionary *constantsNamespace = [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]];
RCTAssert(constantsNamespace[@"Constants"] == nil , @"Cannot redefine Constants in namespace: %@", name);
// add an additional 'Constants' namespace for each class
@ -1350,12 +1365,18 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
NSSet *rootViewTags = [_rootViewTags copy];
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
for (NSNumber *reactTag in rootViewTags) {
RCTRootView *rootView = viewRegistry[reactTag];
[rootView startOrResetInteractionTiming];
id rootView = viewRegistry[reactTag];
if ([rootView respondsToSelector:@selector(startOrResetInteractionTiming)]) {
[rootView startOrResetInteractionTiming];
}
}
}];
}
// TODO: remove horrible hack - this is only here so that
// [rootView endAndResetInteractionTiming] below doesn't raise warnings
- (NSDictionary *)endAndResetInteractionTiming { return nil; }
- (void)endAndResetInteractionTiming:(RCTResponseSenderBlock)onSuccess
onError:(RCTResponseSenderBlock)onError
{
@ -1365,8 +1386,8 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
NSMutableDictionary *timingData = [[NSMutableDictionary alloc] init];
for (NSNumber *reactTag in rootViewTags) {
RCTRootView *rootView = viewRegistry[reactTag];
if (rootView) {
id rootView = viewRegistry[reactTag];
if ([rootView respondsToSelector:@selector(endAndResetInteractionTiming)]) {
timingData[reactTag.stringValue] = [rootView endAndResetInteractionTiming];
}
}

View File

@ -50,7 +50,6 @@
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; };
830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 830BA4541A8E3BDA00D53203 /* RCTCache.m */; };
832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; };
83C911101AAE6521001323A3 /* RCTAnimationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 83C9110F1AAE6521001323A3 /* RCTAnimationManager.m */; };
83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA4B1A601E3B00E9B192 /* RCTAssert.m */; };
83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA4E1A601E3B00E9B192 /* RCTLog.m */; };
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA501A601E3B00E9B192 /* RCTUtils.m */; };
@ -169,8 +168,6 @@
830BA4541A8E3BDA00D53203 /* RCTCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCache.m; sourceTree = "<group>"; };
83BEE46C1A6D19BC00B5863B /* RCTSparseArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSparseArray.h; sourceTree = "<group>"; };
83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSparseArray.m; sourceTree = "<group>"; };
83C9110E1AAE6521001323A3 /* RCTAnimationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationManager.h; sourceTree = "<group>"; };
83C9110F1AAE6521001323A3 /* RCTAnimationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationManager.m; sourceTree = "<group>"; };
83CBBA2E1A601D0E00E9B192 /* libReactKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReactKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
83CBBA4A1A601E3B00E9B192 /* RCTAssert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssert.h; sourceTree = "<group>"; };
83CBBA4B1A601E3B00E9B192 /* RCTAssert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssert.m; sourceTree = "<group>"; };
@ -230,8 +227,6 @@
1372B7091AB030C200659ED6 /* RCTAppState.m */,
13B07FE71A69327A00A75B9A /* RCTAlertManager.h */,
13B07FE81A69327A00A75B9A /* RCTAlertManager.m */,
83C9110E1AAE6521001323A3 /* RCTAnimationManager.h */,
83C9110F1AAE6521001323A3 /* RCTAnimationManager.m */,
58114A4F1AAE93D500E7D092 /* RCTAsyncLocalStorage.h */,
58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */,
13B07FE91A69327A00A75B9A /* RCTExceptionsManager.h */,
@ -489,7 +484,6 @@
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */,
83C911101AAE6521001323A3 /* RCTAnimationManager.m in Sources */,
13C156051AB1A2840079392D /* RCTWebView.m in Sources */,
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */,
13C156061AB1A2840079392D /* RCTWebViewManager.m in Sources */,

View File

@ -44,6 +44,8 @@ RCT_REMAP_VIEW_PROPERTY(timeZoneOffsetInMinutes, timeZone)
- (NSDictionary *)constantsToExport
{
UIDatePicker *dp = [[UIDatePicker alloc] init];
[dp layoutIfNeeded];
return @{
@"ComponentHeight": @(CGRectGetHeight(dp.frame)),
@"ComponentWidth": @(CGRectGetWidth(dp.frame)),

View File

@ -487,16 +487,14 @@ NSInteger kNeverProgressed = -10000;
// We can actually recover from this situation, but it would be nice to know
// when this error happens. This simply means that JS hasn't caught up to a
// back navigation before progressing. It's likely a bug in the JS code that
// catches up/schedules navigations. Eventually, let's recover from this
// error state, but in the mean time, let's get notified about any JS bugs.
// catches up/schedules navigations.
if (!(jsGettingAhead ||
jsCatchingUp ||
jsMakingNoProgressButNeedsToCatchUp ||
jsMakingNoProgressAndDoesntNeedTo)) {
RCTLogError(@"JS has only made partial progress to catch up to UIKit");
}
RCTAssert(
jsGettingAhead ||
jsCatchingUp ||
jsMakingNoProgressButNeedsToCatchUp ||
jsMakingNoProgressAndDoesntNeedTo,
@"JS has only made partial progress to catch up to UIKit"
);
NSAssert(
currentReactCount <= _currentViews.count,
@"Cannot adjust current top of stack beyond available views"
);
@ -504,7 +502,7 @@ NSInteger kNeverProgressed = -10000;
// Views before the previous react count must not have changed. Views greater than previousReactCount
// up to currentReactCount may have changed.
for (NSInteger i = 0; i < MIN(_currentViews.count, MIN(_previousViews.count, previousReactCount)); i++) {
NSAssert(_currentViews[i] == _previousViews[i], @"current view should equal previous view");
RCTAssert(_currentViews[i] == _previousViews[i], @"current view should equal previous view");
}
RCTAssert(currentReactCount >= 1, @"should be at least one current view");
if (jsGettingAhead) {
@ -545,7 +543,7 @@ didMoveToNavigationController:(UINavigationController *)navigationController
RCTAssert(
(navigationController == nil || [_navigationController.viewControllers containsObject:wrapperViewController]),
@"if navigation controller is not nil, it should container the wrapper view controller"
@"if navigation controller is not nil, it should contain the wrapper view controller"
);
RCTAssert(_navigationController.navigationLock == RCTNavigationLockJavaScript ||
_numberOfViewControllerMovesToIgnore == 0,

View File

@ -49,11 +49,6 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
*/
@property (nonatomic, assign, getter=isNewView) BOOL newView;
/**
* Is this the shadowView for an RCTRootView
*/
@property (nonatomic, assign, getter=isReactRootView) BOOL reactRootView;
/**
* Position and dimensions.
* Defaults to { 0, 0, NAN, NAN }.
@ -149,7 +144,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
/**
* Triggers a recalculation of the shadow view's layout.
*/
- (void)updateShadowViewLayout;
- (void)updateLayout;
/**
* Computes the recursive offset, meaning the sum of all descendant offsets -
@ -157,10 +152,8 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
* sum of `top`/`left`s, as this function uses the *actual* positions of
* children, not the style specified positions - it computes this based on the
* resulting layout. It does not yet compensate for native scroll view insets or
* transforms or anchor points. Returns an array containing the `x, y, width,
* height` of the shadow view relative to the ancestor, or `nil` if the `view`
* is not a descendent of `ancestor`.
* transforms or anchor points.
*/
+ (CGRect)measureLayout:(RCTShadowView *)view relativeTo:(RCTShadowView *)ancestor;
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor;
@end

View File

@ -17,8 +17,6 @@
typedef void (^RCTActionBlock)(RCTShadowView *shadowViewSelf, id value);
typedef void (^RCTResetActionBlock)(RCTShadowView *shadowViewSelf);
#define MAX_TREE_DEPTH 30
const NSString *const RCTBackgroundColorProp = @"backgroundColor";
typedef enum {
@ -213,17 +211,18 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
[self applyLayoutNode:_cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero];
}
+ (CGRect)measureLayout:(RCTShadowView *)shadowView relativeTo:(RCTShadowView *)ancestor
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
{
CGFloat totalOffsetTop = 0.0;
CGFloat totalOffsetLeft = 0.0;
CGSize size = shadowView.frame.size;
NSInteger depth = 0;
while (depth < MAX_TREE_DEPTH && shadowView && shadowView != ancestor) {
CGSize size = self.frame.size;
NSInteger depth = 30; // max depth to search
RCTShadowView *shadowView = self;
while (depth && shadowView && shadowView != ancestor) {
totalOffsetTop += shadowView.frame.origin.y;
totalOffsetLeft += shadowView.frame.origin.x;
shadowView = shadowView->_superview;
depth++;
depth--;
}
if (ancestor != shadowView) {
return CGRectNull;
@ -259,6 +258,11 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
return self;
}
- (BOOL)isReactRootView
{
return RCTIsReactRootView(self.reactTag);
}
- (void)dealloc
{
free_css_node(_cssNode);
@ -514,7 +518,7 @@ RCT_STYLE_PROPERTY(FlexWrap, flexWrap, flex_wrap, css_wrap_type_t)
[self dirtyPropagation];
}
- (void)updateShadowViewLayout
- (void)updateLayout
{
if (_recomputePadding) {
RCTProcessMetaProps(_paddingMetaProps, _cssNode->style.padding);

View File

@ -73,7 +73,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v
* Note that this method is not inherited when you subclass a view module, and
* you should not call [super customBubblingEventTypes] when overriding it.
*/
+ (NSDictionary *)customBubblingEventTypes;
- (NSDictionary *)customBubblingEventTypes;
/**
* Returns a dictionary of config data passed to JS that defines eligible events
@ -89,7 +89,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v
* Note that this method is not inherited when you subclass a view module, and
* you should not call [super customDirectEventTypes] when overriding it.
*/
+ (NSDictionary *)customDirectEventTypes;
- (NSDictionary *)customDirectEventTypes;
/**
* Called to notify manager that layout has finished, in case any calculated

View File

@ -44,12 +44,12 @@
return [[RCTShadowView alloc] init];
}
+ (NSDictionary *)customBubblingEventTypes
- (NSDictionary *)customBubblingEventTypes
{
return nil;
}
+ (NSDictionary *)customDirectEventTypes
- (NSDictionary *)customDirectEventTypes
{
return nil;
}
@ -101,7 +101,7 @@ RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTView)
return;
}
switch ([RCTConvert NSInteger:json]) {
switch ([RCTConvert RCTPointerEvents:json]) {
case RCTPointerEventsUnspecified:
// Pointer events "unspecified" acts as if a stylesheet had not specified,
// which is different than "auto" in CSS (which cannot and will not be
@ -116,6 +116,12 @@ RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTView)
RCTLogError(@"UIView base class does not support pointerEvent value: %@", json);
}
}
RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, RCTView)
{
if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) {
view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
}
}
#pragma mark - ShadowView properties
@ -149,4 +155,15 @@ RCT_CUSTOM_SHADOW_PROPERTY(position, RCTShadowView)
view.positionType = json ? [RCTConvert css_position_type_t:json] : defaultView.positionType;
}
// Border properties - to be deprecated
RCT_REMAP_VIEW_PROPERTY(borderTopWidth, reactBorderTop.width);
RCT_REMAP_VIEW_PROPERTY(borderRightWidth, reactBorderRight.width);
RCT_REMAP_VIEW_PROPERTY(borderBottomWidth, reactBorderBottom.width);
RCT_REMAP_VIEW_PROPERTY(borderLeftWidth, reactBorderLeft.width);
RCT_REMAP_VIEW_PROPERTY(borderTopColor, reactBorderTop.color);
RCT_REMAP_VIEW_PROPERTY(borderRightColor, reactBorderRight.color);
RCT_REMAP_VIEW_PROPERTY(borderBottomColor, reactBorderBottom.color);
RCT_REMAP_VIEW_PROPERTY(borderLeftColor, reactBorderLeft.color);
@end

View File

@ -22,7 +22,7 @@
- (id<RCTViewNodeProtocol>)reactSuperview;
- (NSNumber *)reactTagAtPoint:(CGPoint)point;
// View is an RCTRootView
// View/ShadowView is a root view
- (BOOL)isReactRootView;
@optional

View File

@ -15,6 +15,12 @@
@interface UIView (ReactKit) <RCTViewNodeProtocol>
/**
* Used by the UIIManager to set the view frame.
* May be overriden to disable animation, etc.
*/
- (void)reactSetFrame:(CGRect)frame;
/**
* This method finds and returns the containing view controller for the view.
*/
@ -28,4 +34,20 @@
*/
- (void)addControllerToClosestParent:(UIViewController *)controller;
/**
* Responder overrides - to be deprecated.
*/
- (void)reactWillMakeFirstResponder;
- (void)reactDidMakeFirstResponder;
- (BOOL)reactRespondsToTouch:(UITouch *)touch;
@end
@interface UIView (ReactKitBorders)
/**
* Borders stuff - pay no attention to this, it's going away (#6548297)
*/
- (void)reactSetBorders;
@end

View File

@ -12,6 +12,7 @@
#import <objc/runtime.h>
#import "RCTAssert.h"
#import "RCTLog.h"
#import "RCTWrapperViewController.h"
@implementation UIView (ReactKit)
@ -61,6 +62,27 @@
return self.superview;
}
- (void)reactSetFrame:(CGRect)frame
{
// These frames are in terms of anchorPoint = topLeft, but internally the
// views are anchorPoint = center for easier scale and rotation animations.
// Convert the frame so it works with anchorPoint = center.
CGPoint position = {CGRectGetMidX(frame), CGRectGetMidY(frame)};
CGRect bounds = {CGPointZero, frame.size};
// Avoid crashes due to nan coords
if (isnan(position.x) || isnan(position.y) ||
isnan(bounds.origin.x) || isnan(bounds.origin.y) ||
isnan(bounds.size.width) || isnan(bounds.size.height)) {
RCTLogError(@"Invalid layout for (%@)%@. position: %@. bounds: %@",
self.reactTag, self, NSStringFromCGPoint(position), NSStringFromCGRect(bounds));
return;
}
self.layer.position = position;
self.layer.bounds = bounds;
}
- (UIViewController *)backingViewController
{
id responder = [self nextResponder];
@ -86,4 +108,167 @@
}
}
/**
* Responder overrides - to be deprecated.
*/
- (void)reactWillMakeFirstResponder {};
- (void)reactDidMakeFirstResponder {};
- (BOOL)reactRespondsToTouch:(UITouch *)touch
{
return YES;
}
@end
#pragma mark - Borders
// Note: the value of this enum determines their relative zPosition
typedef NS_ENUM(NSUInteger, RCTBorderSide) {
RCTBorderSideTop = 0,
RCTBorderSideRight = 1,
RCTBorderSideBottom = 2,
RCTBorderSideLeft = 3
};
@interface RCTSingleSidedBorder : NSObject
@property (nonatomic, readwrite, assign) CGFloat width;
@property (nonatomic, readwrite, strong) UIColor *color;
@property (nonatomic, readonly, assign) RCTBorderSide side;
- (instancetype)initWithSide:(RCTBorderSide)side superlayer:(CALayer *)superlayer;
- (void)superLayerBoundsDidChange;
@end
@implementation RCTSingleSidedBorder
{
CALayer *_borderLayer;
}
- (instancetype)initWithSide:(RCTBorderSide)side superlayer:(CALayer *)superlayer
{
if (self = [super init]) {
_side = side;
_borderLayer = [CALayer layer];
_borderLayer.delegate = self;
_borderLayer.zPosition = INT_MAX - _side;
[superlayer insertSublayer:_borderLayer atIndex:0];
}
return self;
}
- (void)dealloc
{
_borderLayer.delegate = nil;
}
- (void)setWidth:(CGFloat)width
{
_width = width;
[_borderLayer setNeedsLayout];
}
- (void)setColor:(UIColor *)color
{
_color = color;
_borderLayer.backgroundColor = _color.CGColor;
[_borderLayer setNeedsLayout];
}
- (void)superLayerBoundsDidChange
{
[_borderLayer setNeedsLayout];
}
#pragma mark - CALayerDelegate
- (void)layoutSublayersOfLayer:(CALayer *)layer
{
CGSize superlayerSize = layer.superlayer.frame.size;
CGFloat xPosition = 0.0f;
CGFloat yPosition = 0.0f;
// Note: we ensure side layers are below top & bottom for snapshot test consistency
switch (self.side) {
case RCTBorderSideTop:
layer.frame = CGRectMake(xPosition, yPosition, superlayerSize.width, self.width);
break;
case RCTBorderSideRight:
xPosition = superlayerSize.width - self.width;
layer.frame = CGRectMake(xPosition, yPosition, self.width, superlayerSize.height);
[layer.superlayer insertSublayer:layer atIndex:0];
break;
case RCTBorderSideBottom:
yPosition = superlayerSize.height - self.width;
layer.frame = CGRectMake(xPosition, yPosition, superlayerSize.width, self.width);
break;
case RCTBorderSideLeft:
layer.frame = CGRectMake(xPosition, yPosition, self.width, superlayerSize.height);
[layer.superlayer insertSublayer:layer atIndex:0];
break;
}
}
// Disable animations for layer
- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
{
return (id)[NSNull null];
}
@end
@implementation UIView (ReactKitBorders)
- (void)reactSetBorders
{
NSMutableDictionary *borders = objc_getAssociatedObject(self, @selector(_createOrGetBorderWithSide:));
if (borders) {
for (RCTSingleSidedBorder *border in [borders allValues]) {
[border superLayerBoundsDidChange];
}
}
}
- (RCTSingleSidedBorder *)reactBorderTop
{
return [self _createOrGetBorderWithSide:RCTBorderSideTop];
}
- (RCTSingleSidedBorder *)reactBorderRight
{
return [self _createOrGetBorderWithSide:RCTBorderSideRight];
}
- (RCTSingleSidedBorder *)reactBorderBottom
{
return [self _createOrGetBorderWithSide:RCTBorderSideBottom];
}
- (RCTSingleSidedBorder *)reactBorderLeft
{
return [self _createOrGetBorderWithSide:RCTBorderSideLeft];
}
- (RCTSingleSidedBorder *)_createOrGetBorderWithSide:(RCTBorderSide)side
{
NSMutableDictionary *borders = objc_getAssociatedObject(self, _cmd);
if (!borders) {
borders = [[NSMutableDictionary alloc] init];
objc_setAssociatedObject(self, _cmd, borders, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
RCTSingleSidedBorder *border = [borders objectForKey:@(side)];
if (!border) {
border = [[RCTSingleSidedBorder alloc] initWithSide:side superlayer:self.layer];
[borders setObject:border forKey:@(side)];
}
return border;
}
@end