2015-02-03 updates

- Add back providesModule transform to JSAppServer | Joseph Savona
- [ReactKit] fix open source performance issue | John Harper
- [ReactKit] improve ReactIOSEventEmitter logics | Andrew Rasmussen
- [reactkit] fix web view JS executor and bind it to Command-d | John Harper
- Removed hardcoded RCTModuleIDs | Nick Lockwood
- [ReactKit] Animated GIF support | Alex Akers
- [ReactKit] Update RCTBridge to support non-`id` argument types | Alex Akers
- [reactkit] fix typo in RCTCopyProperty() change | John Harper
- [reactkit] fix shadow view crash on missing properties | John Harper
- [reactkit] fix transform keypath | John Harper
This commit is contained in:
Christopher Chedeau 2015-02-03 16:15:20 -08:00
parent ccd8f184af
commit 6153fffb30
46 changed files with 719 additions and 506 deletions

View File

@ -9,6 +9,7 @@
var React = require('react-native');
var {
Bundler,
Image,
StyleSheet,
Text,
TouchableHighlight,
@ -118,13 +119,22 @@ var Cell = React.createClass({
}
},
imageContents() {
switch (this.props.player) {
case 1:
return 'http://www.picgifs.com/alphabets/alphabets/children-5/alphabets-children-5-277623.gif';
case 2:
return 'http://www.picgifs.com/alphabets/alphabets/children-5/alphabets-children-5-730492.gif';
default:
return '';
}
},
render() {
return (
<TouchableHighlight onPress={this.props.onPress} underlayColor={'clear'} activeOpacity={0.5}>
<View style={[styles.cell, this.cellStyle()]}>
<Text style={[styles.cellText, this.textStyle()]}>
{this.textContents()}
</Text>
<Image source={{uri: this.imageContents()}} />
</View>
</TouchableHighlight>
);

View File

@ -169,12 +169,20 @@ var ReactIOSEventEmitter = merge(ReactEventEmitterMixin, {
var target = nativeEvent.target;
if (target !== null && target !== undefined) {
if (target < ReactIOSTagHandles.tagsStartAt) {
// When we get multiple touches at the same time, only the first touch
// actually has a view attached to it. The rest of the touches do not.
// This is presumably because iOS doesn't want to send touch events to
// two views for a single multi touch. Therefore this warning is only
// appropriate when it happens to the first touch. (hence jj === 0)
if (__DEV__) {
if (jj === 0) {
warning(
false,
'A view is reporting that a touch occured on tag zero.'
);
}
}
continue;
} else {
rootNodeID = NodeHandle.getRootNodeID(target);
}

View File

@ -37,12 +37,14 @@ function createStrictShapeTypeChecker(shapeTypes) {
var allKeys = merge(props[propName], shapeTypes);
for (var key in allKeys) {
var checker = shapeTypes[key];
if (!checker) {
invariant(
checker,
false,
`Invalid props.${propName} key \`${key}\` supplied to \`${componentName}\`.` +
`\nBad object: ` + JSON.stringify(props[propName], null, ' ') +
`\nValid keys: ` + JSON.stringify(Object.keys(shapeTypes), null, ' ')
);
}
var error = checker(propValue, key, componentName, location);
if (error) {
invariant(

View File

@ -33,10 +33,9 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
*/
@interface RCTBridge : NSObject <RCTInvalidating>
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig;
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor;
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args;
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args;
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete;
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;

View File

@ -8,7 +8,6 @@
#import "RCTInvalidating.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTModuleIDs.h"
#import "RCTUtils.h"
/**
@ -36,19 +35,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
RCTBridgeFieldFlushDateMillis
};
static NSString *RCTModuleName(Class moduleClass)
{
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
return [moduleClass moduleName];
} else {
// Default implementation, works in most cases
return NSStringFromClass(moduleClass);
}
}
static NSDictionary *RCTNativeModuleClasses(void)
{
static NSMutableDictionary *modules;
@ -73,7 +59,7 @@ static NSDictionary *RCTNativeModuleClasses(void)
}
// Get module name
NSString *moduleName = RCTModuleName(cls);
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
// Check module name is unique
id existingClass = modules[moduleName];
@ -90,23 +76,22 @@ static NSDictionary *RCTNativeModuleClasses(void)
@implementation RCTBridge
{
NSMutableDictionary *_moduleInstances;
NSDictionary *_javaScriptModulesConfig;
NSMutableDictionary *_moduleIDLookup;
NSMutableDictionary *_methodIDLookup;
id<RCTJavaScriptExecutor> _javaScriptExecutor;
}
static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig
{
if ((self = [super init])) {
_javaScriptExecutor = javaScriptExecutor;
_latestJSExecutor = _javaScriptExecutor;
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
_javaScriptModulesConfig = javaScriptModulesConfig;
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
// Register modules
// Instantiate modules
_moduleInstances = [[NSMutableDictionary alloc] init];
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
if (_moduleInstances[moduleName] == nil) {
@ -118,6 +103,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
}
}];
_moduleIDLookup = [[NSMutableDictionary alloc] init];
_methodIDLookup = [[NSMutableDictionary alloc] init];
[self doneRegisteringModules];
}
@ -168,10 +155,18 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
/**
* Like JS::call, for objective-c.
*/
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
{
RCTAssertMainThread();
[self _invokeRemoteJSModule:moduleID methodID:methodID args:args];
NSNumber *moduleID = _moduleIDLookup[moduleDotMethod];
RCTAssert(moduleID, @"Module '%@' not registered.",
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
NSNumber *methodID = _methodIDLookup[moduleDotMethod];
RCTAssert(methodID, @"Method '%@' not registered.", moduleDotMethod);
[self _invokeAndProcessModule:@"BatchedBridge"
method:@"callFunctionReturnFlushedQueue"
arguments:@[moduleID, methodID, args]];
}
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
@ -217,13 +212,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
callback:processResponse];
}
- (void)_invokeRemoteJSModule:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args
{
[self _invokeAndProcessModule:@"BatchedBridge"
method:@"callFunctionReturnFlushedQueue"
arguments:@[@(moduleID), @(methodID), args]];
}
/**
* TODO (#5906496): Have responses piggy backed on a round trip with ObjC->JS requests.
*/
@ -334,19 +322,19 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
return;
}
NSInvocation *invocation = [RCTBridge invocationForAdditionalArguments:methodArity];
// TODO: we should just store module instances by index, since that's how we look them up anyway
id target = strongSelf->_moduleInstances[moduleName];
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
[invocation setArgument:&target atIndex:0];
SEL selector = method.selector;
NSMethodSignature *methodSignature = [target methodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setArgument:&target atIndex:0];
[invocation setArgument:&selector atIndex:1];
// Retain used blocks until after invocation completes.
NSMutableArray *blocks = [NSMutableArray array];
NS_VALID_UNTIL_END_OF_SCOPE NSMutableArray *blocks = [NSMutableArray array];
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
if ([param isEqual:[NSNull null]]) {
@ -357,7 +345,57 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
param = block;
}
[invocation setArgument:&param atIndex:idx + 2];
NSUInteger argIdx = idx + 2;
BOOL shouldSet = YES;
const char *argumentType = [methodSignature getArgumentTypeAtIndex:argIdx];
switch (argumentType[0]) {
case ':':
if ([param isKindOfClass:[NSString class]]) {
SEL selector = NSSelectorFromString(param);
[invocation setArgument:&selector atIndex:argIdx];
shouldSet = NO;
}
break;
case '*':
if ([param isKindOfClass:[NSString class]]) {
const char *string = [param UTF8String];
[invocation setArgument:&string atIndex:argIdx];
shouldSet = NO;
}
break;
#define CASE(_value, _type, _selector) \
case _value: \
if ([param respondsToSelector:@selector(_selector)]) { \
_type value = [param _selector]; \
[invocation setArgument:&value atIndex:argIdx]; \
shouldSet = NO; \
} \
break;
CASE('c', char, charValue)
CASE('C', unsigned char, unsignedCharValue)
CASE('s', short, shortValue)
CASE('S', unsigned short, unsignedShortValue)
CASE('i', int, intValue)
CASE('I', unsigned int, unsignedIntValue)
CASE('l', long, longValue)
CASE('L', unsigned long, unsignedLongValue)
CASE('q', long long, longLongValue)
CASE('Q', unsigned long long, unsignedLongLongValue)
CASE('f', float, floatValue)
CASE('d', double, doubleValue)
CASE('B', BOOL, boolValue)
default:
break;
}
if (shouldSet) {
[invocation setArgument:&param atIndex:argIdx];
}
}];
@try {
@ -366,10 +404,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
@catch (NSException *exception) {
RCTLogMustFix(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception);
}
@finally {
// Force `blocks` to remain alive until here.
blocks = nil;
}
});
return YES;
@ -412,16 +446,71 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
return invocation;
}
- (NSArray *)JSMethods
{
NSMutableArray *methods = [[NSMutableArray alloc] init];
// Add globally used methods
[methods addObjectsFromArray:@[
@"Bundler.runApplication",
@"RCTEventEmitter.receiveEvent",
@"RCTEventEmitter.receiveTouches",
]];
// NOTE: these methods are currently unused in the OSS project
// @"Dimensions.set",
// @"RCTDeviceEventEmitter.emit",
// @"RCTNativeAppEventEmitter.emit",
// @"ReactIOS.unmountComponentAtNodeAndRemoveContainer",
// Register individual methods from modules
for (Class cls in RCTNativeModuleClasses().allValues) {
if (RCTClassOverridesClassMethod(cls, @selector(JSMethods))) {
[methods addObjectsFromArray:[cls JSMethods]];
}
}
return methods;
}
- (void)doneRegisteringModules
{
// TODO: everything in this method can actually be determined
// statically from just the class, and can therefore be cached
// in a dispatch_once instead of being repeated every time we
// reload or create a new RootView.
RCTAssertMainThread();
RCTAssert(_javaScriptModulesConfig != nil, @"JS module config not loaded in APP");
NSMutableDictionary *objectsToInject = [NSMutableDictionary dictionary];
/**
* This constructs the remote modules configuration data structure,
* which represents the native modules and methods that will be called
* by JS. A numeric ID is assigned to each module and method, which will
* be used to communicate via the bridge. The structure of each
* module is as follows:
*
* "ModuleName1": {
* "moduleID": 0,
* "methods": {
* "methodName1": {
* "methodID": 0,
* "type": "remote"
* },
* "methodName2": {
* "methodID": 1,
* "type": "remote"
* },
* etc...
* },
* "constants": {
* ...
* }
* },
* etc...
*/
// Dictionary of { moduleName0: { moduleID: 0, methods: { methodName0: { methodID: 0, type: remote }, methodName1: { ... }, ... }, ... }
NSUInteger moduleCount = RCTExportedMethodsByModule().count;
NSMutableDictionary *moduleConfigs = [NSMutableDictionary dictionaryWithCapacity:RCTExportedMethodsByModule().count];
NSMutableDictionary *remoteModules = [NSMutableDictionary dictionaryWithCapacity:RCTExportedMethodsByModule().count];
for (NSUInteger i = 0; i < moduleCount; i++) {
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(i);
NSArray *rawMethods = RCTExportedMethodsByModule()[moduleName];
@ -433,37 +522,89 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
};
}];
NSMutableDictionary *moduleConfig = [NSMutableDictionary dictionary];
moduleConfig[@"moduleID"] = @(i);
moduleConfig[@"methods"] = methods;
id target = [_moduleInstances objectForKey:moduleName];
if ([target respondsToSelector:@selector(constantsToExport)]) {
moduleConfig[@"constants"] = [target constantsToExport];
}
moduleConfigs[moduleName] = moduleConfig;
}
NSDictionary *batchedBridgeConfig = @{
@"remoteModuleConfig": moduleConfigs,
@"localModulesConfig": _javaScriptModulesConfig
NSDictionary *module = @{
@"moduleID": @(i),
@"methods": methods
};
NSString *configJSON = RCTJSONStringify(batchedBridgeConfig, NULL);
objectsToInject[@"__fbBatchedBridgeConfig"] = configJSON;
id target = _moduleInstances[moduleName];
if (RCTClassOverridesClassMethod([target class], @selector(constantsToExport))) {
module = [module mutableCopy];
((NSMutableDictionary *)module)[@"constants"] = [[target class] constantsToExport];
}
remoteModules[moduleName] = module;
}
/**
* As above, but for local modules/methods, which represent JS classes
* and methods that will be called by the native code via the bridge.
* Structure is essentially the same as for remote modules:
*
* "ModuleName1": {
* "moduleID": 0,
* "methods": {
* "methodName1": {
* "methodID": 0,
* "type": "local"
* },
* "methodName2": {
* "methodID": 1,
* "type": "local"
* },
* etc...
* }
* },
* etc...
*/
NSMutableDictionary *localModules = [[NSMutableDictionary alloc] init];
for (NSString *moduleDotMethod in [self JSMethods]) {
NSArray *parts = [moduleDotMethod componentsSeparatedByString:@"."];
RCTAssert(parts.count == 2, @"'%@' is not a valid JS method definition - expected 'Module.method' format.", moduleDotMethod);
// Add module if it doesn't already exist
NSString *moduleName = parts[0];
NSDictionary *module = localModules[moduleName];
if (!module) {
module = @{
@"moduleID": @(localModules.count),
@"methods": [[NSMutableDictionary alloc] init]
};
localModules[moduleName] = module;
}
// Add method if it doesn't already exist
NSString *methodName = parts[1];
NSMutableDictionary *methods = module[@"methods"];
if (!methods[methodName]) {
methods[methodName] = @{
@"methodID": @(methods.count),
@"type": @"local"
};
}
// Add module and method lookup
_moduleIDLookup[moduleDotMethod] = module[@"moduleID"];
_methodIDLookup[moduleDotMethod] = methods[methodName][@"methodID"];
}
/**
* Inject module data into JS context
*/
NSString *configJSON = RCTJSONStringify(@{
@"remoteModuleConfig": remoteModules,
@"localModulesConfig": localModules
}, NULL);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *script, BOOL *stop) {
[_javaScriptExecutor injectJSONText:script asGlobalObjectNamed:objectName callback:^(id err) {
[_javaScriptExecutor injectJSONText:configJSON asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) {
dispatch_semaphore_signal(semaphore);
}];
}];
for (NSUInteger i = 0, count = objectsToInject.count; i < count; i++) {
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)) != 0) {
RCTLogMustFix(@"JavaScriptExecutor take too long to inject JSON object");
}
}
}
- (void)registerRootView:(RCTRootView *)rootView
{

View File

@ -41,6 +41,7 @@
+ (UIColor *)UIColor:(id)json;
+ (CGColorRef)CGColor:(id)json;
+ (CAKeyframeAnimation *)GIF:(id)json;
+ (UIImage *)UIImage:(id)json;
+ (CGImageRef)CGImage:(id)json;

View File

@ -2,6 +2,9 @@
#import "RCTConvert.h"
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "RCTLog.h"
CGFloat const RCTDefaultFontSize = 14;
@ -426,6 +429,83 @@ RCT_STRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]
return [self UIColor:json].CGColor;
}
+ (CAKeyframeAnimation *)GIF:(id)json
{
CGImageSourceRef imageSource;
if ([json isKindOfClass:[NSString class]]) {
NSString *path = json;
if (path.length == 0) {
return nil;
}
NSURL *fileURL = [path isAbsolutePath] ? [NSURL fileURLWithPath:path] : [[NSBundle mainBundle] URLForResource:path withExtension:nil];
imageSource = CGImageSourceCreateWithURL((CFURLRef)fileURL, NULL);
} else if ([json isKindOfClass:[NSData class]]) {
NSData *data = json;
if (data.length == 0) {
return nil;
}
imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL);
}
if (!UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)) {
CFRelease(imageSource);
return nil;
}
NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL);
NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue];
size_t imageCount = CGImageSourceGetCount(imageSource);
NSTimeInterval duration = 0;
NSMutableArray *delays = [NSMutableArray arrayWithCapacity:imageCount];
NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount];
for (size_t i = 0; i < imageCount; i++) {
CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL);
NSDictionary *frameGIFProperties = frameProperties[(id)kCGImagePropertyGIFDictionary];
const NSTimeInterval kDelayTimeIntervalDefault = 0.1;
NSNumber *delayTime = frameGIFProperties[(id)kCGImagePropertyGIFUnclampedDelayTime] ?: frameGIFProperties[(id)kCGImagePropertyGIFDelayTime];
if (delayTime == nil) {
if (i == 0) {
delayTime = @(kDelayTimeIntervalDefault);
} else {
delayTime = delays[i - 1];
}
}
const NSTimeInterval kDelayTimeIntervalMinimum = 0.02;
if (delayTime.floatValue < (float)kDelayTimeIntervalMinimum - FLT_EPSILON) {
delayTime = @(kDelayTimeIntervalDefault);
}
duration += delayTime.doubleValue;
delays[i] = delayTime;
images[i] = (__bridge_transfer id)image;
}
CFRelease(imageSource);
NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count];
NSTimeInterval runningDuration = 0;
for (NSNumber *delayNumber in delays) {
[keyTimes addObject:@(runningDuration / duration)];
runningDuration += delayNumber.doubleValue;
}
[keyTimes addObject:@1.0];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
animation.calculationMode = kCAAnimationDiscrete;
animation.repeatCount = loopCount == 0 ? HUGE_VALF : loopCount;
animation.keyTimes = keyTimes;
animation.values = images;
animation.duration = duration;
return animation;
}
+ (UIImage *)UIImage:(id)json
{
if (![json isKindOfClass:[NSString class]]) {

View File

@ -33,9 +33,11 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
- (instancetype)initWithBridge:(RCTBridge *)bridge;
/**
* Send an arbitrary event type
* Send a named event. For most purposes, use the an
* event type of RCTEventTypeDefault, the other types
* are used internally by the React framework.
*/
- (void)sendRawEventWithType:(NSString *)eventType body:(NSDictionary *)body;
- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body;
/**
* Send an array of touch events

View File

@ -3,7 +3,6 @@
#import "RCTEventDispatcher.h"
#import "RCTBridge.h"
#import "RCTModuleIDs.h"
#import "UIView+ReactKit.h"
@implementation RCTEventDispatcher
@ -19,39 +18,13 @@
return self;
}
- (NSArray *)touchEvents
- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body
{
static NSArray *events;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
events = @[
@"topTouchStart",
@"topTouchMove",
@"topTouchEnd",
@"topTouchCancel",
];
});
return events;
}
- (void)sendRawEventWithType:(NSString *)eventType body:(NSDictionary *)body
{
static NSSet *touchEvents;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
touchEvents = [NSSet setWithArray:[self touchEvents]];
});
RCTAssert(![touchEvents containsObject:eventType], @"Touch events must be"
"sent via the sendTouchEventWithOrderedTouches: method, not sendRawEventWithType:");
RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]],
@"Event body dictionary must include a 'target' property containing a react tag");
[_bridge enqueueJSCall:RCTModuleIDReactIOSEventEmitter
methodID:RCTEventEmitterReceiveEvent
args:@[body[@"target"], eventType, body]];
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent"
args:@[body[@"target"], name, body]];
}
/**
@ -69,30 +42,32 @@
touches:(NSArray *)touches
changedIndexes:(NSArray *)changedIndexes
{
static NSString *events[] = {
@"topTouchStart",
@"topTouchMove",
@"topTouchEnd",
@"topTouchCancel",
};
RCTAssert(touches.count, @"No touches in touchEventArgsForOrderedTouches");
[_bridge enqueueJSCall:RCTModuleIDReactIOSEventEmitter
methodID:RCTEventEmitterReceiveTouches
args:@[[self touchEvents][type], touches, changedIndexes]];
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveTouches"
args:@[events[type], touches, changedIndexes]];
}
- (void)sendTextEventWithType:(RCTTextEventType)type
reactTag:(NSNumber *)reactTag
text:(NSString *)text
{
static NSArray *events;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
events = @[
static NSString *events[] = {
@"topFocus",
@"topBlur",
@"topChange",
@"topSubmitEditing",
@"topEndEditing",
];
});
};
[self sendRawEventWithType:events[type] body:@{
[self sendEventWithName:events[type] body:@{
@"text": text,
@"target": reactTag
}];
@ -111,18 +86,14 @@
scrollView:(UIScrollView *)scrollView
userData:(NSDictionary *)userData
{
static NSArray *events;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
events = @[
static NSString *events[] = {
@"topScrollBeginDrag",
@"topScroll",
@"topScrollEndDrag",
@"topMomentumScrollBegin",
@"topMomentumScrollEnd",
@"topScrollAnimationEnd",
];
});
};
NSDictionary *body = @{
@"contentOffset": @{
@ -147,7 +118,7 @@
body = mutableBody;
}
[self sendRawEventWithType:events[type] body:body];
[self sendEventWithName:events[type] body:body];
}
@end

View File

@ -26,6 +26,7 @@ extern NSString *RCTExportedModuleNameAtSortedIndex(NSUInteger index);
extern NSDictionary *RCTExportedMethodsByModule(void);
extern BOOL RCTSetProperty(id target, NSString *keypath, id value);
extern BOOL RCTCopyProperty(id target, id source, NSString *keypath);
extern BOOL RCTCallSetter(id target, SEL setter, id value);
/* ------------------------------------------------------------------- */
@ -66,9 +67,22 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
/**
* Injects constants into JS. These constants are made accessible via
* NativeModules.moduleName.X.
* NativeModules.moduleName.X. Note that this method is not inherited when you
* subclass a module, and you should not call [super constantsToExport] when
* implementing it.
*/
- (NSDictionary *)constantsToExport;
+ (NSDictionary *)constantsToExport;
/**
* An array of JavaScript methods that the module will call via the
* -[RCTBridge enqueueJSCall:args:] method. Each method should be specified
* as a string of the form "JSModuleName.jsMethodName". Attempting to call a
* method that has not been registered will result in an error. If a method
* has already been regsistered by another module, it is not necessary to
* register it again, but it is good pratice. Registering the same method
* more than once is silently ignored and will not result in an error.
*/
+ (NSArray *)JSMethods;
/**
* Notifies the module that a batch of JS method invocations has just completed.
@ -84,9 +98,18 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
@protocol RCTNativeViewModule <NSObject>
/**
* This method instantiates a native view to be managed by the module.
* Designated initializer for view modules. The event dispatched can either be
* used directly by the module, or passed on to instantiated views for event handling.
*/
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
/**
* This method instantiates a native view to be managed by the module. The method
* will be called many times, and should return a fresh instance each time. The
* view module MUST NOT cache the returned view and return the same instance
* for subsequent calls.
*/
- (UIView *)view;
@optional
@ -98,7 +121,8 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
/**
* This method instantiates a shadow view to be managed by the module. If omitted,
* an ordinary RCTShadowView instance will be created.
* an ordinary RCTShadowView instance will be created. As with the -view method,
* the -shadowView method should return a fresh instance each time it is called.
*/
- (RCTShadowView *)shadowView;
@ -164,8 +188,11 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
* }
* }
* }
*
* Note that this method is not inherited when you subclass a view module, and
* you should not call [super customBubblingEventTypes] when implementing it.
*/
- (NSDictionary *)customBubblingEventTypes;
+ (NSDictionary *)customBubblingEventTypes;
/**
* Returns a dictionary of config data passed to JS that defines eligible events
@ -177,14 +204,19 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
* @"registrationName": @"onTwirl"
* }
* }
*
* Note that this method is not inherited when you subclass a view module, and
* you should not call [super customDirectEventTypes] when implementing it.
*/
- (NSDictionary *)customDirectEventTypes;
+ (NSDictionary *)customDirectEventTypes;
/**
* Injects constants into JS. These constants are made accessible via
* NativeModules.moduleName.X.
* NativeModules.moduleName.X. Note that this method is not inherited when you
* subclass a view module, and you should not call [super constantsToExport]
* when implementing it.
*/
- (NSDictionary *)constantsToExport;
+ (NSDictionary *)constantsToExport;
/**
* To deprecate, hopefully

View File

@ -349,6 +349,36 @@ BOOL RCTSetProperty(id target, NSString *keypath, id value)
return YES;
}
BOOL RCTCopyProperty(id target, id source, NSString *keypath)
{
// Split keypath
NSArray *parts = [keypath componentsSeparatedByString:@"."];
NSString *key = [parts lastObject];
for (NSUInteger i = 0; i < parts.count - 1; i++) {
source = [source valueForKey:parts[i]];
target = [target valueForKey:parts[i]];
if (!source || !target) {
return NO;
}
}
// Check class for property definition
if (!class_getProperty([source class], [key UTF8String])) {
// Check if setter exists
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",
[[key substringToIndex:1] uppercaseString],
[key substringFromIndex:1]]);
if (![source respondsToSelector:setter]
|| ![target respondsToSelector:setter]) {
return NO;
}
}
[target setValue:[source valueForKey:key] forKey:key];
return YES;
}
BOOL RCTCallSetter(id target, SEL setter, id value)
{
// Get property name

View File

@ -2,12 +2,16 @@
#import <UIKit/UIKit.h>
typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error);
typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
@interface RCTImageDownloader : NSObject
+ (instancetype)sharedInstance;
- (id)downloadDataForURL:(NSURL *)url
block:(RCTDataDownloadBlock)block;
- (id)downloadImageForURL:(NSURL *)url
size:(CGSize)size
scale:(CGFloat)scale

View File

@ -18,6 +18,20 @@
return sharedInstance;
}
- (id)downloadDataForURL:(NSURL *)url
block:(RCTDataDownloadBlock)block
{
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// Dispatch back to main thread
dispatch_async(dispatch_get_main_queue(), ^{
block(data, error);
});
}];
[task resume];
return task;
}
- (id)downloadImageForURL:(NSURL *)url
size:(CGSize)size
scale:(CGFloat)scale

View File

@ -1,60 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import <Foundation/Foundation.h>
/**
* All of this will be replaced with an auto-generated bridge.
*/
typedef NS_ENUM(NSUInteger, RCTJSModuleIDs) {
RCTModuleIDReactIOSEventEmitter,
RCTModuleIDJSTimers, // JS timer tracking module
RCTModuleIDReactIOS,
RCTModuleIDBundler,
RCTModuleIDDimensions,
RCTModuleIDDeviceEventEmitter,
RCTModuleIDNativeAppEventEmitter,
};
/**
* JS module `RCTIOSEventEmitter`.
*/
typedef NS_ENUM(NSUInteger, RCTEventEmitterRemoteMethodIDs) {
RCTEventEmitterReceiveEvent = 0,
RCTEventEmitterReceiveTouches
};
typedef NS_ENUM(NSUInteger, RCTKeyCode) {
RCTKeyCodeBackspace = 8,
RCTKeyCodeReturn = 13,
};
/**
* JS timer tracking module.
*/
typedef NS_ENUM(NSUInteger, RCTJSTimersMethodIDs) {
RCTJSTimersCallTimers = 0
};
typedef NS_ENUM(NSUInteger, RCTReactIOSMethodIDs) {
RCTReactIOSUnmountComponentAtNodeAndRemoveContainer = 0,
};
typedef NS_ENUM(NSUInteger, RCTBundlerMethodIDs) {
RCTBundlerRunApplication = 0
};
typedef NS_ENUM(NSUInteger, RCTDimensionsMethodIDs) {
RCTDimensionsSet = 0
};
typedef NS_ENUM(NSUInteger, RCTDeviceEventEmitterMethodIDs) {
RCTDeviceEventEmitterEmit = 0
};
@interface RCTModuleIDs : NSObject
+ (NSDictionary *)config;
@end

View File

@ -1,92 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTModuleIDs.h"
@implementation RCTModuleIDs
/**
* Configures invocations from IOS -> JS. Simply passes the name of the key in
* the configuration object `require('ReactIOSEventEmitter')`.
*/
+ (NSDictionary *)config
{
return @{
@"Dimensions": @{
@"moduleID": @(RCTModuleIDDimensions),
@"methods": @{
@"set": @{
@"methodID": @(RCTDimensionsSet),
@"type": @"local"
},
}
},
@"RCTDeviceEventEmitter": @{
@"moduleID": @(RCTModuleIDDeviceEventEmitter),
@"methods": @{
@"emit": @{
@"methodID": @(RCTDeviceEventEmitterEmit),
@"type": @"local"
},
}
},
@"RCTEventEmitter": @{
@"moduleID": @(RCTModuleIDReactIOSEventEmitter),
@"methods": @{
@"receiveEvent": @{
@"methodID": @(RCTEventEmitterReceiveEvent),
@"type": @"local"
},
@"receiveTouches": @{
@"methodID": @(RCTEventEmitterReceiveTouches),
@"type": @"local"
},
}
},
@"RCTNativeAppEventEmitter": @{
@"moduleID": @(RCTModuleIDNativeAppEventEmitter),
@"methods": @{
@"emit": @{
@"methodID": @(RCTDeviceEventEmitterEmit),
@"type": @"local"
},
}
},
@"RCTJSTimers": @{
@"moduleID": @(RCTModuleIDJSTimers),
@"methods": @{
// Last argument is the callback.
@"callTimers": @{
@"methodID": @(RCTJSTimersCallTimers),
@"type": @"local"
},
}
},
@"ReactIOS": @{
@"moduleID": @(RCTModuleIDReactIOS),
@"methods": @{
@"unmountComponentAtNodeAndRemoveContainer": @{
@"methodID": @(RCTReactIOSUnmountComponentAtNodeAndRemoveContainer),
@"type": @"local"
},
}
},
@"Bundler": @{
@"moduleID": @(RCTModuleIDBundler),
@"methods": @{
@"runApplication": @{
@"methodID": @(RCTBundlerRunApplication),
@"type": @"local"
}
}
}
};
}
@end

View File

@ -6,11 +6,11 @@
#import "RCTContextExecutor.h"
#import "RCTEventDispatcher.h"
#import "RCTJavaScriptAppEngine.h"
#import "RCTModuleIDs.h"
#import "RCTTouchHandler.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
#import "RCTViewManager.h"
#import "RCTWebViewExecutor.h"
#import "UIView+ReactKit.h"
#import "RCTKeyCommands.h"
@ -23,6 +23,8 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
RCTTouchHandler *_touchHandler;
}
static BOOL _useWebExec;
+ (void)initialize
{
@ -34,6 +36,13 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
action:^(UIKeyCommand *command) {
[self reloadAll];
}];
// Cmd-D reloads using the web view executor, allows attaching from Safari dev tools.
[[RCTKeyCommands sharedInstance] registerKeyCommandWithInput:@"d"
modifierFlags:UIKeyModifierCommand
action:^(UIKeyCommand *command) {
_useWebExec = YES;
[self reloadAll];
}];
#endif
@ -98,8 +107,8 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
@"rootTag": self.reactTag ?: @0,
@"initialProps": self.initialProperties ?: @{},
};
[_bridge enqueueJSCall:RCTModuleIDBundler
methodID:RCTBundlerRunApplication
[_bridge enqueueJSCall:@"Bundler.runApplication"
args:@[moduleName, appParameters]];
}
}
@ -123,9 +132,13 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
[_executor invalidate];
[_bridge invalidate];
if (!_useWebExec) {
_executor = [[RCTContextExecutor alloc] init];
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor
javaScriptModulesConfig:[RCTModuleIDs config]];
} else {
_executor = [[RCTWebViewExecutor alloc] init];
}
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor];
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];
_touchHandler = [[RCTTouchHandler alloc] initWithEventDispatcher:_bridge.eventDispatcher rootView:self];
@ -165,22 +178,4 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRootViewReloadNotification object:nil];
}
#pragma mark - Key commands
- (NSArray *)keyCommands
{
return @[
// Reload
[UIKeyCommand keyCommandWithInput:@"r"
modifierFlags:UIKeyModifierCommand
action:@selector(reload)]
];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
@end

View File

@ -4,9 +4,8 @@
@interface RCTSparseArray : NSObject <NSCopying>
- (instancetype)init;
- (instancetype)initWithCapacity:(NSUInteger)capacity;
- (instancetype)initWithSparseArray:(RCTSparseArray *)sparseArray;
- (instancetype)initWithCapacity:(NSUInteger)capacity NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithSparseArray:(RCTSparseArray *)sparseArray NS_DESIGNATED_INITIALIZER;
+ (instancetype)sparseArray;
+ (instancetype)sparseArrayWithCapacity:(NSUInteger)capacity;

View File

@ -28,14 +28,6 @@
return self;
}
- (instancetype)initWithStorage:(NSDictionary *)storage
{
if ((self = [super init])) {
_storage = [storage copy];
}
return self;
}
+ (instancetype)sparseArray
{
return [[self alloc] init];

View File

@ -11,6 +11,9 @@
#import "RCTUtils.h"
#import "UIView+ReactKit.h"
// TODO: this class behaves a lot like a module, and could be implemented as a
// module if we were to assume that modules and RootViews had a 1:1 relationship
@implementation RCTTouchHandler
{
__weak UIView *_rootView;
@ -27,11 +30,6 @@
NSMutableArray *_touchViews;
}
- (instancetype)init
{
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithTarget:(id)target action:(SEL)action
{
RCT_NOT_DESIGNATED_INITIALIZER();

View File

@ -20,8 +20,9 @@ id RCTJSONParse(NSString *jsonString, NSError **error);
// Get MD5 hash of a string
NSString *RCTMD5Hash(NSString *string);
// Get screen scale in a thread-safe way
// Get screen metrics in a thread-safe way
CGFloat RCTScreenScale(void);
CGSize RCTScreenSize(void);
// Round float coordinates to nearest whole screen pixel (not point)
CGFloat RCTRoundPixelValue(CGFloat value);
@ -34,3 +35,7 @@ NSTimeInterval RCTTGetAbsoluteTime(void);
// Method swizzling
void RCTSwapClassMethods(Class cls, SEL original, SEL replacement);
void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement);
// Module subclass support
BOOL RCTClassOverridesClassMethod(Class cls, SEL selector);
BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector);

View File

@ -35,7 +35,7 @@ NSString *RCTMD5Hash(NSString *string)
CGFloat RCTScreenScale()
{
static CGFloat scale = -1;
static CGFloat scale;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (![NSThread isMainThread]) {
@ -50,6 +50,23 @@ CGFloat RCTScreenScale()
return scale;
}
CGSize RCTScreenSize()
{
static CGSize size;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (![NSThread isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
size = [UIScreen mainScreen].bounds.size;
});
} else {
size = [UIScreen mainScreen].bounds.size;
}
});
return size;
}
CGFloat RCTRoundPixelValue(CGFloat value)
{
CGFloat scale = RCTScreenScale();
@ -120,3 +137,24 @@ void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement)
method_exchangeImplementations(originalMethod, replacementMethod);
}
}
BOOL RCTClassOverridesClassMethod(Class cls, SEL selector)
{
return RCTClassOverridesInstanceMethod(object_getClass(cls), selector);
}
BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector)
{
unsigned int numberOfMethods;
Method *methods = class_copyMethodList(cls, &numberOfMethods);
for (unsigned int i = 0; i < numberOfMethods; i++)
{
if (method_getName(methods[i]) == selector)
{
free(methods);
return YES;
}
}
return NO;
}

View File

@ -41,6 +41,11 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
return self;
}
- (id)init
{
return [self initWithWebView:[[UIWebView alloc] init]];
}
- (BOOL)isValid
{
return _webView != nil;
@ -98,7 +103,13 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
sourceURL:(NSURL *)url
onComplete:(RCTJavaScriptCompleteBlock)onComplete
{
RCTAssertMainThread();
if (![NSThread isMainThread]) {
dispatch_sync(dispatch_get_main_queue(), ^{
[self executeApplicationScript:script sourceURL:url onComplete:onComplete];
});
return;
}
RCTAssert(onComplete != nil, @"");
_onApplicationScriptLoaded = onComplete;
@ -137,26 +148,12 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
*/
- (void)executeBlockOnJavaScriptQueue:(dispatch_block_t)block
{
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(executeBlockOnJavaScriptQueue:)
withObject:block
waitUntilDone:YES];
} else {
[self performSelector:@selector(_onMainThreadExecuteBlockAfterDelay:)
withObject:block afterDelay:0.001 // This can't be zero!
inModes:@[NSDefaultRunLoopMode, UITrackingRunLoopMode]];
}
}
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_MSEC);
/**
* This timing delay is needed to avoid crashes in WebKit when setting a
* breakpoint or `debugger` statement and debugging via the remote Safari
* inspector.
*/
- (void)_onMainThreadExecuteBlockAfterDelay:(dispatch_block_t)block
{
dispatch_after(when, dispatch_get_main_queue(), ^{
RCTAssertMainThread();
block();
});
}
/**

View File

@ -4,7 +4,6 @@
#import "RCTBridge.h"
#import "RCTLog.h"
#import "RCTModuleIDs.h"
#import "RCTSparseArray.h"
#import "RCTUtils.h"
@ -55,6 +54,11 @@
id _updateTimer;
}
+ (NSArray *)JSMethods
{
return @[@"RCTJSTimers.callTimers"];
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if ((self = [super init])) {
@ -143,7 +147,7 @@
// call timers that need to be called
if ([timersToCall count] > 0) {
[_bridge enqueueJSCall:RCTModuleIDJSTimers methodID:RCTJSTimersCallTimers args:@[timersToCall]];
[_bridge enqueueJSCall:@"RCTJSTimers.callTimers" args:@[timersToCall]];
}
}
@ -155,14 +159,14 @@
* Date.now() from JS and then subtracting that from the current time here.
*/
- (void)createTimer:(NSNumber *)callbackID
duration:(NSNumber *)jsDuration
jsSchedulingTime:(NSNumber *)jsSchedulingTime
repeats:(NSNumber *)repeats
duration:(double)jsDuration
jsSchedulingTime:(double)jsSchedulingTime
repeats:(BOOL)repeats
{
RCT_EXPORT();
NSTimeInterval interval = jsDuration.doubleValue / 1000;
NSTimeInterval jsCreationTimeSinceUnixEpoch = jsSchedulingTime.doubleValue / 1000;
NSTimeInterval interval = jsDuration / 1000;
NSTimeInterval jsCreationTimeSinceUnixEpoch = jsSchedulingTime / 1000;
NSTimeInterval currentTimeSinceUnixEpoch = [[NSDate date] timeIntervalSince1970];
NSTimeInterval jsSchedulingOverhead = currentTimeSinceUnixEpoch - jsCreationTimeSinceUnixEpoch;
if (jsSchedulingOverhead < 0) {

View File

@ -12,7 +12,7 @@
@protocol RCTScrollableProtocol;
@protocol RCTViewNodeProtocol;
@interface RCTUIManager : NSObject <RCTInvalidating, RCTNativeModule>
@interface RCTUIManager : NSObject <RCTNativeModule, RCTInvalidating>
- (instancetype)initWithBridge:(RCTBridge *)bridge;

View File

@ -34,26 +34,6 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
}
}
static NSString *RCTModuleName(Class moduleClass)
{
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
return [moduleClass moduleName];
} else {
// Default implementation, works in most cases
NSString *className = NSStringFromClass(moduleClass);
if ([className hasPrefix:@"RCTUI"]) {
className = [className substringFromIndex:@"RCT".length];
}
if ([className hasSuffix:@"Manager"]) {
className = [className substringToIndex:className.length - @"Manager".length];
}
return className;
}
}
static NSDictionary *RCTViewModuleClasses(void)
{
static NSMutableDictionary *modules;
@ -78,7 +58,7 @@ static NSDictionary *RCTViewModuleClasses(void)
}
// Get module name
NSString *moduleName = RCTModuleName(cls);
NSString *moduleName = [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
// Check module name is unique
id existingClass = modules[moduleName];
@ -131,7 +111,7 @@ static NSDictionary *RCTViewModuleClasses(void)
// Instantiate view managers
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
viewManagers[moduleName] = [[moduleClass alloc] init];
viewManagers[moduleName] = [[moduleClass alloc] initWithEventDispatcher:_bridge.eventDispatcher];
}];
_viewManagers = viewManagers;
@ -535,11 +515,6 @@ static NSDictionary *RCTViewModuleClasses(void)
}
}
- (UIView *)viewForViewManager:(id <RCTNativeViewModule>)manager
{
return [manager viewWithEventDispatcher:_bridge.eventDispatcher];
}
static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView, id <RCTNativeViewModule>manager)
{
// TODO: cache respondsToSelector tests
@ -581,7 +556,9 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
if (obj == [NSNull null]) {
// Copy property from default view to current
RCTSetProperty(shadowView, key, [defaultView valueForKey:key]);
// Note: not just doing `[defaultView valueForKey:key]`, the
// key may not exist, in which case we'd get an exception.
RCTCopyProperty(shadowView, defaultView, key);
} else {
RCTSetProperty(shadowView, key, obj);
}
@ -619,10 +596,10 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
// Note the default is setup after the props are read for the first time ever
// for this className - this is ok because we only use the default for restoring
// defaults, which never happens on first creation.
uiManager->_defaultViews[moduleName] = [uiManager viewForViewManager:manager];
uiManager->_defaultViews[moduleName] = [manager view];
}
UIView *view = [uiManager viewForViewManager:manager];
UIView *view = [manager view];
if (view) {
// Set required properties
view.reactTag = reactTag;
@ -1013,7 +990,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
}];
}
- (NSDictionary *)allBubblingEventTypesConfigs
+ (NSDictionary *)allBubblingEventTypesConfigs
{
NSMutableDictionary *customBubblingEventTypesConfigs = [@{
// Bubble dispatched events
@ -1103,20 +1080,20 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
},
} mutableCopy];
for (id <RCTNativeViewModule> viewManager in _viewManagers.allValues) {
NSDictionary *bubblingEvents = [viewManager respondsToSelector:@selector(customBubblingEventTypes)]? [viewManager customBubblingEventTypes] : nil;
if (bubblingEvents) {
for (NSString *eventName in bubblingEvents) {
RCTCAssert(!customBubblingEventTypesConfigs[eventName], @"Event %@ registered multiple times.", eventName);
}
[customBubblingEventTypesConfigs addEntriesFromDictionary:bubblingEvents];
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
if (RCTClassOverridesClassMethod(cls, @selector(customBubblingEventTypes))) {
NSDictionary *eventTypes = [cls customBubblingEventTypes];
for (NSString *eventName in eventTypes) {
RCTCAssert(!customBubblingEventTypesConfigs[eventName], @"Event '%@' registered multiple times.", eventName);
}
[customBubblingEventTypesConfigs addEntriesFromDictionary:eventTypes];
}
}];
return customBubblingEventTypesConfigs;
}
- (NSDictionary *)allDirectEventTypesConfigs
+ (NSDictionary *)allDirectEventTypesConfigs
{
NSMutableDictionary *customDirectEventTypes = [@{
@"topScrollBeginDrag": @{
@ -1154,22 +1131,21 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
},
} mutableCopy];
for (id <RCTNativeViewModule> viewManager in _viewManagers.allValues) {
NSDictionary *bubblingEvents = [viewManager respondsToSelector:@selector(customDirectEventTypes)] ? [viewManager customDirectEventTypes] : nil;
if (bubblingEvents) {
for (NSString *eventName in bubblingEvents) {
RCTCAssert(!customDirectEventTypes[eventName], @"Event %@ registered multiple times.", eventName);
}
[customDirectEventTypes addEntriesFromDictionary:bubblingEvents];
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
if (RCTClassOverridesClassMethod(cls, @selector(customDirectEventTypes))) {
NSDictionary *eventTypes = [cls customDirectEventTypes];
for (NSString *eventName in eventTypes) {
RCTCAssert(!customDirectEventTypes[eventName], @"Event '%@' registered multiple times.", eventName);
}
[customDirectEventTypes addEntriesFromDictionary:eventTypes];
}
}];
return customDirectEventTypes;
}
- (NSDictionary *)constantsToExport
+ (NSDictionary *)constantsToExport
{
UIScreen *screen = [UIScreen mainScreen];
NSMutableDictionary *allJSConstants = [@{
@"customBubblingEventTypes": [self allBubblingEventTypesConfigs],
@"customDirectEventTypes": [self allDirectEventTypesConfigs],
@ -1180,13 +1156,13 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
},
@"Dimensions": @{
@"window": @{
@"width": @(screen.bounds.size.width),
@"height": @(screen.bounds.size.height),
@"scale": @(screen.scale),
@"width": @(RCTScreenSize().width),
@"height": @(RCTScreenSize().height),
@"scale": @(RCTScreenScale()),
},
@"modalFullscreenView": @{
@"width": @(screen.bounds.size.width),
@"height": @(screen.bounds.size.height),
@"width": @(RCTScreenSize().width),
@"height": @(RCTScreenSize().width),
},
},
@"StyleConstants": @{
@ -1224,12 +1200,15 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
},
} mutableCopy];
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, id <RCTNativeViewModule> viewManager, BOOL *stop) {
NSDictionary *constants = [viewManager respondsToSelector:@selector(constantsToExport)] ? [viewManager constantsToExport] : nil;
if (constants) {
RCTAssert(allJSConstants[name] == nil , @"Cannot redefine constant namespace: %@", name);
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
// TODO: should these be inherited?
NSDictionary *constants = RCTClassOverridesClassMethod(cls, @selector(constantsToExport)) ? [cls constantsToExport] : nil;
if ([constants count]) {
NSMutableDictionary *namespace = [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]];
RCTAssert(namespace[@"Constants"] == nil , @"Cannot redefine Constants in namespace: %@", name);
// add an additional 'Constants' namespace for each class
allJSConstants[name] = @{@"Constants": constants};
namespace[@"Constants"] = constants;
allJSConstants[name] = [namespace copy];
}
}];

View File

@ -50,7 +50,6 @@
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */; };
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */; };
83CBBA871A60202500E9B192 /* RCTJavaScriptAppEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */; };
83CBBA8B1A60204600E9B192 /* RCTModuleIDs.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA8A1A60204600E9B192 /* RCTModuleIDs.m */; };
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */; };
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBACB1A6023D300E9B192 /* RCTConvert.m */; };
83EEC2EE1A604AB200C39218 /* RCTModuleMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EEC2ED1A604AB200C39218 /* RCTModuleMethod.m */; };
@ -162,8 +161,6 @@
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTEventDispatcher.m; sourceTree = "<group>"; };
83CBBA851A60202500E9B192 /* RCTJavaScriptAppEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptAppEngine.h; sourceTree = "<group>"; };
83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJavaScriptAppEngine.m; sourceTree = "<group>"; };
83CBBA891A60204600E9B192 /* RCTModuleIDs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleIDs.h; sourceTree = "<group>"; };
83CBBA8A1A60204600E9B192 /* RCTModuleIDs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleIDs.m; sourceTree = "<group>"; };
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTouchHandler.h; sourceTree = "<group>"; };
83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTouchHandler.m; sourceTree = "<group>"; };
83CBBACA1A6023D300E9B192 /* RCTConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTConvert.h; sourceTree = "<group>"; };
@ -329,8 +326,6 @@
83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */,
83CBBA4D1A601E3B00E9B192 /* RCTLog.h */,
83CBBA4E1A601E3B00E9B192 /* RCTLog.m */,
83CBBA891A60204600E9B192 /* RCTModuleIDs.h */,
83CBBA8A1A60204600E9B192 /* RCTModuleIDs.m */,
83CBBA581A601E9000E9B192 /* RCTRedBox.h */,
83CBBA591A601E9000E9B192 /* RCTRedBox.m */,
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */,
@ -439,7 +434,6 @@
13E067591A70F44B002CDEE1 /* UIView+ReactKit.m in Sources */,
137029531A69923600575408 /* RCTImageDownloader.m in Sources */,
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */,
83CBBA8B1A60204600E9B192 /* RCTModuleIDs.m in Sources */,
83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */,
13B080071A6947C200A75B9A /* RCTShadowRawText.m in Sources */,
13B0801D1A69489C00A75B9A /* RCTNavItemManager.m in Sources */,
@ -555,6 +549,7 @@
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@ -564,6 +559,7 @@
83CBBA411A601D0F00E9B192 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;

View File

@ -7,9 +7,9 @@
@implementation RCTNavItemManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
return [[RCTNavItem alloc] initWithFrame:CGRectZero];
return [[RCTNavItem alloc] init];
}
RCT_EXPORT_VIEW_PROPERTY(title)

View File

@ -9,8 +9,7 @@
@property (nonatomic, strong) UIView *reactNavSuperviewLink;
@property (nonatomic, assign) NSInteger requestedTopOfStack;
- (instancetype)initWithFrame:(CGRect)frame
eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
/**
* Schedules a JavaScript navigation and prevents `UIKit` from navigating until

View File

@ -126,7 +126,17 @@ NSInteger kNeverProgressed = -10000;
*/
@implementation RCTNavigationController
- (instancetype)init
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass
{
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{
RCT_NOT_DESIGNATED_INITIALIZER();
}
@ -137,7 +147,7 @@ NSInteger kNeverProgressed = -10000;
*/
- (instancetype)initWithScrollCallback:(dispatch_block_t)callback
{
if ((self = [super init])) {
if ((self = [super initWithNibName:nil bundle:nil])) {
_scrollCallback = callback;
}
return self;
@ -265,11 +275,14 @@ NSInteger kNeverProgressed = -10000;
@implementation RCTNavigator
- (id)initWithFrame:(CGRect)frame
eventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (id)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
if ((self = [super initWithFrame:CGRectZero])) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(reportNavigationProgress:)];
_mostRecentProgress = kNeverProgressed;
_dummyView = [[UIView alloc] initWithFrame:CGRectZero];
@ -311,24 +324,15 @@ NSInteger kNeverProgressed = -10000;
return;
}
_mostRecentProgress = nextProgress;
[_eventDispatcher sendRawEventWithType:@"topNavigationProgress"
body:@{@"fromIndex": @(_currentlyTransitioningFrom),
[_eventDispatcher sendEventWithName:@"topNavigationProgress" body:@{
@"fromIndex": @(_currentlyTransitioningFrom),
@"toIndex": @(_currentlyTransitioningTo),
@"progress": @(nextProgress),
@"target": self.reactTag}];
@"target": self.reactTag
}];
}
}
- (instancetype)initWithFrame:(CGRect)frame
{
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)init
{
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (void)dealloc
{
_navigationController.delegate = nil;
@ -438,9 +442,10 @@ NSInteger kNeverProgressed = -10000;
- (void)handleTopOfStackChanged
{
[_eventDispatcher sendRawEventWithType:@"topNavigateBack"
body:@{@"target":self.reactTag,
@"stackLength":@(_navigationController.viewControllers.count)}];
[_eventDispatcher sendEventWithName:@"topNavigateBack" body:@{
@"target":self.reactTag,
@"stackLength":@(_navigationController.viewControllers.count)
}];
}
- (void)dispatchFakeScrollEvent

View File

@ -8,9 +8,9 @@
@implementation RCTNavigatorManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
return [[RCTNavigator alloc] initWithFrame:CGRectZero eventDispatcher:eventDispatcher];
return [[RCTNavigator alloc] initWithEventDispatcher:self.eventDispatcher];
}
RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack)

View File

@ -4,6 +4,7 @@
#import "RCTImageDownloader.h"
#import "RCTUtils.h"
#import "RCTConvert.h"
@implementation RCTNetworkImageView
{
@ -52,6 +53,19 @@
self.layer.contentsScale = _defaultImage.scale;
self.layer.contents = (__bridge id)_defaultImage.CGImage;
}
if ([imageURL.pathExtension caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
_downloadToken = [_imageDownloader downloadDataForURL:imageURL block:^(NSData *data, NSError *error) {
if (data) {
CAKeyframeAnimation *animation = [RCTConvert GIF:data];
CGImageRef firstFrame = (__bridge CGImageRef)animation.values.firstObject;
self.layer.bounds = CGRectMake(0, 0, CGImageGetWidth(firstFrame), CGImageGetHeight(firstFrame));
self.layer.contentsScale = 1.0;
self.layer.contentsGravity = kCAGravityResizeAspect;
[self.layer addAnimation:animation forKey:@"contents"];
}
// TODO: handle errors
}];
} else {
_downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale() block:^(UIImage *image, NSError *error) {
if (image) {
self.layer.contentsScale = image.scale;
@ -61,6 +75,7 @@
}];
}
}
}
- (void)setImageURL:(NSURL *)imageURL
{

View File

@ -11,7 +11,7 @@
@implementation RCTNetworkImageViewManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
RCTNetworkImageView *view = [[RCTNetworkImageView alloc] initWithFrame:CGRectZero imageDownloader:[RCTImageDownloader sharedInstance]];
view.contentMode = UIViewContentModeScaleAspectFill;

View File

@ -6,7 +6,7 @@
@implementation RCTRawTextManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
return [[UIView alloc] init];
}

View File

@ -32,6 +32,6 @@
@property (nonatomic, assign) BOOL centerContent;
@property (nonatomic, copy) NSArray *stickyHeaderIndices;
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
@end

View File

@ -8,6 +8,7 @@
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
#import "UIView+ReactKit.h"
CGFloat const ZINDEX_DEFAULT = 0;
@ -122,6 +123,7 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
*/
- (BOOL)touchesShouldCancelInContentView:(UIView *)view
{
//TODO: shouldn't this call super if _shouldDisableScrollInteraction returns NO?
return ![self _shouldDisableScrollInteraction];
}
@ -260,9 +262,14 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
@synthesize nativeMainScrollDelegate = _nativeMainScrollDelegate;
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
if ((self = [super initWithFrame:CGRectZero])) {
_eventDispatcher = eventDispatcher;
_scrollView = [[RCTCustomScrollView alloc] initWithFrame:CGRectZero];

View File

@ -7,9 +7,9 @@
@implementation RCTScrollViewManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
return [[RCTScrollView alloc] initWithFrame:CGRectZero eventDispatcher:eventDispatcher];
return [[RCTScrollView alloc] initWithEventDispatcher:self.eventDispatcher];
}
RCT_EXPORT_VIEW_PROPERTY(alwaysBounceHorizontal)
@ -36,7 +36,7 @@ RCT_EXPORT_VIEW_PROPERTY(contentInset);
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets);
RCT_EXPORT_VIEW_PROPERTY(contentOffset);
- (NSDictionary *)constantsToExport
+ (NSDictionary *)constantsToExport
{
return
@{

View File

@ -9,14 +9,26 @@
@implementation RCTStaticImageManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
return [[RCTStaticImage alloc] init];
}
RCT_REMAP_VIEW_PROPERTY(src, image)
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode)
- (void)set_src:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
{
if (json) {
if ([json isKindOfClass:[NSString class]] && [[json pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
[view.layer addAnimation:[RCTConvert GIF:json] forKey:@"contents"];
} else {
view.image = [RCTConvert UIImage:json];
}
} else {
view.image = defaultView.image;
}
}
- (void)set_capInsets:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
{
view.capInsets = json ? [RCTConvert UIEdgeInsets:json] : defaultView.capInsets;

View File

@ -10,6 +10,6 @@
@property (nonatomic, assign) BOOL autoCorrect;
@property (nonatomic, assign) UIEdgeInsets paddingEdgeInsets; // TODO: contentInset
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
@end

View File

@ -14,14 +14,14 @@
BOOL _jsRequestingFirstResponder;
}
- (instancetype)init
- (instancetype)initWithFrame:(CGRect)frame
{
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
if ((self = [super initWithFrame:frame])) {
if ((self = [super initWithFrame:CGRectZero])) {
_eventDispatcher = eventDispatcher;
[self addTarget:self action:@selector(_textFieldDidChange) forControlEvents:UIControlEventEditingChanged];

View File

@ -8,9 +8,9 @@
@implementation RCTTextFieldManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
return [[RCTTextField alloc] initWithFrame:CGRectZero eventDispatcher:eventDispatcher];
return [[RCTTextField alloc] initWithEventDispatcher:self.eventDispatcher];
}
RCT_EXPORT_VIEW_PROPERTY(caretHidden)

View File

@ -13,7 +13,7 @@
@implementation RCTTextManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = 0;

View File

@ -6,9 +6,9 @@
@implementation RCTUIActivityIndicatorViewManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
return [[UIActivityIndicatorView alloc] initWithFrame:CGRectZero];
return [[UIActivityIndicatorView alloc] init];
}
RCT_EXPORT_VIEW_PROPERTY(activityIndicatorViewStyle)
@ -28,7 +28,7 @@ RCT_EXPORT_VIEW_PROPERTY(color)
}
}
- (NSDictionary *)constantsToExport
+ (NSDictionary *)constantsToExport
{
return
@{

View File

@ -4,6 +4,12 @@
#import "RCTExport.h"
@class RCTEventDispatcher;
@interface RCTUIViewManager : NSObject <RCTNativeViewModule>
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
@end

View File

@ -3,13 +3,38 @@
#import "RCTUIViewManager.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTShadowView.h"
#import "RCTView.h"
@implementation RCTUIViewManager
{
__weak RCTEventDispatcher *_eventDispatcher;
}
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
if ((self = [super init])) {
_eventDispatcher = eventDispatcher;
}
return self;
}
+ (NSString *)moduleName
{
// Default implementation, works in most cases
NSString *name = NSStringFromClass(self);
if ([name hasPrefix:@"RCTUI"]) {
name = [name substringFromIndex:@"RCT".length];
}
if ([name hasSuffix:@"Manager"]) {
name = [name substringToIndex:name.length - @"Manager".length];
}
return name;
}
- (UIView *)view
{
return [[UIView alloc] init];
}
@ -29,7 +54,7 @@ RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius)
RCT_REMAP_VIEW_PROPERTY(borderColor, layer.borderColor);
RCT_REMAP_VIEW_PROPERTY(borderRadius, layer.cornerRadius)
RCT_REMAP_VIEW_PROPERTY(borderWidth, layer.borderWidth)
RCT_REMAP_VIEW_PROPERTY(transformMatrix, view.layer.transform)
RCT_REMAP_VIEW_PROPERTY(transformMatrix, layer.transform)
- (void)set_overflow:(id)json
forView:(UIView *)view

View File

@ -6,12 +6,11 @@
@implementation RCTViewManager
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
- (UIView *)view
{
return [[RCTView alloc] init];
}
RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel)
RCT_EXPORT_VIEW_PROPERTY(pointerEvents)
@end

View File

@ -96,7 +96,7 @@
- (void)rightButtonTapped
{
[_eventDispatcher sendRawEventWithType:@"topNavRightButtonTap" body:@{@"target":_navItem.reactTag}];
[_eventDispatcher sendEventWithName:@"topNavRightButtonTap" body:@{@"target":_navItem.reactTag}];
}
- (void)didMoveToParentViewController:(UIViewController *)parent