mirror of
https://github.com/status-im/react-native.git
synced 2025-02-19 20:58:22 +00:00
[WIP] Migrated View Managers over to new architecture
This commit is contained in:
parent
33290fb1e2
commit
3b11b9d6c3
@ -7,46 +7,46 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1316A21A1AA397CA00C0188E /* libRCTNetworkImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1316A2101AA3871A00C0188E /* libRCTNetworkImage.a */; };
|
||||
1316A21B1AA397CA00C0188E /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1316A2081AA386C700C0188E /* libRCTText.a */; };
|
||||
1316A21C1AA397CA00C0188E /* libReactKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1316A2171AA3875D00C0188E /* libReactKit.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 */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
587651361A9EB175008B8F17 /* libRCTNetworkImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 587651281A9EB168008B8F17 /* libRCTNetworkImage.a */; };
|
||||
587651371A9EB175008B8F17 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 587651301A9EB168008B8F17 /* libRCTText.a */; };
|
||||
587651381A9EB175008B8F17 /* libReactKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 587651351A9EB168008B8F17 /* libReactKit.a */; };
|
||||
587651571A9F862F008B8F17 /* libRCTDataManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5876514F1A9F8619008B8F17 /* libRCTDataManager.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
587651271A9EB168008B8F17 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
|
||||
remoteInfo = RCTNetworkImage;
|
||||
};
|
||||
587651291A9EB168008B8F17 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 58B511681A9E6B3D00147676;
|
||||
remoteInfo = RCTNetworkImageTests;
|
||||
};
|
||||
5876512F1A9EB168008B8F17 /* PBXContainerItemProxy */ = {
|
||||
1316A2071AA386C700C0188E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 58B5119B1A9E6C1200147676;
|
||||
remoteInfo = RCTText;
|
||||
};
|
||||
587651311A9EB168008B8F17 /* PBXContainerItemProxy */ = {
|
||||
1316A2091AA386C700C0188E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 58B511A61A9E6C1300147676;
|
||||
remoteInfo = RCTTextTests;
|
||||
};
|
||||
587651341A9EB168008B8F17 /* PBXContainerItemProxy */ = {
|
||||
1316A20F1AA3871A00C0188E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
|
||||
remoteInfo = RCTNetworkImage;
|
||||
};
|
||||
1316A2111AA3871A00C0188E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 58B511681A9E6B3D00147676;
|
||||
remoteInfo = RCTNetworkImageTests;
|
||||
};
|
||||
1316A2161AA3875D00C0188E /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 587651221A9EB168008B8F17 /* ReactKit.xcodeproj */;
|
||||
proxyType = 2;
|
||||
@ -77,9 +77,9 @@
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetworkImage.xcodeproj; path = "/Users/sahrens/src/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/RCTStandardLibrary/../Image/RCTNetworkImage.xcodeproj"; sourceTree = "<absolute>"; };
|
||||
5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "/Users/sahrens/src/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/RCTStandardLibrary/../Text/RCTText.xcodeproj"; sourceTree = "<absolute>"; };
|
||||
587651221A9EB168008B8F17 /* ReactKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactKit.xcodeproj; path = "/Users/sahrens/src/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/RCTStandardLibrary/../../ReactKit/ReactKit.xcodeproj"; sourceTree = "<absolute>"; };
|
||||
5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetworkImage.xcodeproj; path = ../../Libraries/Image/RCTNetworkImage.xcodeproj; sourceTree = SOURCE_ROOT; };
|
||||
5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = SOURCE_ROOT; };
|
||||
587651221A9EB168008B8F17 /* ReactKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactKit.xcodeproj; path = ../../ReactKit/ReactKit.xcodeproj; sourceTree = SOURCE_ROOT; };
|
||||
587651491A9F8619008B8F17 /* RCTDataManager.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTDataManager.xcodeproj; path = ../../Libraries/Network/RCTDataManager.xcodeproj; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -88,16 +88,53 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1316A21A1AA397CA00C0188E /* libRCTNetworkImage.a in Frameworks */,
|
||||
1316A21B1AA397CA00C0188E /* libRCTText.a in Frameworks */,
|
||||
1316A21C1AA397CA00C0188E /* libReactKit.a in Frameworks */,
|
||||
587651571A9F862F008B8F17 /* libRCTDataManager.a in Frameworks */,
|
||||
587651361A9EB175008B8F17 /* libRCTNetworkImage.a in Frameworks */,
|
||||
587651371A9EB175008B8F17 /* libRCTText.a in Frameworks */,
|
||||
587651381A9EB175008B8F17 /* libReactKit.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1316A2031AA386C700C0188E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1316A2081AA386C700C0188E /* libRCTText.a */,
|
||||
1316A20A1AA386C700C0188E /* RCTTextTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1316A20B1AA3871A00C0188E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1316A2101AA3871A00C0188E /* libRCTNetworkImage.a */,
|
||||
1316A2121AA3871A00C0188E /* RCTNetworkImageTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1316A2131AA3875D00C0188E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1316A2171AA3875D00C0188E /* libReactKit.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1316A21D1AA397F400C0188E /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
587651221A9EB168008B8F17 /* ReactKit.xcodeproj */,
|
||||
587651491A9F8619008B8F17 /* RCTDataManager.xcodeproj */,
|
||||
5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */,
|
||||
5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
13B07FAE1A68108700A75B9A /* UIExplorer */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -111,32 +148,6 @@
|
||||
name = UIExplorer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5876511D1A9EB168008B8F17 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
587651281A9EB168008B8F17 /* libRCTNetworkImage.a */,
|
||||
5876512A1A9EB168008B8F17 /* RCTNetworkImageTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
587651201A9EB168008B8F17 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
587651301A9EB168008B8F17 /* libRCTText.a */,
|
||||
587651321A9EB168008B8F17 /* RCTTextTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
587651231A9EB168008B8F17 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
587651351A9EB168008B8F17 /* libReactKit.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5876514A1A9F8619008B8F17 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -150,10 +161,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FAE1A68108700A75B9A /* UIExplorer */,
|
||||
587651491A9F8619008B8F17 /* RCTDataManager.xcodeproj */,
|
||||
5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */,
|
||||
5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */,
|
||||
587651221A9EB168008B8F17 /* ReactKit.xcodeproj */,
|
||||
1316A21D1AA397F400C0188E /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
@ -212,15 +220,15 @@
|
||||
ProjectRef = 587651491A9F8619008B8F17 /* RCTDataManager.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 5876511D1A9EB168008B8F17 /* Products */;
|
||||
ProductGroup = 1316A20B1AA3871A00C0188E /* Products */;
|
||||
ProjectRef = 5876511C1A9EB168008B8F17 /* RCTNetworkImage.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 587651201A9EB168008B8F17 /* Products */;
|
||||
ProductGroup = 1316A2031AA386C700C0188E /* Products */;
|
||||
ProjectRef = 5876511F1A9EB168008B8F17 /* RCTText.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 587651231A9EB168008B8F17 /* Products */;
|
||||
ProductGroup = 1316A2131AA3875D00C0188E /* Products */;
|
||||
ProjectRef = 587651221A9EB168008B8F17 /* ReactKit.xcodeproj */;
|
||||
},
|
||||
);
|
||||
@ -232,39 +240,39 @@
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
587651281A9EB168008B8F17 /* libRCTNetworkImage.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTNetworkImage.a;
|
||||
remoteRef = 587651271A9EB168008B8F17 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5876512A1A9EB168008B8F17 /* RCTNetworkImageTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = RCTNetworkImageTests.xctest;
|
||||
remoteRef = 587651291A9EB168008B8F17 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
587651301A9EB168008B8F17 /* libRCTText.a */ = {
|
||||
1316A2081AA386C700C0188E /* libRCTText.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTText.a;
|
||||
remoteRef = 5876512F1A9EB168008B8F17 /* PBXContainerItemProxy */;
|
||||
remoteRef = 1316A2071AA386C700C0188E /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
587651321A9EB168008B8F17 /* RCTTextTests.xctest */ = {
|
||||
1316A20A1AA386C700C0188E /* RCTTextTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = RCTTextTests.xctest;
|
||||
remoteRef = 587651311A9EB168008B8F17 /* PBXContainerItemProxy */;
|
||||
remoteRef = 1316A2091AA386C700C0188E /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
587651351A9EB168008B8F17 /* libReactKit.a */ = {
|
||||
1316A2101AA3871A00C0188E /* libRCTNetworkImage.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTNetworkImage.a;
|
||||
remoteRef = 1316A20F1AA3871A00C0188E /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
1316A2121AA3871A00C0188E /* RCTNetworkImageTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = RCTNetworkImageTests.xctest;
|
||||
remoteRef = 1316A2111AA3871A00C0188E /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
1316A2171AA3875D00C0188E /* libReactKit.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libReactKit.a;
|
||||
remoteRef = 587651341A9EB168008B8F17 /* PBXContainerItemProxy */;
|
||||
remoteRef = 1316A2161AA3875D00C0188E /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
5876514F1A9F8619008B8F17 /* libRCTDataManager.a */ = {
|
||||
|
@ -8,7 +8,7 @@
|
||||
var EventEmitter = require('EventEmitter');
|
||||
var React = require('React');
|
||||
var ReactIOSViewAttributes = require('ReactIOSViewAttributes');
|
||||
var { RKUIManager } = require('NativeModules');
|
||||
var { RKNavigatorManager } = require('NativeModules');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var StaticContainer = require('StaticContainer.react');
|
||||
var View = require('View');
|
||||
@ -127,7 +127,7 @@ var RKNavigatorItem = createReactIOSNativeComponentClass({
|
||||
|
||||
var NavigatorTransitionerIOS = React.createClass({
|
||||
requestSchedulingNavigation: function(cb) {
|
||||
RKUIManager.requestSchedulingJavaScriptNavigation(
|
||||
RKNavigatorManager.requestSchedulingJavaScriptNavigation(
|
||||
this.getNodeHandle(),
|
||||
logError,
|
||||
cb
|
||||
|
@ -6,7 +6,7 @@
|
||||
'use strict';
|
||||
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var RCTLocationObserver = require('NativeModules').RCTLocationObserver;
|
||||
var RCTLocationObserver = require('NativeModules').RKLocationObserver;
|
||||
|
||||
var invariant = require('invariant');
|
||||
var logError = require('logError');
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
@interface RCTNetworkImageView : UIView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame imageDownloader:(RCTImageDownloader *)imageDownloader;
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
imageDownloader:(RCTImageDownloader *)imageDownloader NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* An image that will appear while the view is loading the image from the network,
|
||||
|
@ -27,11 +27,6 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (NSURL *)imageURL
|
||||
{
|
||||
// We clear our backing layer's imageURL when we are not in a window for a while,
|
||||
|
@ -65,8 +65,7 @@
|
||||
|
||||
} else {
|
||||
|
||||
RCTLogMustFix(@"unsupported query type %@", queryType);
|
||||
return;
|
||||
RCTLogError(@"unsupported query type %@", queryType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,6 +269,10 @@
|
||||
/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 = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -283,6 +287,10 @@
|
||||
/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 = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
@ -24,17 +24,7 @@
|
||||
}
|
||||
|
||||
RCT_REMAP_VIEW_PROPERTY(containerBackgroundColor, backgroundColor)
|
||||
|
||||
- (void)set_textAlign:(id)json
|
||||
forShadowView:(RCTShadowText *)shadowView
|
||||
withDefaultView:(RCTShadowText *)defaultView
|
||||
{
|
||||
shadowView.textAlign = json ? [RCTConvert NSTextAlignment:json] : defaultView.textAlign;
|
||||
}
|
||||
|
||||
- (void)set_numberOfLines:(id)json
|
||||
forView:(RCTText *)view
|
||||
withDefaultView:(RCTText *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(numberOfLines, RCTText *)
|
||||
{
|
||||
NSLineBreakMode truncationMode = NSLineBreakByClipping;
|
||||
view.numberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.numberOfLines;
|
||||
@ -44,31 +34,33 @@ RCT_REMAP_VIEW_PROPERTY(containerBackgroundColor, backgroundColor)
|
||||
view.lineBreakMode = truncationMode;
|
||||
}
|
||||
|
||||
- (void)set_numberOfLines:(id)json
|
||||
forShadowView:(RCTShadowText *)shadowView
|
||||
withDefaultView:(RCTShadowText *)defaultView
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(backgroundColor, RCTShadowText *)
|
||||
{
|
||||
view.textBackgroundColor = json ? [RCTConvert UIColor:json] : defaultView.textBackgroundColor;
|
||||
}
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(containerBackgroundColor, RCTShadowText *)
|
||||
{
|
||||
view.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor;
|
||||
view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
}
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(numberOfLines, RCTShadowText *)
|
||||
{
|
||||
NSLineBreakMode truncationMode = NSLineBreakByClipping;
|
||||
shadowView.maxNumberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.maxNumberOfLines;
|
||||
if (shadowView.maxNumberOfLines > 0) {
|
||||
view.maxNumberOfLines = json ? [RCTConvert NSInteger:json] : defaultView.maxNumberOfLines;
|
||||
if (view.maxNumberOfLines > 0) {
|
||||
truncationMode = NSLineBreakByTruncatingTail;
|
||||
}
|
||||
shadowView.truncationMode = truncationMode;
|
||||
view.truncationMode = truncationMode;
|
||||
}
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(textAlign, RCTShadowText *)
|
||||
{
|
||||
view.textAlign = json ? [RCTConvert NSTextAlignment:json] : defaultView.textAlign;
|
||||
}
|
||||
|
||||
- (void)set_backgroundColor:(id)json
|
||||
forShadowView:(RCTShadowText *)shadowView
|
||||
withDefaultView:(RCTShadowText *)defaultView
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowText *)shadowView
|
||||
{
|
||||
shadowView.textBackgroundColor = json ? [RCTConvert UIColor:json] : defaultView.textBackgroundColor;
|
||||
}
|
||||
|
||||
- (void)set_containerBackgroundColor:(id)json
|
||||
forShadowView:(RCTShadowText *)shadowView
|
||||
withDefaultView:(RCTShadowText *)defaultView
|
||||
{
|
||||
shadowView.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor;
|
||||
shadowView.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
//TODO: This could be a cleaner replacement for uiBlockToAmendWithShadowViewRegistry
|
||||
return nil;
|
||||
}
|
||||
|
||||
// TODO: the purpose of this block is effectively just to copy properties from the shadow views
|
||||
|
@ -6,32 +6,16 @@
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTRootView;
|
||||
|
||||
/**
|
||||
* Utilities for constructing common response objects. When sending a
|
||||
* systemError back to JS, it's important to describe whether or not it was a
|
||||
* system error, or API usage error. System errors should never happen and are
|
||||
* therefore logged using `RCTLogError()`. API usage errors are expected if the
|
||||
* API is misused and will therefore not be logged using `RCTLogError()`. The JS
|
||||
* application code is expected to handle them. Regardless of type, each error
|
||||
* should be logged at most once.
|
||||
*/
|
||||
static inline NSDictionary *RCTSystemErrorObject(NSString *msg)
|
||||
{
|
||||
return @{@"systemError": msg ?: @""};
|
||||
}
|
||||
|
||||
static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
|
||||
{
|
||||
return @{@"apiError": msg ?: @""};
|
||||
}
|
||||
|
||||
/**
|
||||
* This block can be used to instantiate modules that require additional
|
||||
* init parameters, or additional configuration prior to being used.
|
||||
* The bridge will call this block to instatiate the modules, and will
|
||||
* be responsible for invalidating/releasing them when the bridge is destroyed.
|
||||
* For this reason, the block should always return new module instances, and
|
||||
* module instances should not be shared between bridges.
|
||||
*/
|
||||
typedef NSArray *(^RCTBridgeModuleProviderBlock)(RCTBridge *bridge);
|
||||
typedef NSArray *(^RCTBridgeModuleProviderBlock)(void);
|
||||
|
||||
/**
|
||||
* Async batched bridge used to communicate with the JavaScript application.
|
||||
@ -42,12 +26,12 @@ typedef NSArray *(^RCTBridgeModuleProviderBlock)(RCTBridge *bridge);
|
||||
* The designated initializer. This creates a new bridge on top of the specified
|
||||
* executor. The bridge should then be used for all subsequent communication
|
||||
* with the JavaScript code running in the executor. Modules will be automatically
|
||||
* instantiated using the default contructor, but you can optionally pass in a
|
||||
* module provider block to manually instantiate modules that require additional
|
||||
* init parameters or configuration.
|
||||
* instantiated using the default contructor, but you can optionally pass in an
|
||||
* array of pre-initialized module instances if they require additional init
|
||||
* parameters or configuration.
|
||||
*/
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
moduleProvider:(RCTBridgeModuleProviderBlock)block NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithExecutor:(id<RCTJavaScriptExecutor>)executor
|
||||
moduleProvider:(RCTBridgeModuleProviderBlock)block NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* This method is used to call functions in the JavaScript application context.
|
||||
@ -81,16 +65,6 @@ typedef NSArray *(^RCTBridgeModuleProviderBlock)(RCTBridge *bridge);
|
||||
*/
|
||||
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
|
||||
|
||||
// For use in implementing delegates, which may need to queue responses.
|
||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;
|
||||
|
||||
/**
|
||||
* Register a root view with the bridge. Theorectically, a single bridge can
|
||||
* support multiple root views, however this feature is not currently exposed
|
||||
* and may eventually be removed.
|
||||
*/
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
|
||||
/**
|
||||
* Global logging function that will print to both xcode and JS debugger consoles.
|
||||
*
|
||||
|
@ -3,11 +3,12 @@
|
||||
#import "RCTBridge.h"
|
||||
|
||||
#import <dlfcn.h>
|
||||
#import <mach-o/getsect.h>
|
||||
#import <mach-o/dyld.h>
|
||||
#import <objc/message.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import <mach-o/dyld.h>
|
||||
#import <mach-o/getsect.h>
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
@ -26,60 +27,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
||||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
|
||||
/**
|
||||
* This private class is used as a container for exported method info
|
||||
*/
|
||||
@interface RCTModuleMethod : NSObject
|
||||
|
||||
@property (readonly, nonatomic, assign) SEL selector;
|
||||
@property (readonly, nonatomic, copy) NSString *JSMethodName;
|
||||
@property (readonly, nonatomic, assign) NSUInteger arity;
|
||||
@property (readonly, nonatomic, copy) NSIndexSet *blockArgumentIndexes;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTModuleMethod
|
||||
|
||||
- (instancetype)initWithSelector:(SEL)selector
|
||||
JSMethodName:(NSString *)JSMethodName
|
||||
arity:(NSUInteger)arity
|
||||
blockArgumentIndexes:(NSIndexSet *)blockArgumentIndexes
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_selector = selector;
|
||||
_JSMethodName = [JSMethodName copy];
|
||||
_arity = arity;
|
||||
_blockArgumentIndexes = [blockArgumentIndexes copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
NSString *blocks = @"no block args";
|
||||
if (self.blockArgumentIndexes.count > 0) {
|
||||
NSMutableString *indexString = [NSMutableString string];
|
||||
[self.blockArgumentIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||
[indexString appendFormat:@", %tu", idx];
|
||||
}];
|
||||
blocks = [NSString stringWithFormat:@"block args at %@", [indexString substringFromIndex:2]];
|
||||
}
|
||||
|
||||
return [NSString stringWithFormat:@"<%@: %p; exports -%@ as %@; %@>", NSStringFromClass(self.class), self, NSStringFromSelector(self.selector), self.JSMethodName, blocks];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __LP64__
|
||||
typedef uint64_t RCTExportValue;
|
||||
typedef struct section_64 RCTExportSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader_64
|
||||
#else
|
||||
typedef uint32_t RCTExportValue;
|
||||
typedef struct section RCTExportSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This function returns the module name for a given class.
|
||||
*/
|
||||
@ -88,18 +35,6 @@ static NSString *RCTModuleNameForClass(Class cls)
|
||||
return [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function instantiates a new module instance.
|
||||
*/
|
||||
static id<RCTBridgeModule> RCTCreateModuleInstance(Class cls, RCTBridge *bridge)
|
||||
{
|
||||
if ([cls instancesRespondToSelector:@selector(initWithBridge:)]) {
|
||||
return [[cls alloc] initWithBridge:bridge];
|
||||
} else {
|
||||
return [[cls alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function scans all classes available at runtime and returns an array
|
||||
* of all JSMethods registered.
|
||||
@ -111,22 +46,11 @@ static NSArray *RCTJSMethods(void)
|
||||
dispatch_once(&onceToken, ^{
|
||||
NSMutableSet *uniqueMethods = [NSMutableSet set];
|
||||
|
||||
unsigned int classCount;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
|
||||
Class cls = classes[i];
|
||||
|
||||
if (!class_getSuperclass(cls)) {
|
||||
// Class has no superclass - it's probably something weird
|
||||
continue;
|
||||
}
|
||||
|
||||
RCTEnumerateClasses(^(__unsafe_unretained Class cls) {
|
||||
if (RCTClassOverridesClassMethod(cls, @selector(JSMethods))) {
|
||||
[uniqueMethods addObjectsFromArray:[cls JSMethods]];
|
||||
}
|
||||
}
|
||||
free(classes);
|
||||
});
|
||||
|
||||
JSMethods = [uniqueMethods allObjects];
|
||||
});
|
||||
@ -147,38 +71,216 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
modules = [NSMutableArray array];
|
||||
RCTModuleNamesByID = [NSMutableArray array];
|
||||
|
||||
unsigned int classCount;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
RCTEnumerateClasses(^(__unsafe_unretained Class cls) {
|
||||
if ([cls conformsToProtocol:@protocol(RCTBridgeModule)]) {
|
||||
|
||||
Class cls = classes[i];
|
||||
// Add module
|
||||
[(NSMutableArray *)modules addObject:cls];
|
||||
|
||||
if (!class_getSuperclass(cls)) {
|
||||
// Class has no superclass - it's probably something weird
|
||||
continue;
|
||||
// Add module name
|
||||
NSString *moduleName = RCTModuleNameForClass(cls);
|
||||
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
||||
}
|
||||
|
||||
if (![cls conformsToProtocol:@protocol(RCTBridgeModule)]) {
|
||||
// Not an RCTBridgeModule
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add module
|
||||
[(NSMutableArray *)modules addObject:cls];
|
||||
|
||||
// Add module name
|
||||
NSString *moduleName = RCTModuleNameForClass(cls);
|
||||
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
||||
}
|
||||
free(classes);
|
||||
});
|
||||
|
||||
modules = [modules copy];
|
||||
RCTModuleNamesByID = [RCTModuleNamesByID copy];
|
||||
});
|
||||
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
@interface RCTBridge ()
|
||||
|
||||
- (void)_invokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This private class is used as a container for exported method info
|
||||
*/
|
||||
@interface RCTModuleMethod : NSObject
|
||||
|
||||
@property (nonatomic, copy, readonly) NSString *moduleClassName;
|
||||
@property (nonatomic, copy, readonly) NSString *JSMethodName;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTModuleMethod
|
||||
{
|
||||
BOOL _isClassMethod;
|
||||
Class _moduleClass;
|
||||
SEL _selector;
|
||||
NSMethodSignature *_methodSignature;
|
||||
NSArray *_argumentBlocks;
|
||||
NSString *_methodName;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMethodName:(NSString *)methodName
|
||||
JSMethodName:(NSString *)JSMethodName
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
_methodName = methodName;
|
||||
NSArray *parts = [[methodName substringWithRange:NSMakeRange(2, methodName.length - 3)] componentsSeparatedByString:@" "];
|
||||
|
||||
// Parse class and method
|
||||
_moduleClassName = parts[0];
|
||||
NSRange categoryRange = [_moduleClassName rangeOfString:@"("];
|
||||
if (categoryRange.length)
|
||||
{
|
||||
_moduleClassName = [_moduleClassName substringToIndex:categoryRange.location];
|
||||
}
|
||||
|
||||
// Extract class and method details
|
||||
_isClassMethod = [methodName characterAtIndex:0] == '+';
|
||||
_moduleClass = NSClassFromString(_moduleClassName);
|
||||
_selector = NSSelectorFromString(parts[1]);
|
||||
_JSMethodName = JSMethodName ?: [NSStringFromSelector(_selector) componentsSeparatedByString:@":"][0];
|
||||
|
||||
#if DEBUG
|
||||
|
||||
// Sanity check
|
||||
RCTAssert([_moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
||||
@"You are attempting to export the method %@, but %@ does not \
|
||||
conform to the RCTBridgeModule Protocol", methodName, _moduleClassName);
|
||||
#endif
|
||||
|
||||
// Get method signature
|
||||
_methodSignature = _isClassMethod ?
|
||||
[_moduleClass methodSignatureForSelector:_selector] :
|
||||
[_moduleClass instanceMethodSignatureForSelector:_selector];
|
||||
|
||||
// Process arguments
|
||||
NSUInteger numberOfArguments = _methodSignature.numberOfArguments;
|
||||
NSMutableArray *argumentBlocks = [[NSMutableArray alloc] initWithCapacity:numberOfArguments - 2];
|
||||
for (NSUInteger i = 2; i < numberOfArguments; i++) {
|
||||
const char *argumentType = [_methodSignature getArgumentTypeAtIndex:i];
|
||||
switch (argumentType[0]) {
|
||||
|
||||
#define RCT_ARG_BLOCK(_logic) \
|
||||
[argumentBlocks addObject:^(RCTBridge *bridge, NSInvocation *invocation, NSUInteger index, id json) { \
|
||||
_logic \
|
||||
[invocation setArgument:&value atIndex:index]; \
|
||||
}]; \
|
||||
|
||||
#define RCT_CASE(_value, _class, _logic) \
|
||||
case _value: { \
|
||||
RCT_ARG_BLOCK( \
|
||||
if (json && ![json isKindOfClass:[_class class]]) { \
|
||||
RCTLogError(@"Argument %tu (%@) of %@.%@ should be of type %@", index, \
|
||||
json, RCTModuleNameForClass(_moduleClass), _JSMethodName, [_class class]); \
|
||||
return; \
|
||||
} \
|
||||
_logic \
|
||||
) \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_CASE(':', NSString, SEL value = NSSelectorFromString(json); );
|
||||
RCT_CASE('*', NSString, const char *value = [json UTF8String]; );
|
||||
|
||||
#define RCT_SIMPLE_CASE(_value, _type, _selector) \
|
||||
case _value: { \
|
||||
RCT_ARG_BLOCK( \
|
||||
if (json && ![json respondsToSelector:@selector(_selector)]) { \
|
||||
RCTLogError(@"Argument %tu (%@) of %@.%@ does not respond to selector: %@", \
|
||||
index, json, RCTModuleNameForClass(_moduleClass), _JSMethodName, @#_selector); \
|
||||
return; \
|
||||
} \
|
||||
_type value = [json _selector]; \
|
||||
) \
|
||||
break; \
|
||||
}
|
||||
|
||||
RCT_SIMPLE_CASE('c', char, charValue)
|
||||
RCT_SIMPLE_CASE('C', unsigned char, unsignedCharValue)
|
||||
RCT_SIMPLE_CASE('s', short, shortValue)
|
||||
RCT_SIMPLE_CASE('S', unsigned short, unsignedShortValue)
|
||||
RCT_SIMPLE_CASE('i', int, intValue)
|
||||
RCT_SIMPLE_CASE('I', unsigned int, unsignedIntValue)
|
||||
RCT_SIMPLE_CASE('l', long, longValue)
|
||||
RCT_SIMPLE_CASE('L', unsigned long, unsignedLongValue)
|
||||
RCT_SIMPLE_CASE('q', long long, longLongValue)
|
||||
RCT_SIMPLE_CASE('Q', unsigned long long, unsignedLongLongValue)
|
||||
RCT_SIMPLE_CASE('f', float, floatValue)
|
||||
RCT_SIMPLE_CASE('d', double, doubleValue)
|
||||
RCT_SIMPLE_CASE('B', BOOL, boolValue)
|
||||
|
||||
default: {
|
||||
static const char *blockType = @encode(typeof(^{}));
|
||||
if (!strcmp(argumentType, blockType)) {
|
||||
RCT_ARG_BLOCK(
|
||||
if (json && ![json isKindOfClass:[NSNumber class]]) {
|
||||
RCTLogError(@"Argument %tu (%@) of %@.%@ should be a number", index,
|
||||
json, RCTModuleNameForClass(_moduleClass), _JSMethodName);
|
||||
return;
|
||||
}
|
||||
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||
__autoreleasing id value = (json ? ^(NSArray *args) {
|
||||
[bridge _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||
arguments:@[json, args]];
|
||||
} : ^(NSArray *unused) {});
|
||||
)
|
||||
} else {
|
||||
RCT_ARG_BLOCK( id value = json; )
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_argumentBlocks = [argumentBlocks copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)invokeWithBridge:(RCTBridge *)bridge
|
||||
module:(id)module
|
||||
arguments:(NSArray *)arguments
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
|
||||
// Sanity check
|
||||
RCTAssert([module class] == _moduleClass, @"Attempted to invoke method \
|
||||
%@ on a module of class %@", _methodName, [module class]);
|
||||
#endif
|
||||
|
||||
// Safety check
|
||||
if (arguments.count != _argumentBlocks.count) {
|
||||
RCTLogError(@"%@.%@ was called with %zd arguments, but expects %zd",
|
||||
RCTModuleNameForClass(_moduleClass), _JSMethodName,
|
||||
arguments.count, _argumentBlocks.count);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create invocation (we can't re-use this as it wouldn't be thread-safe)
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:_methodSignature];
|
||||
[invocation setArgument:&_selector atIndex:1];
|
||||
|
||||
// Set arguments
|
||||
NSUInteger index = 0;
|
||||
for (id json in arguments) {
|
||||
id arg = (json == [NSNull null]) ? nil : json;
|
||||
void (^block)(RCTBridge *, NSInvocation *, NSUInteger, id) = _argumentBlocks[index];
|
||||
block(bridge, invocation, index + 2, arg);
|
||||
index ++;
|
||||
}
|
||||
|
||||
// Invoke method
|
||||
[invocation invokeWithTarget:_isClassMethod ? [module class] : module];
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: %p; exports %@ as %@;>", NSStringFromClass(self.class), self, _methodName, _JSMethodName];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This function parses the exported methods inside RCTBridgeModules and
|
||||
* generates an array of arrays of RCTModuleMethod objects, keyed
|
||||
@ -193,6 +295,16 @@ static RCTSparseArray *RCTExportedMethodsByModuleID(void)
|
||||
Dl_info info;
|
||||
dladdr(&RCTExportedMethodsByModuleID, &info);
|
||||
|
||||
#ifdef __LP64__
|
||||
typedef uint64_t RCTExportValue;
|
||||
typedef struct section_64 RCTExportSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader_64
|
||||
#else
|
||||
typedef uint32_t RCTExportValue;
|
||||
typedef struct section RCTExportSection;
|
||||
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
||||
#endif
|
||||
|
||||
const RCTExportValue mach_header = (RCTExportValue)info.dli_fbase;
|
||||
const RCTExportSection *section = RCTGetSectByNameFromHeader((void *)mach_header, "__DATA", "RCTExport");
|
||||
|
||||
@ -202,53 +314,23 @@ static RCTSparseArray *RCTExportedMethodsByModuleID(void)
|
||||
|
||||
NSArray *classes = RCTBridgeModuleClassesByModuleID();
|
||||
NSMutableDictionary *methodsByModuleClassName = [NSMutableDictionary dictionaryWithCapacity:[classes count]];
|
||||
NSCharacterSet *plusMinusCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"+-"];
|
||||
|
||||
for (RCTExportValue addr = section->offset;
|
||||
addr < section->offset + section->size;
|
||||
addr += sizeof(id) * 2) {
|
||||
addr += sizeof(const char **) * 2) {
|
||||
|
||||
const char **entry = (const char **)(mach_header + addr);
|
||||
NSScanner *scanner = [NSScanner scannerWithString:@(entry[0])];
|
||||
// Get data entry
|
||||
const char **entries = (const char **)(mach_header + addr);
|
||||
|
||||
NSString *plusMinus;
|
||||
if (![scanner scanCharactersFromSet:plusMinusCharacterSet intoString:&plusMinus]) continue;
|
||||
if (![scanner scanString:@"[" intoString:NULL]) continue;
|
||||
|
||||
NSString *className;
|
||||
if (![scanner scanUpToString:@" " intoString:&className]) continue;
|
||||
[scanner scanString:@" " intoString:NULL];
|
||||
|
||||
NSString *selectorName;
|
||||
if (![scanner scanUpToString:@"]" intoString:&selectorName]) continue;
|
||||
|
||||
Class moduleClass = NSClassFromString(className);
|
||||
if (moduleClass == Nil) continue;
|
||||
|
||||
SEL selector = NSSelectorFromString(selectorName);
|
||||
Method method = ([plusMinus characterAtIndex:0] == '+' ? class_getClassMethod : class_getInstanceMethod)(moduleClass, selector);
|
||||
if (method == nil) continue;
|
||||
|
||||
unsigned int argumentCount = method_getNumberOfArguments(method);
|
||||
NSMutableIndexSet *blockArgumentIndexes = [NSMutableIndexSet indexSet];
|
||||
static const char *blockType = @encode(typeof(^{}));
|
||||
for (unsigned int i = 2; i < argumentCount; i++) {
|
||||
char *type = method_copyArgumentType(method, i);
|
||||
if (!strcmp(type, blockType)) {
|
||||
[blockArgumentIndexes addIndex:i - 2];
|
||||
}
|
||||
free(type);
|
||||
}
|
||||
|
||||
NSString *JSMethodName = strlen(entry[1]) ? @(entry[1]) : [NSStringFromSelector(selector) componentsSeparatedByString:@":"][0];
|
||||
// Create method
|
||||
RCTModuleMethod *moduleMethod =
|
||||
[[RCTModuleMethod alloc] initWithSelector:selector
|
||||
JSMethodName:JSMethodName
|
||||
arity:method_getNumberOfArguments(method) - 2
|
||||
blockArgumentIndexes:blockArgumentIndexes];
|
||||
[[RCTModuleMethod alloc] initWithMethodName:@(entries[0])
|
||||
JSMethodName:strlen(entries[1]) ? @(entries[1]) : nil];
|
||||
|
||||
NSArray *methods = methodsByModuleClassName[className];
|
||||
methodsByModuleClassName[className] = methods ? [methods arrayByAddingObject:moduleMethod] : @[moduleMethod];
|
||||
// Cache method
|
||||
NSArray *methods = methodsByModuleClassName[moduleMethod.moduleClassName];
|
||||
methodsByModuleClassName[moduleMethod.moduleClassName] =
|
||||
methods ? [methods arrayByAddingObject:moduleMethod] : @[moduleMethod];
|
||||
}
|
||||
|
||||
methodsByModuleID = [[RCTSparseArray alloc] initWithCapacity:[classes count]];
|
||||
@ -256,7 +338,7 @@ static RCTSparseArray *RCTExportedMethodsByModuleID(void)
|
||||
methodsByModuleID[moduleID] = methodsByModuleClassName[NSStringFromClass(moduleClass)];
|
||||
}];
|
||||
});
|
||||
|
||||
|
||||
return methodsByModuleID;
|
||||
}
|
||||
|
||||
@ -303,19 +385,12 @@ static NSDictionary *RCTRemoteModulesConfig(NSDictionary *modulesByName)
|
||||
@"type": @"remote",
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
NSDictionary *module = @{
|
||||
@"moduleID": @(moduleID),
|
||||
@"methods": methodsByName
|
||||
};
|
||||
|
||||
// Add static constants
|
||||
if (RCTClassOverridesClassMethod(moduleClass, @selector(constantsToExport))) {
|
||||
NSMutableDictionary *mutableModule = [module mutableCopy];
|
||||
mutableModule[@"constants"] = [moduleClass constantsToExport] ?: @{};
|
||||
module = [mutableModule copy];
|
||||
}
|
||||
|
||||
remoteModuleConfigByClassName[NSStringFromClass(moduleClass)] = module;
|
||||
}];
|
||||
});
|
||||
@ -324,14 +399,15 @@ static NSDictionary *RCTRemoteModulesConfig(NSDictionary *modulesByName)
|
||||
NSMutableDictionary *moduleConfig = [[NSMutableDictionary alloc] init];
|
||||
[modulesByName enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, id<RCTBridgeModule> module, BOOL *stop) {
|
||||
|
||||
// Add "psuedo-constants"
|
||||
// Add constants
|
||||
NSMutableDictionary *config = remoteModuleConfigByClassName[NSStringFromClass([module class])];
|
||||
if (RCTClassOverridesInstanceMethod([module class], @selector(constantsToExport))) {
|
||||
NSMutableDictionary *mutableConfig = [NSMutableDictionary dictionaryWithDictionary:config];
|
||||
NSMutableDictionary *mutableConstants = [NSMutableDictionary dictionaryWithDictionary:config[@"constants"]];
|
||||
[mutableConstants addEntriesFromDictionary:[module constantsToExport]];
|
||||
mutableConfig[@"constants"] = mutableConstants; // There's no real need to copy this
|
||||
config = mutableConfig; // Nor this - receiver is unlikely to mutate it
|
||||
if ([module respondsToSelector:@selector(constantsToExport)]) {
|
||||
NSDictionary *constants = [module constantsToExport];
|
||||
if (constants) {
|
||||
NSMutableDictionary *mutableConfig = [NSMutableDictionary dictionaryWithDictionary:config];
|
||||
mutableConfig[@"constants"] = constants; // There's no real need to copy this
|
||||
config = mutableConfig; // Nor this - receiver is unlikely to mutate it
|
||||
}
|
||||
}
|
||||
|
||||
moduleConfig[moduleName] = config;
|
||||
@ -340,6 +416,7 @@ static NSDictionary *RCTRemoteModulesConfig(NSDictionary *modulesByName)
|
||||
return moduleConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* As above, but for local modules/methods, which represent JS classes
|
||||
* and methods that will be called by the native code via the bridge.
|
||||
@ -417,21 +494,19 @@ static NSDictionary *RCTLocalModulesConfig()
|
||||
|
||||
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
|
||||
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
||||
- (instancetype)initWithExecutor:(id<RCTJavaScriptExecutor>)executor
|
||||
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_javaScriptExecutor = javaScriptExecutor;
|
||||
_javaScriptExecutor = executor;
|
||||
_latestJSExecutor = _javaScriptExecutor;
|
||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
// Register passed-in module instances
|
||||
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
|
||||
if (block) {
|
||||
for (id<RCTBridgeModule> module in block(self)) {
|
||||
preregisteredModules[RCTModuleNameForClass([module class])] = module;
|
||||
}
|
||||
for (id<RCTBridgeModule> module in block ? block() : nil) {
|
||||
preregisteredModules[RCTModuleNameForClass([module class])] = module;
|
||||
}
|
||||
|
||||
// Instantiate modules
|
||||
@ -444,14 +519,14 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
// Preregistered instances takes precedence, no questions asked
|
||||
if (!preregisteredModules[moduleName]) {
|
||||
// It's OK to have a name collision as long as the second instance is nil
|
||||
RCTAssert(RCTCreateModuleInstance(moduleClass, self) == nil,
|
||||
RCTAssert([[moduleClass alloc] init] == nil,
|
||||
@"Attempted to register RCTBridgeModule class %@ for the name '%@', \
|
||||
but name was already registered by class %@", moduleClass,
|
||||
moduleName, [modulesByName[moduleName] class]);
|
||||
}
|
||||
} else {
|
||||
// Module name hasn't been used before, so go ahead and instantiate
|
||||
id<RCTBridgeModule> module = RCTCreateModuleInstance(moduleClass, self);
|
||||
id<RCTBridgeModule> module = [[moduleClass alloc] init];
|
||||
if (module) {
|
||||
_modulesByID[moduleID] = modulesByName[moduleName] = module;
|
||||
}
|
||||
@ -461,6 +536,13 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
// Store modules
|
||||
_modulesByName = [modulesByName copy];
|
||||
|
||||
// Set bridge
|
||||
for (id<RCTBridgeModule> module in _modulesByName.allValues) {
|
||||
if ([module respondsToSelector:@selector(setBridge:)]) {
|
||||
module.bridge = self;
|
||||
}
|
||||
}
|
||||
|
||||
// Inject module data into JS context
|
||||
NSString *configJSON = RCTJSONStringify(@{
|
||||
@"remoteModuleConfig": RCTRemoteModulesConfig(_modulesByName),
|
||||
@ -470,12 +552,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
[_javaScriptExecutor injectJSONText:configJSON asGlobalObjectNamed:@"__fbBatchedBridgeConfig" callback:^(id err) {
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
|
||||
if (dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)) != 0) {
|
||||
RCTLogMustFix(@"JavaScriptExecutor took too long to inject JSON object");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -501,23 +583,29 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
// Release executor
|
||||
if (_latestJSExecutor == _javaScriptExecutor) {
|
||||
_latestJSExecutor = nil;
|
||||
}
|
||||
[_javaScriptExecutor invalidate];
|
||||
_javaScriptExecutor = nil;
|
||||
|
||||
dispatch_sync(_shadowQueue, ^{
|
||||
|
||||
// Wait for queued methods to finish
|
||||
dispatch_sync(self.shadowQueue, ^{
|
||||
// Make sure all dispatchers have been executed before continuing
|
||||
// TODO: is this still needed?
|
||||
});
|
||||
|
||||
|
||||
// Invalidate modules
|
||||
for (id target in _modulesByID.allObjects) {
|
||||
if ([target respondsToSelector:@selector(invalidate)]) {
|
||||
[(id<RCTInvalidating>)target invalidate];
|
||||
}
|
||||
}
|
||||
[_modulesByID removeAllObjects];
|
||||
|
||||
// Release modules (breaks retain cycle if module has strong bridge reference)
|
||||
_modulesByID = nil;
|
||||
_modulesByName = nil;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -537,10 +625,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
||||
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
|
||||
[[moduleDotMethod componentsSeparatedByString:@"."] firstObject]);
|
||||
|
||||
|
||||
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
||||
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
|
||||
|
||||
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, args ?: @[]]];
|
||||
@ -554,12 +642,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
onComplete(scriptLoadError);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
[_javaScriptExecutor executeJSCall:@"BatchedBridge"
|
||||
method:@"flushedQueue"
|
||||
arguments:@[]
|
||||
callback:^(id objcValue, NSError *error) {
|
||||
[self _handleBuffer:objcValue];
|
||||
callback:^(id json, NSError *error) {
|
||||
[self _handleBuffer:json];
|
||||
onComplete(error);
|
||||
}];
|
||||
}];
|
||||
@ -570,35 +658,28 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
||||
{
|
||||
NSTimeInterval startJS = RCTTGetAbsoluteTime();
|
||||
|
||||
RCTJavaScriptCallback processResponse = ^(id objcValue, NSError *error) {
|
||||
|
||||
RCTJavaScriptCallback processResponse = ^(id json, NSError *error) {
|
||||
NSTimeInterval startNative = RCTTGetAbsoluteTime();
|
||||
[self _handleBuffer:objcValue];
|
||||
|
||||
[self _handleBuffer:json];
|
||||
|
||||
NSTimeInterval end = RCTTGetAbsoluteTime();
|
||||
NSTimeInterval timeJS = startNative - startJS;
|
||||
NSTimeInterval timeNative = end - startNative;
|
||||
|
||||
|
||||
// TODO: surface this performance information somewhere
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"PERF" object:nil userInfo:@{@"JS": @(timeJS * 1000000), @"Native": @(timeNative * 1000000)}];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"PERF" object:nil userInfo:@{
|
||||
@"JS": @(timeJS * 1000000),
|
||||
@"Native": @(timeNative * 1000000)
|
||||
}];
|
||||
};
|
||||
|
||||
|
||||
[_javaScriptExecutor executeJSCall:module
|
||||
method:method
|
||||
arguments:args
|
||||
callback:processResponse];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO (#5906496): Have responses piggy backed on a round trip with ObjC->JS requests.
|
||||
*/
|
||||
- (void)_sendResponseToJavaScriptCallbackID:(NSInteger)cbID args:(NSArray *)args
|
||||
{
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"invokeCallbackAndReturnFlushedQueue"
|
||||
arguments:@[@(cbID), args]];
|
||||
}
|
||||
|
||||
#pragma mark - Payload Processing
|
||||
|
||||
- (void)_handleBuffer:(id)buffer
|
||||
@ -606,12 +687,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
if (buffer == nil || buffer == (id)kCFNull) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (![buffer isKindOfClass:[NSArray class]]) {
|
||||
RCTLogError(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class]));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSArray *requestsArray = (NSArray *)buffer;
|
||||
NSUInteger bufferRowCount = [requestsArray count];
|
||||
NSUInteger expectedFieldsCount = RCTBridgeFieldResponseReturnValues + 1;
|
||||
@ -619,7 +700,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
RCTLogError(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (NSUInteger fieldIndex = RCTBridgeFieldRequestModuleIDs; fieldIndex <= RCTBridgeFieldParamss; fieldIndex++) {
|
||||
id field = [requestsArray objectAtIndex:fieldIndex];
|
||||
if (![field isKindOfClass:[NSArray class]]) {
|
||||
@ -627,18 +708,18 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
|
||||
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
|
||||
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
|
||||
|
||||
|
||||
NSUInteger numRequests = [moduleIDs count];
|
||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
|
||||
if (!allSame) {
|
||||
RCTLogError(@"Invalid data message - all must be length: %zd", numRequests);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (NSUInteger i = 0; i < numRequests; i++) {
|
||||
@autoreleasepool {
|
||||
[self _handleRequestNumber:i
|
||||
@ -647,9 +728,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
params:paramsArrays[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
dispatch_async(self.shadowQueue, ^{
|
||||
for (id module in _modulesByID.allObjects) {
|
||||
if ([module respondsToSelector:@selector(batchDidComplete)]) {
|
||||
[module batchDidComplete];
|
||||
@ -668,170 +749,42 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Look up method
|
||||
NSArray *methods = RCTExportedMethodsByModuleID()[moduleID];
|
||||
if (methodID >= methods.count) {
|
||||
RCTLogError(@"Unknown methodID: %zd for module: %zd (%@)", methodID, moduleID, RCTModuleNamesByID[moduleID]);
|
||||
return NO;
|
||||
}
|
||||
|
||||
RCTModuleMethod *method = methods[methodID];
|
||||
NSUInteger methodArity = method.arity;
|
||||
if (params.count != methodArity) {
|
||||
RCTLogError(@"Expected %tu arguments but got %tu invoking %@.%@",
|
||||
methodArity,
|
||||
params.count,
|
||||
RCTModuleNamesByID[moduleID],
|
||||
method.JSMethodName);
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
__weak RCTBridge *weakSelf = self;
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
dispatch_async(self.shadowQueue, ^{
|
||||
__strong RCTBridge *strongSelf = weakSelf;
|
||||
|
||||
|
||||
if (!strongSelf.isValid) {
|
||||
// strongSelf has been invalidated since the dispatch_async call and this
|
||||
// invocation should not continue.
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: we should just store module instances by index, since that's how we look them up anyway
|
||||
id target = strongSelf->_modulesByID[moduleID];
|
||||
RCTAssert(target != nil, @"No module found for name '%@'", RCTModuleNamesByID[moduleID]);
|
||||
|
||||
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.
|
||||
NS_VALID_UNTIL_END_OF_SCOPE NSMutableArray *blocks = [NSMutableArray array];
|
||||
|
||||
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
|
||||
if ([param isEqual:[NSNull null]]) {
|
||||
param = nil;
|
||||
} else if ([method.blockArgumentIndexes containsIndex:idx]) {
|
||||
id block = [strongSelf createResponseSenderBlock:[param integerValue]];
|
||||
[blocks addObject:block];
|
||||
param = block;
|
||||
}
|
||||
|
||||
NSUInteger argIdx = idx + 2;
|
||||
|
||||
// TODO: can we do this lookup in advance and cache the logic instead of
|
||||
// recalculating it every time for every parameter?
|
||||
BOOL shouldSet = YES;
|
||||
const char *argumentType = [methodSignature getArgumentTypeAtIndex:argIdx];
|
||||
switch (argumentType[0]) {
|
||||
case ':':
|
||||
if ([param isKindOfClass:[NSString class]]) {
|
||||
SEL sel = NSSelectorFromString(param);
|
||||
[invocation setArgument:&sel atIndex:argIdx];
|
||||
shouldSet = NO;
|
||||
}
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if ([param isKindOfClass:[NSString class]]) {
|
||||
const char *string = [param UTF8String];
|
||||
[invocation setArgument:&string atIndex:argIdx];
|
||||
shouldSet = NO;
|
||||
}
|
||||
break;
|
||||
|
||||
// TODO: it seems like an error if the param doesn't respond
|
||||
// so we should probably surface that error rather than failing silently
|
||||
#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:¶m atIndex:argIdx];
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
// Look up module
|
||||
id module = strongSelf->_modulesByID[moduleID];
|
||||
if (!module) {
|
||||
RCTLogError(@"No module found for name '%@'", RCTModuleNamesByID[moduleID]);
|
||||
return;
|
||||
}
|
||||
|
||||
@try {
|
||||
[invocation invoke];
|
||||
[method invokeWithBridge:strongSelf module:module arguments:params];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception);
|
||||
RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a callback that reports values back to the JS thread.
|
||||
* TODO (#5906496): These responses should go into their own queue `MessageQueue.m` that
|
||||
* mirrors the JS queue and protocol. For now, we speak the "language" of the JS
|
||||
* queue by packing it into an array that matches the wire protocol.
|
||||
*/
|
||||
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)cbID
|
||||
{
|
||||
if (!cbID) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return ^(NSArray *args) {
|
||||
[self _sendResponseToJavaScriptCallbackID:cbID args:args];
|
||||
};
|
||||
}
|
||||
|
||||
+ (NSInvocation *)invocationForAdditionalArguments:(NSUInteger)argCount
|
||||
{
|
||||
static NSMutableDictionary *invocations;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
invocations = [NSMutableDictionary dictionary];
|
||||
});
|
||||
|
||||
id key = @(argCount);
|
||||
NSInvocation *invocation = invocations[key];
|
||||
if (invocation == nil) {
|
||||
NSString *objCTypes = [@"v@:" stringByPaddingToLength:3 + argCount withString:@"@" startingAtIndex:0];
|
||||
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:objCTypes.UTF8String];
|
||||
invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
invocations[key] = invocation;
|
||||
}
|
||||
|
||||
return invocation;
|
||||
}
|
||||
|
||||
- (void)registerRootView:(RCTRootView *)rootView
|
||||
{
|
||||
// TODO: only used by RCTUIManager - can we eliminate this special case?
|
||||
for (id module in _modulesByID.allObjects) {
|
||||
if ([module respondsToSelector:@selector(registerRootView:)]) {
|
||||
[module registerRootView:rootView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)hasValidJSExecutor
|
||||
{
|
||||
return (_latestJSExecutor != nil && [_latestJSExecutor isValid]);
|
||||
@ -844,7 +797,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
return;
|
||||
}
|
||||
NSMutableArray *args = [NSMutableArray arrayWithObject:level];
|
||||
|
||||
|
||||
// TODO (#5906496): Find out and document why we skip the first object
|
||||
for (id ob in [objects subarrayWithRange:(NSRange){1, [objects count] - 1}]) {
|
||||
if ([NSJSONSerialization isValidJSONObject:@[ob]]) {
|
||||
@ -853,11 +806,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
[args addObject:[ob description]];
|
||||
}
|
||||
}
|
||||
// Note the js executor could get invalidated while we're trying to call this...need to watch out for that.
|
||||
|
||||
// Note: the js executor could get invalidated while we're trying to call this...need to watch out for that.
|
||||
[_latestJSExecutor executeJSCall:@"RCTLog"
|
||||
method:@"logIfNoNativeHook"
|
||||
arguments:args
|
||||
callback:^(id objcValue, NSError *error) {}];
|
||||
callback:^(id json, NSError *error) {}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -19,10 +19,12 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Optional initializer for modules that require access
|
||||
* to bridge features, such as sending events or making JS calls
|
||||
* A reference to the RCTBridge. Useful for modules that require access
|
||||
* to bridge features, such as sending events or making JS calls. This
|
||||
* will be set automatically by the bridge when it initializes the module.
|
||||
* To implement this in your module, just add @synthesize bridge = _bridge;
|
||||
*/
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
@property (nonatomic, strong) RCTBridge *bridge;
|
||||
|
||||
/**
|
||||
* The module name exposed to JS. If omitted, this will be inferred
|
||||
@ -42,17 +44,11 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Some "constants" are not really constant, and need to be re-generated
|
||||
* each time the bridge module is created. Support for this feature is
|
||||
* deprecated and may be going away or changing, but for now you can use
|
||||
* the -constantsToExport instance method to register these "pseudo-constants".
|
||||
* NativeModules.ModuleName.X. This method is called when the module is
|
||||
* registered by the bridge. It is only called once for the lifetime of the
|
||||
* bridge, so it is not suitable for returning dynamic values, but may be
|
||||
* used for long-lived values such as session keys, that are regenerated only
|
||||
* as part of a reload of the entire React application.
|
||||
*/
|
||||
- (NSDictionary *)constantsToExport;
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#import "RCTInvalidating.h"
|
||||
|
||||
typedef void (^RCTJavaScriptCompleteBlock)(NSError *error);
|
||||
typedef void (^RCTJavaScriptCallback)(id objcValue, NSError *error);
|
||||
typedef void (^RCTJavaScriptCallback)(id json, NSError *error);
|
||||
|
||||
/**
|
||||
* Abstracts away a JavaScript execution context - we may be running code in a
|
||||
|
@ -20,28 +20,22 @@
|
||||
// If defined, only log messages that match this regex will fatal
|
||||
#define RCTLOG_FATAL_REGEX nil
|
||||
|
||||
#define _RCTLog(__RCTLog__level, ...) do { \
|
||||
NSString *__RCTLog__levelStr; \
|
||||
switch(__RCTLog__level) { \
|
||||
case RCTLOG_INFO: __RCTLog__levelStr = @"info"; break; \
|
||||
case RCTLOG_WARN: __RCTLog__levelStr = @"warn"; break; \
|
||||
case RCTLOG_ERROR: __RCTLog__levelStr = @"error"; break; \
|
||||
case RCTLOG_MUSTFIX: __RCTLog__levelStr = @"mustfix"; break; \
|
||||
} \
|
||||
NSString *__RCTLog__msg = _RCTLogObjects(RCTLogFormat(__VA_ARGS__), __RCTLog__levelStr); \
|
||||
if (__RCTLog__level >= RCTLOG_FATAL_LEVEL) { \
|
||||
BOOL __RCTLog__fail = YES; \
|
||||
if (RCTLOG_FATAL_REGEX) { \
|
||||
NSError *__RCTLog__e; \
|
||||
NSRegularExpression *__RCTLog__regex = [NSRegularExpression regularExpressionWithPattern:RCTLOG_FATAL_REGEX options:0 error:&__RCTLog__e]; \
|
||||
__RCTLog__fail = [__RCTLog__regex numberOfMatchesInString:__RCTLog__msg options:0 range:NSMakeRange(0, [__RCTLog__msg length])] > 0; \
|
||||
} \
|
||||
RCTCAssert(!__RCTLog__fail, @"RCTLOG_FATAL_LEVEL %@: %@", __RCTLog__levelStr, __RCTLog__msg); \
|
||||
} \
|
||||
if (__RCTLog__level >= RCTLOG_REDBOX_LEVEL) { \
|
||||
RCTRedBox *__RCTLog__redBox = [RCTRedBox sharedInstance]; \
|
||||
[__RCTLog__redBox showErrorMessage:__RCTLog__msg]; \
|
||||
} \
|
||||
extern __unsafe_unretained NSString *RCTLogLevels[];
|
||||
|
||||
#define _RCTLog(_level, ...) do { \
|
||||
NSString *__RCTLog__levelStr = RCTLogLevels[_level - 1]; \
|
||||
NSString *__RCTLog__msg = RCTLogObjects(RCTLogFormat(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__), __RCTLog__levelStr); \
|
||||
if (_level >= RCTLOG_FATAL_LEVEL) { \
|
||||
BOOL __RCTLog__fail = YES; \
|
||||
if (RCTLOG_FATAL_REGEX) { \
|
||||
NSRegularExpression *__RCTLog__regex = [NSRegularExpression regularExpressionWithPattern:RCTLOG_FATAL_REGEX options:0 error:NULL]; \
|
||||
__RCTLog__fail = [__RCTLog__regex numberOfMatchesInString:__RCTLog__msg options:0 range:NSMakeRange(0, [__RCTLog__msg length])] > 0; \
|
||||
} \
|
||||
RCTCAssert(!__RCTLog__fail, @"RCTLOG_FATAL_LEVEL %@: %@", __RCTLog__levelStr, __RCTLog__msg); \
|
||||
} \
|
||||
if (_level >= RCTLOG_REDBOX_LEVEL) { \
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:__RCTLog__msg]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RCTLog(...) _RCTLog(RCTLOG_INFO, __VA_ARGS__)
|
||||
@ -50,20 +44,15 @@
|
||||
#define RCTLogError(...) _RCTLog(RCTLOG_ERROR, __VA_ARGS__)
|
||||
#define RCTLogMustFix(...) _RCTLog(RCTLOG_MUSTFIX, __VA_ARGS__)
|
||||
|
||||
#define RCTLogFormat(...) _RCTLogFormat(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
|
||||
#define RCTLogFormatString(...) _RCTLogFormatString(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
NSString *_RCTLogObjects(NSArray *objects, NSString *level);
|
||||
NSArray *_RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
NSString *_RCTLogFormatString(const char *file, int lineNumber, const char *funcName, NSString *format, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
NSString *RCTLogObjects(NSArray *objects, NSString *level);
|
||||
NSArray *RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
|
||||
void RCTInjectLogFunction(void (^logFunction)(NSString *msg));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef void (^RCTLogFunction)(NSString *format, NSString *str);
|
||||
void RCTInjectLogFunction(RCTLogFunction func);
|
||||
|
@ -4,10 +4,17 @@
|
||||
|
||||
#import "RCTBridge.h"
|
||||
|
||||
static RCTLogFunction injectedLogFunction;
|
||||
__unsafe_unretained NSString *RCTLogLevels[] = {
|
||||
@"info",
|
||||
@"warn",
|
||||
@"error",
|
||||
@"mustfix"
|
||||
};
|
||||
|
||||
void RCTInjectLogFunction(RCTLogFunction func) {
|
||||
injectedLogFunction = func;
|
||||
static void (^RCTInjectedLogFunction)(NSString *msg);
|
||||
|
||||
void RCTInjectLogFunction(void (^logFunction)(NSString *msg)) {
|
||||
RCTInjectedLogFunction = logFunction;
|
||||
}
|
||||
|
||||
static inline NSString *_RCTLogPreamble(const char *file, int lineNumber, const char *funcName)
|
||||
@ -21,7 +28,7 @@ static inline NSString *_RCTLogPreamble(const char *file, int lineNumber, const
|
||||
}
|
||||
|
||||
// TODO (#5906496): // kinda ugly that this is tied to RCTBridge
|
||||
NSString *_RCTLogObjects(NSArray *objects, NSString *level)
|
||||
NSString *RCTLogObjects(NSArray *objects, NSString *level)
|
||||
{
|
||||
NSString *str = objects[0];
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
@ -33,8 +40,8 @@ NSString *_RCTLogObjects(NSArray *objects, NSString *level)
|
||||
{
|
||||
// Print normal errors with timestamps when not in simulator.
|
||||
// Non errors are already compiled out above, so log as error here.
|
||||
if (injectedLogFunction) {
|
||||
injectedLogFunction(@">\n %@", str);
|
||||
if (RCTInjectedLogFunction) {
|
||||
RCTInjectedLogFunction(str);
|
||||
} else {
|
||||
NSLog(@">\n %@", str);
|
||||
}
|
||||
@ -42,14 +49,14 @@ NSString *_RCTLogObjects(NSArray *objects, NSString *level)
|
||||
return str;
|
||||
}
|
||||
|
||||
// Returns array of objects. First arg is a simple string to print, remaining args are objects to pass through to the debugger so they are
|
||||
// inspectable in the console.
|
||||
NSArray *_RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...)
|
||||
// Returns array of objects. First arg is a simple string to print, remaining args
|
||||
// are objects to pass through to the debugger so they are inspectable in the console.
|
||||
NSArray *RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString *preamble = _RCTLogPreamble(file, lineNumber, funcName);
|
||||
|
||||
|
||||
// Pull out NSObjects so we can pass them through as inspectable objects to the js debugger
|
||||
NSArray *formatParts = [format componentsSeparatedByString:@"%"];
|
||||
NSMutableArray *objects = [NSMutableArray arrayWithObject:preamble];
|
||||
@ -77,12 +84,3 @@ NSArray *_RCTLogFormat(const char *file, int lineNumber, const char *funcName, N
|
||||
[objectsOut addObjectsFromArray:objects];
|
||||
return objectsOut;
|
||||
}
|
||||
|
||||
NSString *_RCTLogFormatString(const char *file, int lineNumber, const char *funcName, NSString *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
NSString *body = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end (args);
|
||||
return [NSString stringWithFormat:@"%@ %@", _RCTLogPreamble(file, lineNumber, funcName), body];
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#import "RCTRedBox.h"
|
||||
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@interface RCTRedBoxWindow : UIWindow <UITableViewDelegate, UITableViewDataSource>
|
||||
@ -13,31 +12,31 @@
|
||||
{
|
||||
UIView *_rootView;
|
||||
UITableView *_stackTraceTableView;
|
||||
|
||||
|
||||
NSString *_lastErrorMessage;
|
||||
NSArray *_lastStackTrace;
|
||||
|
||||
|
||||
UITableViewCell *_cachedMessageCell;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
|
||||
|
||||
self.windowLevel = UIWindowLevelStatusBar + 5;
|
||||
self.backgroundColor = [UIColor colorWithRed:0.8 green:0 blue:0 alpha:1];
|
||||
self.hidden = YES;
|
||||
|
||||
|
||||
UIViewController *rootController = [[UIViewController alloc] init];
|
||||
self.rootViewController = rootController;
|
||||
_rootView = rootController.view;
|
||||
_rootView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
|
||||
const CGFloat buttonHeight = 60;
|
||||
|
||||
CGRect detailsFrame = _rootView.bounds;
|
||||
detailsFrame.size.height -= buttonHeight;
|
||||
|
||||
|
||||
_stackTraceTableView = [[UITableView alloc] initWithFrame:detailsFrame style:UITableViewStylePlain];
|
||||
_stackTraceTableView.delegate = self;
|
||||
_stackTraceTableView.dataSource = self;
|
||||
@ -45,21 +44,21 @@
|
||||
_stackTraceTableView.separatorColor = [[UIColor whiteColor] colorWithAlphaComponent:0.3];
|
||||
_stackTraceTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
[_rootView addSubview:_stackTraceTableView];
|
||||
|
||||
|
||||
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
dismissButton.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
[dismissButton setTitle:@"Dismiss (ESC)" forState:UIControlStateNormal];
|
||||
[dismissButton setTitleColor:[[UIColor whiteColor] colorWithAlphaComponent:0.5] forState:UIControlStateNormal];
|
||||
[dismissButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[dismissButton addTarget:self action:@selector(dismiss) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
|
||||
UIButton *reloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
reloadButton.titleLabel.font = [UIFont systemFontOfSize:14];
|
||||
[reloadButton setTitle:@"Reload JS (\u2318R)" forState:UIControlStateNormal];
|
||||
[reloadButton setTitleColor:[[UIColor whiteColor] colorWithAlphaComponent:0.5] forState:UIControlStateNormal];
|
||||
[reloadButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
[reloadButton addTarget:self action:@selector(reload) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
|
||||
CGFloat buttonWidth = self.bounds.size.width / 2;
|
||||
dismissButton.frame = CGRectMake(0, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
reloadButton.frame = CGRectMake(buttonWidth, self.bounds.size.height - buttonHeight, buttonWidth, buttonHeight);
|
||||
@ -87,7 +86,7 @@
|
||||
{
|
||||
_lastStackTrace = stack;
|
||||
_lastErrorMessage = message;
|
||||
|
||||
|
||||
if (self.hidden && shouldShow) {
|
||||
|
||||
_cachedMessageCell = [self reuseCell:nil forErrorMessage:message];
|
||||
@ -109,7 +108,7 @@
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
[RCTRootView reloadAll];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"RCTReloadNotification" object:nil userInfo:nil];
|
||||
[self dismiss];
|
||||
}
|
||||
|
||||
@ -149,9 +148,9 @@
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
}
|
||||
|
||||
|
||||
cell.textLabel.text = message;
|
||||
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
@ -168,7 +167,7 @@
|
||||
cell.selectedBackgroundView = [[UIView alloc] init];
|
||||
cell.selectedBackgroundView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2];
|
||||
}
|
||||
|
||||
|
||||
cell.textLabel.text = stackFrame[@"methodName"];
|
||||
cell.detailTextLabel.text = cell.detailTextLabel.text = [NSString stringWithFormat:@"%@:%@", [stackFrame[@"file"] lastPathComponent], stackFrame[@"lineNumber"]];
|
||||
return cell;
|
||||
@ -179,7 +178,7 @@
|
||||
if ([indexPath section] == 0) {
|
||||
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
||||
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
|
||||
|
||||
NSDictionary *attributes = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:16],
|
||||
NSParagraphStyleAttributeName: paragraphStyle};
|
||||
CGRect boundingRect = [_lastErrorMessage boundingRectWithSize:CGSizeMake(tableView.frame.size.width - 30, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
|
||||
@ -206,14 +205,14 @@
|
||||
// NOTE: We could use RCTKeyCommands for this, but since
|
||||
// we control this window, we can use the standard, non-hacky
|
||||
// mechanism instead
|
||||
|
||||
|
||||
return @[
|
||||
|
||||
|
||||
// Dismiss red box
|
||||
[UIKeyCommand keyCommandWithInput:UIKeyInputEscape
|
||||
modifierFlags:0
|
||||
action:@selector(dismiss)],
|
||||
|
||||
|
||||
// Reload
|
||||
[UIKeyCommand keyCommandWithInput:@"r"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
@ -269,12 +268,12 @@
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack showIfHidden:(BOOL)shouldShow
|
||||
{
|
||||
|
||||
|
||||
#if DEBUG
|
||||
|
||||
|
||||
dispatch_block_t block = ^{
|
||||
if (!_window) {
|
||||
_window = [[RCTRedBoxWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
_window = [[RCTRedBoxWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
}
|
||||
[_window showErrorMessage:message withStack:stack showIfHidden:shouldShow];
|
||||
};
|
||||
@ -283,9 +282,9 @@
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), block);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void)dismiss
|
||||
|
@ -14,13 +14,13 @@
|
||||
#import "RCTWebViewExecutor.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification";
|
||||
NSString *const RCTReloadNotification = @"RCTReloadNotification";
|
||||
|
||||
@implementation RCTRootView
|
||||
{
|
||||
RCTBridge *_bridge;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
id <RCTJavaScriptExecutor> _executor;
|
||||
id<RCTJavaScriptExecutor> _executor;
|
||||
}
|
||||
|
||||
static Class _globalExecutorClass;
|
||||
@ -77,7 +77,7 @@ static Class _globalExecutorClass;
|
||||
// Add reload observer
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reload)
|
||||
name:RCTRootViewReloadNotification
|
||||
name:RCTReloadNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
@ -95,6 +95,10 @@ static Class _globalExecutorClass;
|
||||
|
||||
[_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
|
||||
args:@[self.reactTag]];
|
||||
|
||||
// TODO: eventually we'll want to be able to share the bridge between
|
||||
// multiple rootviews, in which case we'll need to move this elsewhere
|
||||
[_bridge invalidate];
|
||||
}
|
||||
|
||||
- (void)bundleFinishedLoading:(NSError *)error
|
||||
@ -108,7 +112,7 @@ static Class _globalExecutorClass;
|
||||
}
|
||||
} else {
|
||||
|
||||
[_bridge registerRootView:self];
|
||||
[_bridge.uiManager registerRootView:self];
|
||||
|
||||
NSString *moduleName = _moduleName ?: @"";
|
||||
NSDictionary *appParameters = @{
|
||||
@ -137,7 +141,7 @@ static Class _globalExecutorClass;
|
||||
|
||||
// Choose local executor if specified, followed by global, followed by default
|
||||
_executor = [[_executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class] alloc] init];
|
||||
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor moduleProvider:nil];
|
||||
_bridge = [[RCTBridge alloc] initWithExecutor:_executor moduleProvider:nil];
|
||||
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
||||
[self addGestureRecognizer:_touchHandler];
|
||||
|
||||
@ -229,7 +233,7 @@ static Class _globalExecutorClass;
|
||||
|
||||
+ (void)reloadAll
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRootViewReloadNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil];
|
||||
}
|
||||
|
||||
- (void)startOrResetInteractionTiming
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
@interface RCTTouchHandler : UIGestureRecognizer<RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
|
||||
- (void)startOrResetInteractionTiming;
|
||||
- (NSDictionary *)endAndResetInteractionTiming;
|
||||
|
||||
|
@ -67,11 +67,6 @@
|
||||
NSMutableArray *_bridgeInteractionTiming;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTarget:(id)target action:(SEL)action
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super initWithTarget:nil action:NULL])) {
|
||||
|
@ -1,17 +1,15 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <tgmath.h>
|
||||
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <tgmath.h>
|
||||
|
||||
#import "RCTAssert.h"
|
||||
|
||||
// Macro to indicate when inherited initializer is not to be used
|
||||
#define RCT_NOT_DESIGNATED_INITIALIZER() \
|
||||
do { \
|
||||
RCTAssert(NO, @"%@ is not the designated initializer for instances of %@.", NSStringFromSelector(_cmd), [self class]); \
|
||||
return nil; \
|
||||
} while (0)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Utility functions for JSON object <-> string serialization/deserialization
|
||||
NSString *RCTJSONStringify(id jsonObject, NSError **error);
|
||||
@ -39,3 +37,10 @@ void RCTSwapInstanceMethods(Class cls, SEL original, SEL replacement);
|
||||
// Module subclass support
|
||||
BOOL RCTClassOverridesClassMethod(Class cls, SEL selector);
|
||||
BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector);
|
||||
|
||||
// Enumerate all classes that conform to NSObject protocol
|
||||
void RCTEnumerateClasses(void (^block)(Class cls));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -155,6 +155,31 @@ BOOL RCTClassOverridesInstanceMethod(Class cls, SEL selector)
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
free(methods);
|
||||
return NO;
|
||||
}
|
||||
|
||||
void RCTEnumerateClasses(void (^block)(Class cls))
|
||||
{
|
||||
static Class *classes;
|
||||
static unsigned int classCount;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
classes = objc_copyClassList(&classCount);
|
||||
});
|
||||
|
||||
for (unsigned int i = 0; i < classCount; i++)
|
||||
{
|
||||
Class cls = classes[i];
|
||||
Class superclass = cls;
|
||||
while (superclass)
|
||||
{
|
||||
if (class_conformsToProtocol(superclass, @protocol(NSObject)))
|
||||
{
|
||||
block(cls);
|
||||
break;
|
||||
}
|
||||
superclass = class_getSuperclass(superclass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*/
|
||||
@protocol RCTViewNodeProtocol <NSObject>
|
||||
|
||||
@property (nonatomic, strong) NSNumber *reactTag;
|
||||
@property (nonatomic, copy) NSNumber *reactTag;
|
||||
|
||||
- (void)insertReactSubview:(id<RCTViewNodeProtocol>)subview atIndex:(NSInteger)atIndex;
|
||||
- (void)removeReactSubview:(id<RCTViewNodeProtocol>)subview;
|
||||
@ -21,6 +21,8 @@
|
||||
@optional
|
||||
|
||||
// TODO: Deprecate this
|
||||
// This method is called after layout has been performed for all views known
|
||||
// to the RCTViewManager. It is only called on UIViews, not shadow views.
|
||||
- (void)reactBridgeDidFinishTransaction;
|
||||
|
||||
@end
|
||||
|
@ -45,7 +45,7 @@ static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object,
|
||||
fprintf(stderr, "%s\n", [modifiedString UTF8String]); // don't print timestamps and other junk
|
||||
#else
|
||||
// Print normal errors with timestamps to files when not in simulator.
|
||||
_RCTLogObjects(@[modifiedString], @"log");
|
||||
RCTLogObjects(@[modifiedString], @"log");
|
||||
#endif
|
||||
JSStringRelease(string);
|
||||
}
|
||||
|
@ -48,10 +48,10 @@
|
||||
NSArray *buttons = args[@"buttons"];
|
||||
|
||||
if (!title && !message) {
|
||||
RCTLogMustFix(@"Must specify either an alert title, or message, or both");
|
||||
RCTLogError(@"Must specify either an alert title, or message, or both");
|
||||
return;
|
||||
} else if (buttons.count == 0) {
|
||||
RCTLogMustFix(@"Must have at least one button.");
|
||||
RCTLogError(@"Must have at least one button.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
NSInteger index = 0;
|
||||
for (NSDictionary *button in buttons) {
|
||||
if (button.count != 1) {
|
||||
RCTLogMustFix(@"Button definitions should have exactly one key.");
|
||||
RCTLogError(@"Button definitions should have exactly one key.");
|
||||
}
|
||||
NSString *buttonKey = [button.allKeys firstObject];
|
||||
NSString *buttonTitle = [button[buttonKey] description];
|
||||
|
@ -5,6 +5,9 @@
|
||||
#import <CoreLocation/CLLocationManager.h>
|
||||
#import <CoreLocation/CLLocationManagerDelegate.h>
|
||||
|
||||
#import <FBReactKit/RCTAssert.h>
|
||||
#import <FBReactKit/RCTLog.h>
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
@ -29,17 +32,17 @@ const CLLocationAccuracy RCTLocationAccuracy = 500.0; // meters
|
||||
@implementation RCTLocationObserver
|
||||
{
|
||||
CLLocationManager *_locationManager;
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
NSDictionary *_lastLocationEvent;
|
||||
NSMutableDictionary *_pendingRequests;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_eventDispatcher = bridge.eventDispatcher;
|
||||
if ((self = [super init])) {
|
||||
_pendingRequests = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
@ -107,7 +110,7 @@ const CLLocationAccuracy RCTLocationAccuracy = 500.0; // meters
|
||||
},
|
||||
@"timestamp": @(CACurrentMediaTime())
|
||||
};
|
||||
[_eventDispatcher sendDeviceEventWithName:@"geoLocationDidChange" body:event];
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"geoLocationDidChange" body:event];
|
||||
NSArray *pendingRequestsCopy;
|
||||
|
||||
// TODO (#5906496): is this locking neccessary? If so, use something better than @synchronize
|
||||
|
@ -49,7 +49,7 @@ static BOOL RCTViewControllerBasedStatusBarAppearance()
|
||||
});
|
||||
}
|
||||
|
||||
+ (NSDictionary *)constantsToExport
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return @{
|
||||
@"Style": @{
|
||||
|
@ -51,35 +51,35 @@
|
||||
@implementation RCTTiming
|
||||
{
|
||||
RCTSparseArray *_timers;
|
||||
RCTBridge *_bridge;
|
||||
id _updateTimer;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
+ (NSArray *)JSMethods
|
||||
{
|
||||
return @[@"RCTJSTimers.callTimers"];
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
|
||||
_timers = [[RCTSparseArray alloc] init];
|
||||
[self startTimers];
|
||||
|
||||
|
||||
for (NSString *name in @[UIApplicationWillResignActiveNotification,
|
||||
UIApplicationDidEnterBackgroundNotification,
|
||||
UIApplicationWillTerminateNotification]) {
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(stopTimers)
|
||||
name:name
|
||||
object:nil];
|
||||
}
|
||||
|
||||
|
||||
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
|
||||
UIApplicationWillEnterForegroundNotification]) {
|
||||
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(startTimers)
|
||||
name:name
|
||||
@ -114,14 +114,14 @@
|
||||
- (void)startTimers
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if (![self isValid] || _updateTimer != nil) {
|
||||
|
||||
if (![self isValid] || _updateTimer != nil || _timers.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
_updateTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
|
||||
if (_updateTimer) {
|
||||
[_updateTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_updateTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
} else {
|
||||
RCTLogWarn(@"Failed to create a display link (probably on buildbot) - using an NSTimer for AppEngine instead.");
|
||||
_updateTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60)
|
||||
@ -135,7 +135,7 @@
|
||||
- (void)update
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
|
||||
NSMutableArray *timersToCall = [[NSMutableArray alloc] init];
|
||||
for (RCTTimer *timer in _timers.allObjects) {
|
||||
if ([timer updateFoundNeedsJSUpdate]) {
|
||||
@ -145,7 +145,7 @@
|
||||
_timers[timer.callbackID] = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// call timers that need to be called
|
||||
if ([timersToCall count] > 0) {
|
||||
[_bridge enqueueJSCall:@"RCTJSTimers.callTimers" args:@[timersToCall]];
|
||||
@ -185,6 +185,7 @@
|
||||
repeats:repeats];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_timers[callbackID] = timer;
|
||||
[self startTimers];
|
||||
});
|
||||
}
|
||||
|
||||
@ -195,6 +196,9 @@
|
||||
if (timerID) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_timers[timerID] = nil;
|
||||
if (_timers.count == 0) {
|
||||
[self stopTimers];
|
||||
}
|
||||
});
|
||||
} else {
|
||||
RCTLogWarn(@"Called deleteTimer: with a nil timerID");
|
||||
|
@ -2,13 +2,18 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTInvalidating.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
@class RCTRootView;
|
||||
|
||||
@protocol RCTScrollableProtocol;
|
||||
|
||||
/**
|
||||
* The RCTUIManager is the module responsible for updating the view hierarchy.
|
||||
*/
|
||||
@interface RCTUIManager : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||
|
||||
@property (nonatomic, weak) id<RCTScrollableProtocol> mainScrollView;
|
||||
@ -19,8 +24,33 @@
|
||||
*/
|
||||
@property (nonatomic, readwrite, weak) id<UIScrollViewDelegate> nativeMainScrollDelegate;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
|
||||
/**
|
||||
* Schedule a block to be executed on the UI thread. Useful if you need to execute
|
||||
* view logic after all currently queued view updates have completed.
|
||||
*/
|
||||
- (void)addUIBlock:(RCTViewManagerUIBlock)block;
|
||||
|
||||
/**
|
||||
* The view that is currently first responder, according to the JS context.
|
||||
*/
|
||||
+ (UIView *)JSResponder;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This category makes the current RCTUIManager instance available via the
|
||||
* RCTBridge, which is useful for RCTBridgeModules or RCTViewManagers that
|
||||
* need to access the RCTUIManager.
|
||||
*/
|
||||
@interface RCTBridge (RCTUIManager)
|
||||
|
||||
@property (nonatomic, readonly) RCTUIManager *uiManager;
|
||||
|
||||
@end
|
||||
|
@ -32,47 +32,6 @@ static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_b
|
||||
}
|
||||
}
|
||||
|
||||
static NSDictionary *RCTViewModuleClasses(void)
|
||||
{
|
||||
static NSMutableDictionary *modules;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
modules = [NSMutableDictionary dictionary];
|
||||
|
||||
unsigned int classCount;
|
||||
Class *classes = objc_copyClassList(&classCount);
|
||||
for (unsigned int i = 0; i < classCount; i++) {
|
||||
|
||||
Class cls = classes[i];
|
||||
|
||||
if (!class_getSuperclass(cls)) {
|
||||
// Class has no superclass - it's probably something weird
|
||||
continue;
|
||||
}
|
||||
|
||||
if (![cls isSubclassOfClass:[RCTViewManager class]]) {
|
||||
// Not a view module
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get module name
|
||||
NSString *moduleName = [cls moduleName];
|
||||
|
||||
// Check module name is unique
|
||||
id existingClass = modules[moduleName];
|
||||
RCTCAssert(existingClass == Nil, @"Attempted to register view module class %@ "
|
||||
"for the name '%@', but name was already registered by class %@", cls, moduleName, existingClass);
|
||||
|
||||
// Add to module list
|
||||
modules[moduleName] = cls;
|
||||
}
|
||||
|
||||
free(classes);
|
||||
});
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
@interface RCTAnimation : NSObject
|
||||
|
||||
@property (nonatomic, readonly) NSTimeInterval duration;
|
||||
@ -190,48 +149,70 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
|
||||
@implementation RCTUIManager
|
||||
{
|
||||
dispatch_queue_t _shadowQueue;
|
||||
|
||||
// Root views are only mutated on the shadow queue
|
||||
NSMutableSet *_rootViewTags;
|
||||
NSMutableArray *_pendingUIBlocks;
|
||||
NSLock *_pendingUIBlocksLock;
|
||||
|
||||
|
||||
// Animation
|
||||
RCTLayoutAnimation *_nextLayoutAnimation; // RCT thread only
|
||||
RCTLayoutAnimation *_layoutAnimation; // Main thread only
|
||||
|
||||
// Keyed by moduleName
|
||||
// Keyed by viewName
|
||||
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
|
||||
|
||||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
@synthesize bridge =_bridge;
|
||||
|
||||
/**
|
||||
* This function derives the view name automatically
|
||||
* from the module name.
|
||||
*/
|
||||
static NSString *RCTViewNameForModuleName(NSString *moduleName)
|
||||
{
|
||||
NSString *name = moduleName;
|
||||
if ([name hasSuffix:@"Manager"]) {
|
||||
name = [name substringToIndex:name.length - @"Manager".length];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This private constructor should only be called when creating
|
||||
* isolated UIImanager instances for testing. Normal initialization
|
||||
* is via -init:, which is called automatically by the bridge.
|
||||
*/
|
||||
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
|
||||
{
|
||||
if ((self = [self init])) {
|
||||
_shadowQueue = shadowQueue;
|
||||
_viewManagers = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
_bridge = bridge;
|
||||
|
||||
_pendingUIBlocksLock = [[NSLock alloc] init];
|
||||
|
||||
// Instantiate view managers
|
||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
||||
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
viewManagers[moduleName] = [[moduleClass alloc] initWithEventDispatcher:_bridge.eventDispatcher];
|
||||
}];
|
||||
_viewManagers = viewManagers;
|
||||
|
||||
_defaultShadowViews = [[NSMutableDictionary alloc] init];
|
||||
_defaultViews = [[NSMutableDictionary alloc] init];
|
||||
|
||||
|
||||
_viewManagerRegistry = [[RCTSparseArray alloc] init];
|
||||
_shadowViewRegistry = [[RCTSparseArray alloc] init];
|
||||
_viewRegistry = [[RCTSparseArray alloc] init];
|
||||
|
||||
|
||||
// Internal resources
|
||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||
_rootViewTags = [[NSMutableSet alloc] init];
|
||||
@ -239,16 +220,35 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if (_bridge) {
|
||||
|
||||
// Clear previous bridge data
|
||||
[self invalidate];
|
||||
}
|
||||
if (bridge) {
|
||||
|
||||
_bridge = bridge;
|
||||
_shadowQueue = _bridge.shadowQueue;
|
||||
|
||||
// Get view managers from bridge
|
||||
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
|
||||
[_bridge.modules enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, RCTViewManager *manager, BOOL *stop) {
|
||||
if ([manager isKindOfClass:[RCTViewManager class]]) {
|
||||
viewManagers[RCTViewNameForModuleName(moduleName)] = manager;
|
||||
}
|
||||
}];
|
||||
|
||||
_viewManagers = [viewManagers copy];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _viewRegistry != nil;
|
||||
@ -260,6 +260,7 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
|
||||
_viewRegistry = nil;
|
||||
_shadowViewRegistry = nil;
|
||||
_bridge = nil;
|
||||
|
||||
[_pendingUIBlocksLock lock];
|
||||
_pendingUIBlocks = nil;
|
||||
@ -269,22 +270,22 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
- (void)registerRootView:(RCTRootView *)rootView;
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
|
||||
NSNumber *reactTag = rootView.reactTag;
|
||||
UIView *existingView = _viewRegistry[reactTag];
|
||||
RCTCAssert(existingView == nil || existingView == rootView,
|
||||
@"Expect all root views to have unique tag. Added %@ twice", reactTag);
|
||||
|
||||
|
||||
// Register view
|
||||
_viewRegistry[reactTag] = rootView;
|
||||
CGRect frame = rootView.frame;
|
||||
|
||||
|
||||
// Register manager (TODO: should we do this, or leave it nil?)
|
||||
_viewManagerRegistry[reactTag] = _viewManagers[[RCTViewManager moduleName]];
|
||||
|
||||
_viewManagerRegistry[reactTag] = _viewManagers[@"View"];
|
||||
|
||||
// Register shadow view
|
||||
dispatch_async(_bridge.shadowQueue, ^{
|
||||
|
||||
dispatch_async(_shadowQueue, ^{
|
||||
|
||||
RCTShadowView *shadowView = [[RCTShadowView alloc] init];
|
||||
shadowView.reactTag = reactTag;
|
||||
shadowView.frame = frame;
|
||||
@ -315,7 +316,7 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
- (void)addUIBlock:(RCTViewManagerUIBlock)block
|
||||
{
|
||||
RCTAssert(![NSThread isMainThread], @"This method should only be called on the shadow thread");
|
||||
|
||||
|
||||
__weak RCTUIManager *weakViewManager = self;
|
||||
__weak RCTSparseArray *weakViewRegistry = _viewRegistry;
|
||||
dispatch_block_t outerBlock = ^{
|
||||
@ -333,6 +334,8 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockWithLayoutUpdateForRootView:(RCTShadowView *)rootShadowView
|
||||
{
|
||||
RCTAssert(![NSThread isMainThread], @"This should never be executed on main thread.");
|
||||
|
||||
NSMutableSet *viewsWithNewFrames = [NSMutableSet setWithCapacity:1];
|
||||
|
||||
// This is nuanced. In the JS thread, we create a new update buffer
|
||||
@ -340,7 +343,8 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
// these structures in the UI-thread block. `NSMutableArray` is not thread
|
||||
// safe so we rely on the fact that we never mutate it after it's passed to
|
||||
// the main thread.
|
||||
[rootShadowView collectRootUpdatedFrames:viewsWithNewFrames parentConstraint:(CGSize){CSS_UNDEFINED, CSS_UNDEFINED}];
|
||||
[rootShadowView collectRootUpdatedFrames:viewsWithNewFrames
|
||||
parentConstraint:(CGSize){CSS_UNDEFINED, CSS_UNDEFINED}];
|
||||
|
||||
// Parallel arrays
|
||||
NSMutableArray *frameReactTags = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count];
|
||||
@ -354,12 +358,19 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
[areNew addObject:@(shadowView.isNewView)];
|
||||
[parentsAreNew addObject:@(shadowView.superview.isNewView)];
|
||||
}
|
||||
|
||||
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
// We have to do this after we build the parentsAreNew array.
|
||||
shadowView.newView = NO;
|
||||
}
|
||||
|
||||
NSMutableArray *updateBlocks = [[NSMutableArray alloc] init];
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
RCTViewManager *manager = _viewManagerRegistry[shadowView.reactTag];
|
||||
RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView];
|
||||
if (block) [updateBlocks addObject:block];
|
||||
}
|
||||
|
||||
// Perform layout (possibly animated)
|
||||
NSNumber *rootViewTag = rootShadowView.reactTag;
|
||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
@ -373,15 +384,16 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
// 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 (%zd)%@. position: %@. bounds: %@", [view reactTag], self, NSStringFromCGPoint(position), NSStringFromCGRect(bounds));
|
||||
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)]);
|
||||
@ -395,14 +407,20 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
[updateAnimation performAnimations:^{
|
||||
view.layer.position = position;
|
||||
view.layer.bounds = bounds;
|
||||
for (RCTViewManagerUIBlock block in updateBlocks) {
|
||||
block(self, _viewRegistry);
|
||||
}
|
||||
} withCompletionBlock:completion];
|
||||
} else {
|
||||
view.layer.position = position;
|
||||
view.layer.bounds = bounds;
|
||||
for (RCTViewManagerUIBlock block in updateBlocks) {
|
||||
block(self, _viewRegistry);
|
||||
}
|
||||
completion(YES);
|
||||
}
|
||||
|
||||
// Animate view creations
|
||||
|
||||
// Animate view creation
|
||||
BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue];
|
||||
RCTAnimation *createAnimation = _layoutAnimation.createAnimation;
|
||||
if (shouldAnimateCreation && createAnimation) {
|
||||
@ -417,12 +435,22 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
} else if ([createAnimation.property isEqual:@"opacity"]) {
|
||||
view.layer.opacity = 1.0;
|
||||
} else {
|
||||
RCTLogError(@"Unsupported layout animation createConfig property %@", createAnimation.property);
|
||||
RCTLogError(@"Unsupported layout animation createConfig property %@",
|
||||
createAnimation.property);
|
||||
}
|
||||
for (RCTViewManagerUIBlock block in updateBlocks) {
|
||||
block(self, _viewRegistry);
|
||||
}
|
||||
} withCompletionBlock:nil];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerate all active (attached to a parent) views and call
|
||||
* reactBridgeDidFinishTransaction on them if they implement it.
|
||||
* TODO: this is quite inefficient. If this was handled via the
|
||||
* ViewManager instead, it could be done more efficiently.
|
||||
*/
|
||||
RCTRootView *rootView = _viewRegistry[rootViewTag];
|
||||
RCTTraverseViewNodes(rootView, ^(id<RCTViewNodeProtocol> view) {
|
||||
if ([view respondsToSelector:@selector(reactBridgeDidFinishTransaction)]) {
|
||||
@ -436,7 +464,7 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
{
|
||||
NSMutableSet *applierBlocks = [NSMutableSet setWithCapacity:1];
|
||||
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
|
||||
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
for (RCTApplierBlock block in applierBlocks) {
|
||||
block(viewRegistry);
|
||||
@ -454,7 +482,7 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
|
||||
|
||||
id<RCTViewNodeProtocol> container = _viewRegistry[containerID];
|
||||
RCTAssert(container != nil, @"container view (for ID %@) not found", containerID);
|
||||
|
||||
|
||||
NSUInteger subviewsCount = [[container reactSubviews] count];
|
||||
NSMutableArray *indices = [[NSMutableArray alloc] initWithCapacity:subviewsCount];
|
||||
for (NSInteger childIndex = 0; childIndex < subviewsCount; childIndex++) {
|
||||
@ -616,11 +644,11 @@ static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView,
|
||||
{
|
||||
// TODO: cache respondsToSelector tests
|
||||
if ([manager respondsToSelector:setter]) {
|
||||
|
||||
|
||||
if (value == [NSNull null]) {
|
||||
value = nil;
|
||||
}
|
||||
|
||||
|
||||
((void (*)(id, SEL, id, id, id))objc_msgSend)(manager, setter, value, view, defaultView);
|
||||
return YES;
|
||||
}
|
||||
@ -631,9 +659,9 @@ static void RCTSetViewProps(NSDictionary *props, UIView *view,
|
||||
UIView *defaultView, RCTViewManager *manager)
|
||||
{
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
|
||||
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:forView:withDefaultView:", key]);
|
||||
|
||||
|
||||
// For regular views we don't attempt to set properties
|
||||
// unless the view property has been explicitly exported.
|
||||
RCTCallPropertySetter(setter, obj, view, defaultView, manager);
|
||||
@ -644,13 +672,13 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
RCTShadowView *defaultView, RCTViewManager *manager)
|
||||
{
|
||||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
|
||||
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:forShadowView:withDefaultView:", key]);
|
||||
|
||||
|
||||
// For shadow views we call any custom setter methods by default,
|
||||
// but if none is specified, we attempt to set property anyway.
|
||||
if (!RCTCallPropertySetter(setter, obj, shadowView, defaultView, manager)) {
|
||||
|
||||
|
||||
if (obj == [NSNull null]) {
|
||||
// Copy property from default view to current
|
||||
// Note: not just doing `[defaultView valueForKey:key]`, the
|
||||
@ -661,20 +689,20 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
// Update layout
|
||||
[shadowView updateShadowViewLayout];
|
||||
}
|
||||
|
||||
- (void)createAndRegisterViewWithReactTag:(NSNumber *)reactTag
|
||||
moduleName:(NSString *)moduleName
|
||||
viewName:(NSString *)viewName
|
||||
props:(NSDictionary *)props
|
||||
{
|
||||
RCT_EXPORT(createView);
|
||||
|
||||
RCTViewManager *manager = _viewManagers[moduleName];
|
||||
RCTViewManager *manager = _viewManagers[viewName];
|
||||
if (manager == nil) {
|
||||
RCTLogWarn(@"No manager class found for view with module name \"%@\"", moduleName);
|
||||
RCTLogWarn(@"No manager class found for view with module name \"%@\"", viewName);
|
||||
manager = [[RCTViewManager alloc] init];
|
||||
}
|
||||
|
||||
@ -682,57 +710,57 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
_viewManagerRegistry[reactTag] = manager;
|
||||
|
||||
// Generate default view, used for resetting default props
|
||||
if (!_defaultShadowViews[moduleName]) {
|
||||
_defaultShadowViews[moduleName] = [manager shadowView];
|
||||
if (!_defaultShadowViews[viewName]) {
|
||||
_defaultShadowViews[viewName] = [manager shadowView];
|
||||
}
|
||||
|
||||
|
||||
RCTShadowView *shadowView = [manager shadowView];
|
||||
shadowView.moduleName = moduleName;
|
||||
shadowView.moduleName = viewName;
|
||||
shadowView.reactTag = reactTag;
|
||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], manager);
|
||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[viewName], manager);
|
||||
_shadowViewRegistry[shadowView.reactTag] = shadowView;
|
||||
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||
RCTCAssertMainThread();
|
||||
|
||||
// Generate default view, used for resetting default props
|
||||
if (!uiManager->_defaultViews[moduleName]) {
|
||||
if (!uiManager->_defaultViews[viewName]) {
|
||||
// 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] = [manager view];
|
||||
uiManager->_defaultViews[viewName] = [manager view];
|
||||
}
|
||||
|
||||
|
||||
UIView *view = [manager view];
|
||||
if (view) {
|
||||
|
||||
|
||||
// Set required properties
|
||||
view.reactTag = reactTag;
|
||||
view.multipleTouchEnabled = YES;
|
||||
view.userInteractionEnabled = YES; // required for touch handling
|
||||
view.layer.allowsGroupOpacity = YES; // required for touch handling
|
||||
|
||||
|
||||
// Set custom properties
|
||||
RCTSetViewProps(props, view, uiManager->_defaultViews[moduleName], manager);
|
||||
RCTSetViewProps(props, view, uiManager->_defaultViews[viewName], manager);
|
||||
}
|
||||
viewRegistry[view.reactTag] = view;
|
||||
}];
|
||||
}
|
||||
|
||||
// TODO: remove moduleName param as it isn't needed
|
||||
- (void)updateView:(NSNumber *)reactTag moduleName:(__unused NSString *)_ props:(NSDictionary *)props
|
||||
// TODO: remove viewName param as it isn't needed
|
||||
- (void)updateView:(NSNumber *)reactTag viewName:(__unused NSString *)_ props:(NSDictionary *)props
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
RCTViewManager *viewManager = _viewManagerRegistry[reactTag];
|
||||
NSString *moduleName = [[viewManager class] moduleName];
|
||||
|
||||
NSString *viewName = RCTViewNameForModuleName([[viewManager class] moduleName]);
|
||||
|
||||
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[moduleName], viewManager);
|
||||
|
||||
RCTSetShadowViewProps(props, shadowView, _defaultShadowViews[viewName], viewManager);
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
UIView *view = uiManager->_viewRegistry[reactTag];
|
||||
RCTSetViewProps(props, view, uiManager->_defaultViews[moduleName], viewManager);
|
||||
RCTSetViewProps(props, view, uiManager->_defaultViews[viewName], viewManager);
|
||||
}];
|
||||
}
|
||||
|
||||
@ -760,16 +788,15 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
|
||||
- (void)batchDidComplete
|
||||
{
|
||||
// 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.
|
||||
// Gather blocks to be executed now that all view hierarchy manipulations have
|
||||
// been completed (note that these may still take place before layout has finished)
|
||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [manager uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||
if (uiBlock) {
|
||||
[self addUIBlock:uiBlock];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set up next layout animation
|
||||
if (_nextLayoutAnimation) {
|
||||
RCTLayoutAnimation *layoutAnimation = _nextLayoutAnimation;
|
||||
@ -777,14 +804,14 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
uiManager->_layoutAnimation = layoutAnimation;
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
// Perform layout
|
||||
for (NSNumber *reactTag in _rootViewTags) {
|
||||
RCTShadowView *rootView = _shadowViewRegistry[reactTag];
|
||||
[self addUIBlock:[self uiBlockWithLayoutUpdateForRootView:rootView]];
|
||||
[self _amendPendingUIBlocksWithStylePropagationUpdateForRootView:rootView];
|
||||
}
|
||||
|
||||
|
||||
// Clear layout animations
|
||||
if (_nextLayoutAnimation) {
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
@ -792,12 +819,16 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
}];
|
||||
_nextLayoutAnimation = nil;
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
[_pendingUIBlocksLock lock];
|
||||
NSArray *previousPendingUIBlocks = _pendingUIBlocks;
|
||||
_pendingUIBlocks = [[NSMutableArray alloc] init];
|
||||
[_pendingUIBlocksLock unlock];
|
||||
|
||||
|
||||
// Execute the previously queued UI blocks
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
for (dispatch_block_t block in previousPendingUIBlocks) {
|
||||
block();
|
||||
@ -828,7 +859,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
}
|
||||
|
||||
RCTCAssert([rootView isReactRootView], @"React view not inside RCTRootView");
|
||||
|
||||
|
||||
// By convention, all coordinates, whether they be touch coordinates, or
|
||||
// measurement coordinates are with respect to the root view.
|
||||
CGPoint pagePoint = [view.superview convertPoint:frame.origin toView:rootView];
|
||||
@ -844,37 +875,6 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)requestSchedulingJavaScriptNavigation:(NSNumber *)reactTag
|
||||
errorCallback:(RCTResponseSenderBlock)errorCallback
|
||||
callback:(RCTResponseSenderBlock)callback
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
if (!callback || !errorCallback) {
|
||||
RCTLogError(@"Callback not provided for navigation scheduling.");
|
||||
return;
|
||||
}
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||
if (reactTag) {
|
||||
//TODO: This is nasty - why is RCTNavigator hard-coded?
|
||||
id rkObject = viewRegistry[reactTag];
|
||||
if ([rkObject isKindOfClass:[RCTNavigator class]]) {
|
||||
RCTNavigator *navigator = (RCTNavigator *)rkObject;
|
||||
BOOL wasAcquired = [navigator requestSchedulingJavaScriptNavigation];
|
||||
callback(@[@(wasAcquired)]);
|
||||
} else {
|
||||
NSString *msg =
|
||||
[NSString stringWithFormat: @"Cannot set lock: Tag %@ is not an RCTNavigator", reactTag];
|
||||
errorCallback(@[RCTAPIErrorObject(msg)]);
|
||||
}
|
||||
} else {
|
||||
NSString *msg = [NSString stringWithFormat: @"Tag not specified for requestSchedulingJavaScriptNavigation"];
|
||||
errorCallback(@[RCTAPIErrorObject(msg)]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO: This could be modified to accept any `RCTViewNodeProtocol`, if
|
||||
* appropriate changes were made to that protocol to support `superview`
|
||||
@ -884,22 +884,20 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
+ (void)measureLayoutOnNodes:(RCTShadowView *)view
|
||||
ancestor:(RCTShadowView *)ancestor
|
||||
errorCallback:(RCTResponseSenderBlock)errorCallback
|
||||
callback:(RCTResponseSenderBlock)callback
|
||||
callback:(__unused RCTResponseSenderBlock)callback
|
||||
{
|
||||
if (!view) {
|
||||
NSString *msg = [NSString stringWithFormat: @"Attempting to measure view that does not exist %@", view];
|
||||
errorCallback(@[RCTAPIErrorObject(msg)]);
|
||||
RCTLogError(@"Attempting to measure view that does not exist");
|
||||
return;
|
||||
}
|
||||
if (!ancestor) {
|
||||
NSString *msg = [NSString stringWithFormat: @"Attempting to measure relative to ancestor that does not exist %@", ancestor];
|
||||
errorCallback(@[RCTAPIErrorObject(msg)]);
|
||||
RCTLogError(@"Attempting to measure relative to ancestor that does not exist");
|
||||
return;
|
||||
}
|
||||
CGRect result = [RCTShadowView measureLayout:view relativeTo:ancestor];
|
||||
if (CGRectIsNull(result)) {
|
||||
NSString *msg = [NSString stringWithFormat: @"view %@ is not an decendant of %@", view, ancestor];
|
||||
errorCallback(@[RCTAPIErrorObject(msg)]);
|
||||
RCTLogError(@"view %@ (tag #%@) is not a decendant of %@ (tag #%@)",
|
||||
view, view.reactTag, ancestor, ancestor.reactTag);
|
||||
return;
|
||||
}
|
||||
CGFloat leftOffset = result.origin.x;
|
||||
@ -907,7 +905,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
CGFloat width = result.size.width;
|
||||
CGFloat height = result.size.height;
|
||||
if (isnan(leftOffset) || isnan(topOffset) || isnan(width) || isnan(height)) {
|
||||
errorCallback(@[RCTAPIErrorObject(@"Attempted to measure layout but offset or dimensions were NaN")]);
|
||||
RCTLogError(@"Attempted to measure layout but offset or dimensions were NaN");
|
||||
return;
|
||||
}
|
||||
callback(@[@(topOffset), @(leftOffset), @(width), @(height)]);
|
||||
@ -965,8 +963,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
|
||||
RCTShadowView *shadowView = _shadowViewRegistry[reactTag];
|
||||
if (!shadowView) {
|
||||
NSString *msg = [NSString stringWithFormat: @"Attempting to measure view that does not exist %@", shadowView];
|
||||
errorCallback(@[RCTAPIErrorObject(msg)]);
|
||||
RCTLogError(@"Attempting to measure view that does not exist (tag #%@)", reactTag);
|
||||
return;
|
||||
}
|
||||
NSArray *childShadowViews = [shadowView reactSubviews];
|
||||
@ -977,8 +974,8 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
RCTShadowView *childShadowView = [childShadowViews objectAtIndex:ii];
|
||||
CGRect childLayout = [RCTShadowView measureLayout:childShadowView relativeTo:shadowView];
|
||||
if (CGRectIsNull(childLayout)) {
|
||||
NSString *msg = [NSString stringWithFormat: @"view %@ is not a decendant of %@", childShadowView, shadowView];
|
||||
errorCallback(@[RCTAPIErrorObject(msg)]);
|
||||
RCTLogError(@"View %@ (tag #%@) is not a decendant of %@ (tag #%@)",
|
||||
childShadowView, childShadowView.reactTag, shadowView, shadowView.reactTag);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1088,7 +1085,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
_jsResponder = viewRegistry[reactTag];
|
||||
if (!_jsResponder) {
|
||||
RCTLogMustFix(@"Invalid view set to be the JS responder - tag %zd", reactTag);
|
||||
RCTLogError(@"Invalid view set to be the JS responder - tag %zd", reactTag);
|
||||
}
|
||||
}];
|
||||
}
|
||||
@ -1102,7 +1099,11 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
}];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)allBubblingEventTypesConfigs
|
||||
// TODO: these event types should be distributed among the modules
|
||||
// that declare them. Also, events should be registerable by any class
|
||||
// that can call event handlers, not just UIViewManagers. This code
|
||||
// also seems highly redundant - every event has the same properties.
|
||||
- (NSDictionary *)customBubblingEventTypes
|
||||
{
|
||||
NSMutableDictionary *customBubblingEventTypesConfigs = [@{
|
||||
// Bubble dispatched events
|
||||
@ -1192,11 +1193,12 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
},
|
||||
} mutableCopy];
|
||||
|
||||
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
|
||||
[_viewManagers 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);
|
||||
RCTCAssert(!customBubblingEventTypesConfigs[eventName],
|
||||
@"Event '%@' registered multiple times.", eventName);
|
||||
}
|
||||
[customBubblingEventTypesConfigs addEntriesFromDictionary:eventTypes];
|
||||
}
|
||||
@ -1205,7 +1207,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
return customBubblingEventTypesConfigs;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)allDirectEventTypesConfigs
|
||||
- (NSDictionary *)customDirectEventTypes
|
||||
{
|
||||
NSMutableDictionary *customDirectEventTypes = [@{
|
||||
@"topScrollBeginDrag": @{
|
||||
@ -1243,7 +1245,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
},
|
||||
} mutableCopy];
|
||||
|
||||
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
|
||||
[_viewManagers enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
|
||||
if (RCTClassOverridesClassMethod(cls, @selector(customDirectEventTypes))) {
|
||||
NSDictionary *eventTypes = [cls customDirectEventTypes];
|
||||
for (NSString *eventName in eventTypes) {
|
||||
@ -1256,11 +1258,11 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
return customDirectEventTypes;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)constantsToExport
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
NSMutableDictionary *allJSConstants = [@{
|
||||
@"customBubblingEventTypes": [self allBubblingEventTypesConfigs],
|
||||
@"customDirectEventTypes": [self allDirectEventTypesConfigs],
|
||||
@"customBubblingEventTypes": [self customBubblingEventTypes],
|
||||
@"customDirectEventTypes": [self customDirectEventTypes],
|
||||
@"NSTextAlignment": @{
|
||||
@"Left": @(NSTextAlignmentLeft),
|
||||
@"Center": @(NSTextAlignmentCenter),
|
||||
@ -1320,15 +1322,15 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
},
|
||||
} mutableCopy];
|
||||
|
||||
[RCTViewModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *name, Class cls, BOOL *stop) {
|
||||
[_viewManagers 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);
|
||||
NSMutableDictionary *constantsNamespace = [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]];
|
||||
RCTAssert(constantsNamespace[@"Constants"] == nil , @"Cannot redefine Constants in namespace: %@", name);
|
||||
// add an additional 'Constants' namespace for each class
|
||||
namespace[@"Constants"] = constants;
|
||||
allJSConstants[name] = [namespace copy];
|
||||
constantsNamespace[@"Constants"] = constants;
|
||||
allJSConstants[name] = [constantsNamespace copy];
|
||||
}
|
||||
}];
|
||||
|
||||
@ -1342,10 +1344,11 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
RCT_EXPORT();
|
||||
|
||||
if (_nextLayoutAnimation) {
|
||||
RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.", _nextLayoutAnimation, config);
|
||||
RCTLogWarn(@"Warning: Overriding previous layout animation with new one before the first began:\n%@ -> %@.",
|
||||
_nextLayoutAnimation, config);
|
||||
}
|
||||
if (config[@"delete"] != nil) {
|
||||
RCTLogError(@"LayoutAnimation only supports create and update right now. Config: %@", config);
|
||||
RCTLogError(@"LayoutAnimation only supports create and update right now. Config: %@", config);
|
||||
}
|
||||
_nextLayoutAnimation = [[RCTLayoutAnimation alloc] initWithDictionary:config callback:callback];
|
||||
}
|
||||
@ -1389,3 +1392,12 @@ static UIView *_jsResponder;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBridge (RCTUIManager)
|
||||
|
||||
- (RCTUIManager *)uiManager
|
||||
{
|
||||
return self.modules[NSStringFromClass([RCTUIManager class])];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -11,7 +11,7 @@
|
||||
@property (nonatomic, strong) UIView *reactNavSuperviewLink;
|
||||
@property (nonatomic, assign) NSInteger requestedTopOfStack;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Schedules a JavaScript navigation and prevents `UIKit` from navigating until
|
||||
|
@ -126,21 +126,6 @@ NSInteger kNeverProgressed = -10000;
|
||||
*/
|
||||
@implementation RCTNavigationController
|
||||
|
||||
- (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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callback Callback that is invoked when a "scroll" interaction begins
|
||||
* so that `RCTNavigator` can notify `JavaScript`.
|
||||
@ -275,11 +260,6 @@ NSInteger kNeverProgressed = -10000;
|
||||
|
||||
@implementation RCTNavigator
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (id)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
@ -308,7 +288,6 @@ NSInteger kNeverProgressed = -10000;
|
||||
[self addSubview:_navigationController.view];
|
||||
[_navigationController.view addSubview:_dummyView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -363,8 +342,8 @@ NSInteger kNeverProgressed = -10000;
|
||||
(RCTWrapperViewController *)[context viewControllerForKey:UITransitionContextToViewControllerKey];
|
||||
NSUInteger indexOfFrom = [_currentViews indexOfObject:fromController.navItem];
|
||||
NSUInteger indexOfTo = [_currentViews indexOfObject:toController.navItem];
|
||||
CGFloat destination = indexOfFrom < indexOfTo ? 1.0f : -1.0f;
|
||||
_dummyView.frame = CGRectMake(destination, 0.0f, 0.0f, 0.0f);
|
||||
CGFloat destination = indexOfFrom < indexOfTo ? 1.0 : -1.0;
|
||||
_dummyView.frame = (CGRect){destination};
|
||||
_currentlyTransitioningFrom = indexOfFrom;
|
||||
_currentlyTransitioningTo = indexOfTo;
|
||||
if (indexOfFrom != indexOfTo) {
|
||||
@ -375,7 +354,7 @@ NSInteger kNeverProgressed = -10000;
|
||||
[weakSelf freeLock];
|
||||
_currentlyTransitioningFrom = 0;
|
||||
_currentlyTransitioningTo = 0;
|
||||
_dummyView.frame = CGRectMake(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
_dummyView.frame = CGRectZero;
|
||||
_displayLink.paused = YES;
|
||||
// Reset the parallel position tracker
|
||||
}];
|
||||
@ -462,7 +441,7 @@ NSInteger kNeverProgressed = -10000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be overridden because UIKit destroys the views superview link when used
|
||||
* Must be overridden because UIKit removes the view's superview when used
|
||||
* as a navigator - it's considered outside the view hierarchy.
|
||||
*/
|
||||
- (UIView *)reactSuperview
|
||||
@ -490,7 +469,7 @@ NSInteger kNeverProgressed = -10000;
|
||||
// we can't hook up the VC hierarchy in 'init' because the subviews aren't hooked up yet,
|
||||
// so we do it on demand here
|
||||
[self addControllerToClosestParent:_navigationController];
|
||||
|
||||
|
||||
NSInteger viewControllerCount = _navigationController.viewControllers.count;
|
||||
// The "react count" is the count of views that are visible on the navigation
|
||||
// stack. There may be more beyond this - that aren't visible, and may be
|
||||
@ -563,7 +542,6 @@ NSInteger kNeverProgressed = -10000;
|
||||
_previousRequestedTopOfStack = _requestedTopOfStack;
|
||||
}
|
||||
|
||||
|
||||
// TODO: This will likely fail when performing multiple pushes/pops. We must
|
||||
// free the lock only after the *last* push/pop.
|
||||
- (void)wrapperViewController:(RCTWrapperViewController *)wrapperViewController
|
||||
@ -574,7 +552,7 @@ didMoveToNavigationController:(UINavigationController *)navigationController
|
||||
// while a push/pop is in progress.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
RCTAssert(
|
||||
(navigationController == nil || [_navigationController.viewControllers containsObject:wrapperViewController]),
|
||||
@"if navigation controller is not nil, it should container the wrapper view controller"
|
||||
|
@ -5,6 +5,8 @@
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTNavigator.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUIManager.h"
|
||||
|
||||
@implementation RCTNavigatorManager
|
||||
|
||||
@ -24,5 +26,26 @@ RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack)
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
// TODO: remove error callbacks
|
||||
- (void)requestSchedulingJavaScriptNavigation:(NSNumber *)reactTag
|
||||
errorCallback:(RCTResponseSenderBlock)errorCallback
|
||||
callback:(__unused RCTResponseSenderBlock)callback
|
||||
{
|
||||
RCT_EXPORT();
|
||||
|
||||
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||
if (reactTag) {
|
||||
RCTNavigator *navigator = viewRegistry[reactTag];
|
||||
if ([navigator isKindOfClass:[RCTNavigator class]]) {
|
||||
BOOL wasAcquired = [navigator requestSchedulingJavaScriptNavigation];
|
||||
callback(@[@(wasAcquired)]);
|
||||
} else {
|
||||
RCTLogError(@"Cannot set lock: %@ (tag #%@) is not an RCTNavigator", navigator, reactTag);
|
||||
}
|
||||
} else {
|
||||
RCTLogError(@"Tag not specified for requestSchedulingJavaScriptNavigation");
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
@interface RCTScrollView : RCTView <UIScrollViewDelegate, RCTScrollableProtocol, RCTAutoInsetsProtocol>
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* If the `contentSize` is not provided, then the `contentSize` will
|
||||
* automatically be determined by the size of the `RKScrollView` subview.
|
||||
@ -32,6 +34,4 @@
|
||||
@property (nonatomic, assign) BOOL centerContent;
|
||||
@property (nonatomic, copy) NSArray *stickyHeaderIndices;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
|
||||
@end
|
||||
|
@ -31,8 +31,7 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
[self.panGestureRecognizer addTarget:self action:@selector(handleCustomPan:)];
|
||||
}
|
||||
return self;
|
||||
@ -257,11 +256,6 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
|
||||
|
||||
@synthesize nativeMainScrollDelegate = _nativeMainScrollDelegate;
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
|
@ -31,23 +31,22 @@ RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator)
|
||||
RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator)
|
||||
RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices);
|
||||
RCT_EXPORT_VIEW_PROPERTY(throttleScrollCallbackMS);
|
||||
RCT_EXPORT_VIEW_PROPERTY(zoomScale);
|
||||
RCT_EXPORT_VIEW_PROPERTY(zoomScale); // TODO: this needs to be set first because it resets other props like contentOffset
|
||||
RCT_EXPORT_VIEW_PROPERTY(contentInset);
|
||||
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets);
|
||||
RCT_EXPORT_VIEW_PROPERTY(contentOffset);
|
||||
|
||||
+ (NSDictionary *)constantsToExport
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return
|
||||
@{
|
||||
return @{
|
||||
@"DecelerationRate": @{
|
||||
@"Normal": @(UIScrollViewDecelerationRateNormal),
|
||||
@"Fast": @(UIScrollViewDecelerationRateFast),
|
||||
@"Normal": @(UIScrollViewDecelerationRateNormal),
|
||||
@"Fast": @(UIScrollViewDecelerationRateFast),
|
||||
},
|
||||
@"KeyboardDismissMode": @{
|
||||
@"None": @(UIScrollViewKeyboardDismissModeNone),
|
||||
@"Interactive": @(UIScrollViewKeyboardDismissModeInteractive),
|
||||
@"OnDrag": @(UIScrollViewKeyboardDismissModeOnDrag),
|
||||
@"None": @(UIScrollViewKeyboardDismissModeNone),
|
||||
@"Interactive": @(UIScrollViewKeyboardDismissModeInteractive),
|
||||
@"OnDrag": @(UIScrollViewKeyboardDismissModeOnDrag),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -3,29 +3,15 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "Layout.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTViewNodeProtocol.h"
|
||||
|
||||
@class RCTSparseArray;
|
||||
|
||||
// TODO: amalgamate these enums?
|
||||
typedef NS_ENUM(NSUInteger, RCTLayoutLifecycle) {
|
||||
RCTLayoutLifecycleUninitialized = 0,
|
||||
RCTLayoutLifecycleComputed,
|
||||
RCTLayoutLifecycleDirtied,
|
||||
};
|
||||
|
||||
// TODO: is this still needed?
|
||||
typedef NS_ENUM(NSUInteger, RCTPropagationLifecycle) {
|
||||
RCTPropagationLifecycleUninitialized = 0,
|
||||
RCTPropagationLifecycleComputed,
|
||||
RCTPropagationLifecycleDirtied,
|
||||
};
|
||||
|
||||
// TODO: move this to text node?
|
||||
typedef NS_ENUM(NSUInteger, RCTTextLifecycle) {
|
||||
RCTTextLifecycleUninitialized = 0,
|
||||
RCTTextLifecycleComputed,
|
||||
RCTTextLifecycleDirtied,
|
||||
typedef NS_ENUM(NSUInteger, RCTUpdateLifecycle) {
|
||||
RCTUpdateLifecycleUninitialized = 0,
|
||||
RCTUpdateLifecycleComputed,
|
||||
RCTUpdateLifecycleDirtied,
|
||||
};
|
||||
|
||||
// TODO: is this redundact now?
|
||||
@ -48,7 +34,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
||||
@property (nonatomic, copy) NSString *moduleName;
|
||||
@property (nonatomic, assign) BOOL isBGColorExplicitlySet; // Used to propogate to children
|
||||
@property (nonatomic, strong) UIColor *backgroundColor; // Used to propogate to children
|
||||
@property (nonatomic, assign) RCTLayoutLifecycle layoutLifecycle;
|
||||
@property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle;
|
||||
|
||||
/**
|
||||
* isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is
|
||||
@ -122,11 +108,23 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
||||
@property (nonatomic, assign) css_wrap_type_t flexWrap;
|
||||
@property (nonatomic, assign) CGFloat flex;
|
||||
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)viewsWithNewProperties parentProperties:(NSDictionary *)parentProperties;
|
||||
- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame parentConstraint:(CGSize)parentConstraint;
|
||||
- (void)fillCSSNode:(css_node_t *)node;
|
||||
/**
|
||||
* Calculate property changes that need to be propagated to the view.
|
||||
* The applierBlocks set contains RCTApplierBlock functions that must be applied
|
||||
* on the main thread in order to update the view.
|
||||
*/
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties;
|
||||
|
||||
// The following are implementation details exposed to subclasses. Do not call them directly
|
||||
/**
|
||||
* Calculate all views whose frame needs updating after layout has been calculated.
|
||||
* The viewsWithNewFrame set contains the reactTags of the views that need updating.
|
||||
*/
|
||||
- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame parentConstraint:(CGSize)parentConstraint;
|
||||
|
||||
/**
|
||||
* The following are implementation details exposed to subclasses. Do not call them directly
|
||||
*/
|
||||
- (void)fillCSSNode:(css_node_t *)node;
|
||||
- (void)dirtyLayout;
|
||||
- (BOOL)isLayoutDirty;
|
||||
|
||||
|
@ -10,46 +10,6 @@
|
||||
typedef void (^RCTActionBlock)(RCTShadowView *shadowViewSelf, id value);
|
||||
typedef void (^RCTResetActionBlock)(RCTShadowView *shadowViewSelf);
|
||||
|
||||
@interface RCTLayoutAction : NSObject
|
||||
|
||||
@property (nonatomic, readwrite, copy) RCTActionBlock block;
|
||||
@property (nonatomic, readwrite, copy) RCTResetActionBlock resetBlock;
|
||||
@property (nonatomic, readwrite, assign) NSInteger precedence;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTLayoutAction @end
|
||||
|
||||
#define ACTION_FOR_KEY_DEFAULT(name, default, blockIn) \
|
||||
do { \
|
||||
RCTLayoutAction *action = [[RCTLayoutAction alloc] init]; \
|
||||
action.block = blockIn; \
|
||||
action.resetBlock = ^(id idSelf1) { \
|
||||
blockIn(idSelf1, default); \
|
||||
}; \
|
||||
actions[@"" #name ""] = action; \
|
||||
} while(0)
|
||||
|
||||
#define ACTION_FOR_KEY(name, blockIn) \
|
||||
ACTION_FOR_KEY_DEFAULT(name, @([defaultShadowView name]), (blockIn))
|
||||
|
||||
#define ACTION_FOR_FLOAT_KEY_DEFAULT(name, default, blockIn) \
|
||||
ACTION_FOR_KEY_DEFAULT(name, @(default), ^(id idSelf2, NSNumber *n) { \
|
||||
if (isnan([n floatValue])) { \
|
||||
RCTLogWarn(@"Got NaN for `"#name"` prop, ignoring"); \
|
||||
return; \
|
||||
} \
|
||||
blockIn(idSelf2, RCTNumberToFloat(n)); \
|
||||
});
|
||||
|
||||
#define ACTION_FOR_FLOAT_KEY(name, blockIn) \
|
||||
ACTION_FOR_FLOAT_KEY_DEFAULT(name, [defaultShadowView name], (blockIn))
|
||||
|
||||
#define ACTION_FOR_DEFAULT_UNDEFINED_KEY(name, blockIn) \
|
||||
ACTION_FOR_KEY_DEFAULT(name, nil, ^(id idSelf2, NSNumber *n) { \
|
||||
blockIn(idSelf2, n == nil ? CSS_UNDEFINED : [n floatValue]); \
|
||||
});
|
||||
|
||||
#define MAX_TREE_DEPTH 30
|
||||
|
||||
const NSString *const RCTBackgroundColorProp = @"backgroundColor";
|
||||
@ -65,22 +25,16 @@ typedef enum {
|
||||
META_PROP_COUNT,
|
||||
} meta_prop_t;
|
||||
|
||||
@interface RCTShadowView()
|
||||
{
|
||||
float _paddingMetaProps[META_PROP_COUNT];
|
||||
float _marginMetaProps[META_PROP_COUNT];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTShadowView
|
||||
{
|
||||
RCTPropagationLifecycle _propagationLifecycle;
|
||||
RCTTextLifecycle _textLifecycle;
|
||||
RCTUpdateLifecycle _propagationLifecycle;
|
||||
RCTUpdateLifecycle _textLifecycle;
|
||||
NSDictionary *_lastParentProperties;
|
||||
NSMutableArray *_reactSubviews;
|
||||
BOOL _recomputePadding;
|
||||
BOOL _recomputeMargin;
|
||||
float _paddingMetaProps[META_PROP_COUNT];
|
||||
float _marginMetaProps[META_PROP_COUNT];
|
||||
}
|
||||
|
||||
@synthesize reactTag = _reactTag;
|
||||
@ -131,11 +85,6 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
node->children_count = (int)_reactSubviews.count;
|
||||
}
|
||||
|
||||
- (void)applyLayoutNode:(css_node_t *)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame
|
||||
{
|
||||
[self _applyLayoutNode:node viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero];
|
||||
}
|
||||
|
||||
// The absolute stuff is so that we can take into account our absolute position when rounding in order to
|
||||
// snap to the pixel grid. For example, say you have the following structure:
|
||||
//
|
||||
@ -165,24 +114,24 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
// width = 213.5 - 106.5 = 107
|
||||
// You'll notice that this is the same width we calculated for the parent view because we've taken its position into account.
|
||||
|
||||
- (void)_applyLayoutNode:(css_node_t *)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition
|
||||
- (void)applyLayoutNode:(css_node_t *)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition
|
||||
{
|
||||
if (!node->layout.should_update) {
|
||||
return;
|
||||
}
|
||||
node->layout.should_update = false;
|
||||
_layoutLifecycle = RCTLayoutLifecycleComputed;
|
||||
_layoutLifecycle = RCTUpdateLifecycleComputed;
|
||||
|
||||
CGPoint absoluteTopLeft = {
|
||||
RCTRoundPixelValue(absolutePosition.x + node->layout.position[CSS_LEFT]),
|
||||
RCTRoundPixelValue(absolutePosition.y + node->layout.position[CSS_TOP])
|
||||
};
|
||||
|
||||
|
||||
CGPoint absoluteBottomRight = {
|
||||
RCTRoundPixelValue(absolutePosition.x + node->layout.position[CSS_LEFT] + node->layout.dimensions[CSS_WIDTH]),
|
||||
RCTRoundPixelValue(absolutePosition.y + node->layout.position[CSS_TOP] + node->layout.dimensions[CSS_HEIGHT])
|
||||
};
|
||||
|
||||
|
||||
CGRect frame = {
|
||||
RCTRoundPixelValue(node->layout.position[CSS_LEFT]),
|
||||
RCTRoundPixelValue(node->layout.position[CSS_TOP]),
|
||||
@ -205,7 +154,7 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
|
||||
for (int i = 0; i < node->children_count; ++i) {
|
||||
RCTShadowView *child = (RCTShadowView *)_reactSubviews[i];
|
||||
[child _applyLayoutNode:node->get_child(node->context, i) viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
|
||||
[child applyLayoutNode:node->get_child(node->context, i) viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,10 +187,10 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties
|
||||
{
|
||||
if (_propagationLifecycle == RCTPropagationLifecycleComputed && [parentProperties isEqualToDictionary:_lastParentProperties]) {
|
||||
if (_propagationLifecycle == RCTUpdateLifecycleComputed && [parentProperties isEqualToDictionary:_lastParentProperties]) {
|
||||
return;
|
||||
}
|
||||
_propagationLifecycle = RCTPropagationLifecycleComputed;
|
||||
_propagationLifecycle = RCTUpdateLifecycleComputed;
|
||||
_lastParentProperties = parentProperties;
|
||||
NSDictionary *nextProps = [self processBackgroundColor:applierBlocks parentProperties:parentProperties];
|
||||
for (RCTShadowView *child in _reactSubviews) {
|
||||
@ -253,7 +202,7 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
{
|
||||
[self fillCSSNode:_cssNode];
|
||||
layoutNode(_cssNode, CSS_UNDEFINED);
|
||||
[self applyLayoutNode:_cssNode viewsWithNewFrame:viewsWithNewFrame];
|
||||
[self applyLayoutNode:_cssNode viewsWithNewFrame:viewsWithNewFrame absolutePosition:CGPointZero];
|
||||
}
|
||||
|
||||
+ (CGRect)measureLayout:(RCTShadowView *)shadowView relativeTo:(RCTShadowView *)ancestor
|
||||
@ -277,7 +226,7 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
|
||||
_frame = CGRectMake(0, 0, CSS_UNDEFINED, CSS_UNDEFINED);
|
||||
|
||||
for (int ii = 0; ii < META_PROP_COUNT; ii++) {
|
||||
@ -286,9 +235,9 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
}
|
||||
|
||||
_newView = YES;
|
||||
_layoutLifecycle = RCTLayoutLifecycleUninitialized;
|
||||
_propagationLifecycle = RCTPropagationLifecycleUninitialized;
|
||||
_textLifecycle = RCTTextLifecycleUninitialized;
|
||||
_layoutLifecycle = RCTUpdateLifecycleUninitialized;
|
||||
_propagationLifecycle = RCTUpdateLifecycleUninitialized;
|
||||
_textLifecycle = RCTUpdateLifecycleUninitialized;
|
||||
|
||||
_reactSubviews = [NSMutableArray array];
|
||||
|
||||
@ -309,46 +258,46 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
|
||||
- (void)dirtyLayout
|
||||
{
|
||||
if (_layoutLifecycle != RCTLayoutLifecycleDirtied) {
|
||||
_layoutLifecycle = RCTLayoutLifecycleDirtied;
|
||||
if (_layoutLifecycle != RCTUpdateLifecycleDirtied) {
|
||||
_layoutLifecycle = RCTUpdateLifecycleDirtied;
|
||||
[_superview dirtyLayout];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isLayoutDirty
|
||||
{
|
||||
return _layoutLifecycle != RCTLayoutLifecycleComputed;
|
||||
return _layoutLifecycle != RCTUpdateLifecycleComputed;
|
||||
}
|
||||
|
||||
- (void)dirtyPropagation
|
||||
{
|
||||
if (_propagationLifecycle != RCTPropagationLifecycleDirtied) {
|
||||
_propagationLifecycle = RCTPropagationLifecycleDirtied;
|
||||
if (_propagationLifecycle != RCTUpdateLifecycleDirtied) {
|
||||
_propagationLifecycle = RCTUpdateLifecycleDirtied;
|
||||
[_superview dirtyPropagation];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isPropagationDirty
|
||||
{
|
||||
return _propagationLifecycle != RCTLayoutLifecycleComputed;
|
||||
return _propagationLifecycle != RCTUpdateLifecycleComputed;
|
||||
}
|
||||
|
||||
- (void)dirtyText
|
||||
{
|
||||
if (_textLifecycle != RCTTextLifecycleDirtied) {
|
||||
_textLifecycle = RCTTextLifecycleDirtied;
|
||||
if (_textLifecycle != RCTUpdateLifecycleDirtied) {
|
||||
_textLifecycle = RCTUpdateLifecycleDirtied;
|
||||
[_superview dirtyText];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isTextDirty
|
||||
{
|
||||
return _textLifecycle != RCTTextLifecycleComputed;
|
||||
return _textLifecycle != RCTUpdateLifecycleComputed;
|
||||
}
|
||||
|
||||
- (void)setTextComputed
|
||||
{
|
||||
_textLifecycle = RCTTextLifecycleComputed;
|
||||
_textLifecycle = RCTUpdateLifecycleComputed;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
|
||||
@ -387,7 +336,6 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
||||
return [shadowView reactTagAtPoint:relativePoint];
|
||||
}
|
||||
}
|
||||
|
||||
return self.reactTag;
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,7 @@
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(capInsets)
|
||||
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode)
|
||||
|
||||
- (void)set_src:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(src, RCTStaticImage *)
|
||||
{
|
||||
if (json) {
|
||||
if ([[[json description] pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
|
||||
@ -29,8 +28,7 @@ RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode)
|
||||
view.image = defaultView.image;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)set_tintColor:(id)json forView:(RCTStaticImage *)view withDefaultView:(RCTStaticImage *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(tintColor, RCTStaticImage *)
|
||||
{
|
||||
if (json) {
|
||||
view.renderingMode = UIImageRenderingModeAlwaysTemplate;
|
||||
|
@ -10,6 +10,6 @@
|
||||
@property (nonatomic, assign) BOOL autoCorrect;
|
||||
@property (nonatomic, assign) UIEdgeInsets paddingEdgeInsets; // TODO: contentInset
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
@ -14,11 +14,6 @@
|
||||
BOOL _jsRequestingFirstResponder;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
@ -44,7 +39,7 @@
|
||||
|
||||
- (void)removeReactSubview:(UIView *)subview
|
||||
{
|
||||
// TODO: this is a bit broken - if the TextView inserts any of
|
||||
// TODO: this is a bit broken - if the TextField inserts any of
|
||||
// it's own views below or between React's, the indices won't match
|
||||
[_reactSubviews removeObject:subview];
|
||||
[subview removeFromSuperview];
|
||||
@ -52,7 +47,7 @@
|
||||
|
||||
- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
|
||||
{
|
||||
// TODO: this is a bit broken - if the TextView inserts any of
|
||||
// TODO: this is a bit broken - if the TextField inserts any of
|
||||
// it's own views below or between React's, the indices won't match
|
||||
[_reactSubviews insertObject:view atIndex:atIndex];
|
||||
[super insertSubview:view atIndex:atIndex];
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTTextField.h"
|
||||
|
||||
@implementation RCTTextFieldManager
|
||||
@ -15,38 +16,34 @@
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(caretHidden)
|
||||
RCT_EXPORT_VIEW_PROPERTY(autoCorrect)
|
||||
RCT_REMAP_VIEW_PROPERTY(autoCapitalize, autocapitalizationType)
|
||||
RCT_EXPORT_VIEW_PROPERTY(enabled)
|
||||
RCT_EXPORT_VIEW_PROPERTY(placeholder)
|
||||
RCT_EXPORT_VIEW_PROPERTY(text)
|
||||
RCT_EXPORT_VIEW_PROPERTY(font)
|
||||
RCT_EXPORT_VIEW_PROPERTY(clearButtonMode)
|
||||
RCT_REMAP_VIEW_PROPERTY(autoCapitalize, autocapitalizationType)
|
||||
RCT_EXPORT_VIEW_PROPERTY(keyboardType)
|
||||
RCT_REMAP_VIEW_PROPERTY(color, textColor)
|
||||
|
||||
- (void)set_fontSize:(id)json
|
||||
forView:(RCTTextField *)view
|
||||
withDefaultView:(RCTTextField *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(fontSize, RCTTextField *)
|
||||
{
|
||||
view.font = [RCTConvert UIFont:view.font withSize:json ?: @(defaultView.font.pointSize)];
|
||||
}
|
||||
|
||||
- (void)set_FontWeight:(id)json
|
||||
forView:(RCTTextField *)view
|
||||
withDefaultView:(RCTTextField *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(fontWeight, RCTTextField *)
|
||||
{
|
||||
view.font = [RCTConvert UIFont:view.font withWeight:json]; // TODO
|
||||
}
|
||||
|
||||
- (void)set_fontFamily:(id)json
|
||||
forView:(RCTTextField *)view
|
||||
withDefaultView:(RCTTextField *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(fontFamily, RCTTextField *)
|
||||
{
|
||||
view.font = [RCTConvert UIFont:view.font withFamily:json ?: defaultView.font.familyName];
|
||||
}
|
||||
|
||||
// TODO: original code set view.paddingEdgeInsets from shadowView.paddingAsInsets
|
||||
// could it be that this property is calculated asynchrously on shadow thread?
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
|
||||
{
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
((RCTTextField *)viewRegistry[reactTag]).paddingEdgeInsets = padding;
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
@ -13,12 +13,9 @@
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(activityIndicatorViewStyle)
|
||||
RCT_EXPORT_VIEW_PROPERTY(color)
|
||||
|
||||
- (void)set_animating:(NSNumber *)value
|
||||
forView:(UIActivityIndicatorView *)view
|
||||
withDefaultView:(UIActivityIndicatorView *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(animating, UIActivityIndicatorView *)
|
||||
{
|
||||
BOOL animating = value ? [value boolValue] : [defaultView isAnimating];
|
||||
BOOL animating = json ? [json boolValue] : [defaultView isAnimating];
|
||||
if (animating != [view isAnimating]) {
|
||||
if (animating) {
|
||||
[view startAnimating];
|
||||
@ -28,7 +25,7 @@ RCT_EXPORT_VIEW_PROPERTY(color)
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSDictionary *)constantsToExport
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return
|
||||
@{
|
||||
|
@ -11,7 +11,6 @@
|
||||
@interface RCTView : UIView
|
||||
|
||||
@property (nonatomic, assign) RCTPointerEvents pointerEvents;
|
||||
@property (nonatomic, copy) NSString *overrideAccessibilityLabel;
|
||||
|
||||
+ (void)autoAdjustInsetsForView:(UIView<RCTAutoInsetsProtocol> *)parentView
|
||||
withScrollView:(UIScrollView *)scrollView
|
||||
|
@ -34,8 +34,8 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
|
||||
- (NSString *)accessibilityLabel
|
||||
{
|
||||
if (self.overrideAccessibilityLabel) {
|
||||
return self.overrideAccessibilityLabel;
|
||||
if (super.accessibilityLabel) {
|
||||
return super.accessibilityLabel;
|
||||
}
|
||||
return RCTRecursiveAccessibilityLabel(self);
|
||||
}
|
||||
@ -84,7 +84,7 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
UIEdgeInsets baseInset = parentView.contentInset;
|
||||
CGFloat previousInsetTop = scrollView.contentInset.top;
|
||||
CGPoint contentOffset = scrollView.contentOffset;
|
||||
|
||||
|
||||
if (parentView.automaticallyAdjustContentInsets) {
|
||||
UIEdgeInsets autoInset = [self contentInsetsForView:parentView];
|
||||
baseInset.top += autoInset.top;
|
||||
@ -94,7 +94,7 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
||||
}
|
||||
[scrollView setContentInset:baseInset];
|
||||
[scrollView setScrollIndicatorInsets:baseInset];
|
||||
|
||||
|
||||
if (updateOffset) {
|
||||
// If we're adjusting the top inset, then let's also adjust the contentOffset so that the view
|
||||
// elements above the top guide do not cover the content.
|
||||
|
@ -2,9 +2,11 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTShadowView;
|
||||
@class RCTSparseArray;
|
||||
@ -12,19 +14,22 @@
|
||||
|
||||
typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *viewRegistry);
|
||||
|
||||
@interface RCTViewManager : NSObject
|
||||
@interface RCTViewManager : NSObject <RCTBridgeModule>
|
||||
|
||||
/**
|
||||
* Designated initializer for view modules. Override this when subclassing.
|
||||
* The bridge can be used to access both the RCTUIIManager and the RCTEventDispatcher,
|
||||
* allowing the manager (or the views that it manages) to manipulate the view
|
||||
* hierarchy and send events back to the JS context.
|
||||
*/
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
@property (nonatomic, strong) RCTBridge *bridge;
|
||||
|
||||
/**
|
||||
* The event dispatcher is used to send events back to the JavaScript application.
|
||||
* It can either be used directly by the module, or passed on to instantiated
|
||||
* view subclasses so that they can handle their own events.
|
||||
*/
|
||||
@property (nonatomic, readonly, weak) RCTEventDispatcher *eventDispatcher;
|
||||
// TODO: remove this, as it can be accessed directly from bridge
|
||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
/**
|
||||
* The module name exposed to React JS. If omitted, this will be inferred
|
||||
@ -88,34 +93,19 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v
|
||||
+ (NSDictionary *)customDirectEventTypes;
|
||||
|
||||
/**
|
||||
* Injects constants into JS. These constants are made accessible via
|
||||
* NativeModules.moduleName.X. Note that this method is not inherited when you
|
||||
* subclass a view module, and you should not call [super constantsToExport]
|
||||
* when overriding it.
|
||||
* Called to notify manager that layout has finished, in case any calculated
|
||||
* properties need to be copied over from shadow view to view.
|
||||
*/
|
||||
+ (NSDictionary *)constantsToExport;
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView;
|
||||
|
||||
/**
|
||||
* To deprecate, hopefully
|
||||
* Called after view hierarchy manipulation has finished, and all shadow props
|
||||
* have been set, but before layout has been performed. Useful for performing
|
||||
* custo layout logic or tasks that involve walking the view hierarchy.
|
||||
* To be deprecated, hopefully.
|
||||
*/
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry;
|
||||
|
||||
/**
|
||||
* Informal protocol for setting view and shadowView properties.
|
||||
* Implement methods matching these patterns to set any properties that
|
||||
* require special treatment (e.g. where the type or name cannot be inferred).
|
||||
*
|
||||
* - (void)set_<propertyName>:(id)property
|
||||
* forView:(UIView *)view
|
||||
* withDefaultView:(UIView *)defaultView;
|
||||
*
|
||||
* - (void)set_<propertyName>:(id)property
|
||||
* forShadowView:(RCTShadowView *)view
|
||||
* withDefaultView:(RCTShadowView *)defaultView;
|
||||
*
|
||||
* For simple cases, use the macros below:
|
||||
*/
|
||||
|
||||
/**
|
||||
* This handles the simple case, where JS and native property names match
|
||||
* And the type can be automatically inferred.
|
||||
@ -131,10 +121,21 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
|
||||
- (void)set_##name:(id)json forView:(id)view withDefaultView:(id)defaultView { \
|
||||
if ((json && !RCTSetProperty(view, @#keypath, json)) || \
|
||||
(!json && !RCTCopyProperty(view, defaultView, @#keypath))) { \
|
||||
RCTLogMustFix(@"%@ does not have setter for `%s` property", [view class], #name); \
|
||||
RCTLogError(@"%@ does not have setter for `%s` property", [view class], #name); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* These macros can be used when you need to provide custom logic for setting
|
||||
* view properties. The macro should be followed by a method body, which can
|
||||
* refer to "json", "view" and "defaultView" to implement the required logic.
|
||||
*/
|
||||
#define RCT_CUSTOM_VIEW_PROPERTY(name, viewType) \
|
||||
- (void)set_##name:(id)json forView:(viewType)view withDefaultView:(viewType)defaultView
|
||||
|
||||
#define RCT_CUSTOM_SHADOW_PROPERTY(name, viewType) \
|
||||
- (void)set_##name:(id)json forShadowView:(viewType)view withDefaultView:(viewType)defaultView
|
||||
|
||||
/**
|
||||
* These are useful in cases where the module's superclass handles a
|
||||
* property, but you wish to "unhandle" it, so it will be ignored.
|
||||
|
@ -2,32 +2,33 @@
|
||||
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTShadowView.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTView.h"
|
||||
|
||||
@implementation RCTViewManager
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_eventDispatcher = eventDispatcher;
|
||||
}
|
||||
return self;
|
||||
return _bridge.eventDispatcher;
|
||||
}
|
||||
|
||||
+ (NSString *)moduleName
|
||||
{
|
||||
// Default implementation, works in most cases
|
||||
NSString *name = NSStringFromClass(self);
|
||||
if ([name hasPrefix:@"RK"]) {
|
||||
name = [name stringByReplacingCharactersInRange:(NSRange){0,@"RK".length} withString:@"RCT"];
|
||||
}
|
||||
if ([name hasPrefix:@"RCTUI"]) {
|
||||
name = [name substringFromIndex:@"RCT".length];
|
||||
}
|
||||
if ([name hasSuffix:@"Manager"]) {
|
||||
name = [name substringToIndex:name.length - @"Manager".length];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -51,7 +52,12 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)constantsToExport
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
@ -77,28 +83,22 @@ 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, layer.transform)
|
||||
|
||||
- (void)set_overflow:(id)json
|
||||
forView:(UIView *)view
|
||||
withDefaultView:(UIView *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(overflow, UIView *)
|
||||
{
|
||||
view.clipsToBounds = json ? ![RCTConvert css_overflow:json] : defaultView.clipsToBounds;
|
||||
}
|
||||
|
||||
- (void)set_pointerEvents:(id)json
|
||||
forView:(RCTView *)view
|
||||
withDefaultView:(RCTView *)defaultView
|
||||
RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTView *)
|
||||
{
|
||||
if ([view respondsToSelector:@selector(setPointerEvents:)]) {
|
||||
view.pointerEvents = json ? [RCTConvert RCTPointerEvents:json] : defaultView.pointerEvents;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!json) {
|
||||
view.userInteractionEnabled = defaultView.userInteractionEnabled;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
switch ([RCTConvert NSInteger:json]) {
|
||||
case RCTPointerEventsUnspecified:
|
||||
// Pointer events "unspecified" acts as if a stylesheet had not specified,
|
||||
@ -117,54 +117,34 @@ RCT_REMAP_VIEW_PROPERTY(transformMatrix, layer.transform)
|
||||
|
||||
// ShadowView properties
|
||||
|
||||
- (void)set_backgroundColor:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(backgroundColor, RCTShadowView *)
|
||||
{
|
||||
shadowView.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor;
|
||||
shadowView.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
view.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor;
|
||||
view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
|
||||
}
|
||||
|
||||
- (void)set_flexDirection:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(flexDirection, RCTShadowView *)
|
||||
{
|
||||
shadowView.flexDirection = json? [RCTConvert css_flex_direction_t:json] : defaultView.flexDirection;
|
||||
view.flexDirection = json? [RCTConvert css_flex_direction_t:json] : defaultView.flexDirection;
|
||||
}
|
||||
|
||||
- (void)set_flexWrap:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(flexWrap, RCTShadowView *)
|
||||
{
|
||||
shadowView.flexWrap = json ? [RCTConvert css_wrap_type_t:json] : defaultView.flexWrap;
|
||||
view.flexWrap = json ? [RCTConvert css_wrap_type_t:json] : defaultView.flexWrap;
|
||||
}
|
||||
|
||||
- (void)set_justifyContent:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(justifyContent, RCTShadowView *)
|
||||
{
|
||||
shadowView.justifyContent = json ? [RCTConvert css_justify_t:json] : defaultView.justifyContent;
|
||||
view.justifyContent = json ? [RCTConvert css_justify_t:json] : defaultView.justifyContent;
|
||||
}
|
||||
|
||||
- (void)set_alignItems:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(alignItems, RCTShadowView *)
|
||||
{
|
||||
shadowView.alignItems = json ? [RCTConvert css_align_t:json] : defaultView.alignItems;
|
||||
view.alignItems = json ? [RCTConvert css_align_t:json] : defaultView.alignItems;
|
||||
}
|
||||
|
||||
- (void)set_alignSelf:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(alignSelf, RCTShadowView *)
|
||||
{
|
||||
shadowView.alignSelf = json ? [RCTConvert css_align_t:json] : defaultView.alignSelf;
|
||||
view.alignSelf = json ? [RCTConvert css_align_t:json] : defaultView.alignSelf;
|
||||
}
|
||||
|
||||
- (void)set_position:(id)json
|
||||
forShadowView:(RCTShadowView *)shadowView
|
||||
withDefaultView:(RCTShadowView *)defaultView
|
||||
RCT_CUSTOM_SHADOW_PROPERTY(position, RCTShadowView *)
|
||||
{
|
||||
shadowView.positionType = json ? [RCTConvert css_position_type_t:json] : defaultView.positionType;
|
||||
view.positionType = json ? [RCTConvert css_position_type_t:json] : defaultView.positionType;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -15,8 +15,11 @@ didMoveToNavigationController:(UINavigationController *)navigationController;
|
||||
|
||||
@interface RCTWrapperViewController : UIViewController
|
||||
|
||||
- (instancetype)initWithContentView:(UIView *)contentView eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
- (instancetype)initWithNavItem:(RCTNavItem *)navItem eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
- (instancetype)initWithContentView:(UIView *)contentView
|
||||
eventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)initWithNavItem:(RCTNavItem *)navItem
|
||||
eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
|
||||
|
||||
@property (nonatomic, readwrite, weak) id<RCTWrapperViewControllerNavigationListener> navigationListener;
|
||||
@property (nonatomic, strong, readwrite) RCTNavItem *navItem;
|
||||
|
@ -15,11 +15,6 @@
|
||||
CGFloat _previousBottomLayout;
|
||||
}
|
||||
|
||||
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
|
||||
{
|
||||
RCT_NOT_DESIGNATED_INITIALIZER();
|
||||
}
|
||||
|
||||
- (instancetype)initWithContentView:(UIView *)contentView eventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
||||
{
|
||||
if ((self = [super initWithNibName:nil bundle:nil])) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user