[React Native] Remove layout-only nodes

Summary:
Remove layout-only views. Works by checking properties against a list of known properties that only affect layout. The `RCTShadowView` hierarchy still has a 1:1 correlation with the JS nodes.

This works by adjusting the tags and indices in `manageChildren`. For example, if JS told us to insert tag 1 at index 0 and tag 1 is layout-only with children whose tags are 2 and 3, we adjust it so we insert tags 2 and 3 at indices 0 and 1. This keeps changes out of `RCTView` and `RCTScrollView`. In order to simplify this logic, view moves are now processed as view removals followed by additions. A move from index 0 to 1 is recorded as a removal of view at indices 0 and 1 and an insertion of tags 1 and 2 at indices 0 and 1. Of course, the remaining indices have to be offset to take account for this.

The `collapsible` attribute is a bit of a hack to force `RCTScrollView` to always have one child. This was easier than rethinking out the logic there, but we could change this later.

@public

Test Plan: There are tests in `RCTUIManagerTests.m` that test the tag- and index-manipulation logic works. There are various scenarios including add-only, remove-only, and move. In addition, two scenario tests verify that the optimization works by checking the number of views and shadow views after various situations happen.
This commit is contained in:
Alex Akers
2015-06-25 09:07:32 -07:00
parent 64d7933e66
commit 3c5b4b0a9f
13 changed files with 2022 additions and 113 deletions

View File

@@ -47,6 +47,7 @@
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; };
3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; };
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; };
83686E801B39D26300CBA10B /* Test.includeRequire.runModule.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 83686E7F1B39D26300CBA10B /* Test.includeRequire.runModule.bundle */; };
D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; };
/* End PBXBuildFile section */
@@ -202,6 +203,7 @@
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = "<group>"; };
83686E7F1B39D26300CBA10B /* Test.includeRequire.runModule.bundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Test.includeRequire.runModule.bundle; sourceTree = "<group>"; };
D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -351,6 +353,7 @@
1497CFAB1B21F5E400C1F8F2 /* RCTUIManagerTests.m */,
143BC57E1B21E18100462512 /* Info.plist */,
14D6D7101B220EB3001FB087 /* libOCMock.a */,
83686E7F1B39D26300CBA10B /* Test.includeRequire.runModule.bundle */,
14D6D7011B220AE3001FB087 /* OCMock */,
143BC57F1B21E18100462512 /* ReferenceImages */,
);
@@ -745,6 +748,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
83686E801B39D26300CBA10B /* Test.includeRequire.runModule.bundle in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -2,6 +2,8 @@
#import <XCTest/XCTest.h>
#import "RCTRootView.h"
#import "RCTShadowView.h"
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
#import "UIView+React.h"
@@ -9,14 +11,36 @@
@interface RCTUIManager (Testing)
- (void)_manageChildren:(NSNumber *)containerReactTag
moveFromIndices:(NSArray *)moveFromIndices
moveToIndices:(NSArray *)moveToIndices
addChildReactTags:(NSArray *)addChildReactTags
addAtIndices:(NSArray *)addAtIndices
removeAtIndices:(NSArray *)removeAtIndices
registry:(RCTSparseArray *)registry;
- (void)modifyManageChildren:(NSNumber *)containerReactTag
addChildReactTags:(NSMutableArray *)mutableAddChildReactTags
addAtIndices:(NSMutableArray *)mutableAddAtIndices
removeAtIndices:(NSMutableArray *)mutableRemoveAtIndices;
- (void)createView:(NSNumber *)reactTag
viewName:(NSString *)viewName
rootTag:(NSNumber *)rootTag
props:(NSDictionary *)props;
- (void)updateView:(NSNumber *)reactTag
viewName:(NSString *)viewName
props:(NSDictionary *)props;
- (void)manageChildren:(NSNumber *)containerReactTag
moveFromIndices:(NSArray *)moveFromIndices
moveToIndices:(NSArray *)moveToIndices
addChildReactTags:(NSArray *)addChildReactTags
addAtIndices:(NSArray *)addAtIndices
removeAtIndices:(NSArray *)removeAtIndices;
- (void)flushUIBlocks;
@property (nonatomic, readonly) RCTSparseArray *viewRegistry;
@property (nonatomic, readonly) RCTSparseArray *shadowViewRegistry; // RCT thread only
@end
@@ -39,6 +63,11 @@
UIView *registeredView = [[UIView alloc] init];
[registeredView setReactTag:@(i)];
_uiManager.viewRegistry[i] = registeredView;
RCTShadowView *registeredShadowView = [[RCTShadowView alloc] init];
registeredShadowView.viewName = @"RCTView";
[registeredShadowView setReactTag:@(i)];
_uiManager.shadowViewRegistry[i] = registeredShadowView;
}
}
@@ -55,8 +84,6 @@
// Add views 1-5 to view 20
[_uiManager _manageChildren:@20
moveFromIndices:nil
moveToIndices:nil
addChildReactTags:tagsToAdd
addAtIndices:addAtIndices
removeAtIndices:nil
@@ -89,8 +116,6 @@
// Remove views 1-5 from view 20
[_uiManager _manageChildren:@20
moveFromIndices:nil
moveToIndices:nil
addChildReactTags:nil
addAtIndices:nil
removeAtIndices:removeAtIndices
@@ -128,11 +153,9 @@
{
UIView *containerView = _uiManager.viewRegistry[20];
NSArray *removeAtIndices = @[@2, @3, @5, @8];
NSArray *addAtIndices = @[@0, @6];
NSArray *tagsToAdd = @[@11, @12];
NSArray *moveFromIndices = @[@4, @9];
NSArray *moveToIndices = @[@1, @7];
NSArray *removeAtIndices = @[@2, @3, @5, @8, @4, @9];
NSArray *addAtIndices = @[@0, @6, @1, @7];
NSArray *tagsToAdd = @[@11, @12, @5, @10];
// We need to keep these in array to keep them around
NSMutableArray *viewsToRemove = [NSMutableArray array];
@@ -148,8 +171,6 @@
}
[_uiManager _manageChildren:@20
moveFromIndices:moveFromIndices
moveToIndices:moveToIndices
addChildReactTags:tagsToAdd
addAtIndices:addAtIndices
removeAtIndices:removeAtIndices
@@ -176,4 +197,331 @@
}
}
/* +-----------------------------------------------------------+ +----------------------+
* | Shadow Hierarchy | | Legend |
* +-----------------------------------------------------------+ +----------------------+
* | | | |
* | +---+ ****** | | ******************** |
* | | 1 | * 11 * | | * Layout-only View * |
* | +---+ ****** | | ******************** |
* | | | | | |
* | +-------+---+---+----------+ +---+---+ | | +----+ |
* | | | | | | | | | |View| Subview |
* | v v v v v v | | +----+ -----------> |
* | ***** +---+ ***** +---+ +----+ +----+ | | |
* | * 2 * | 3 | * 4 * | 5 | | 12 | | 13 | | +----------------------+
* | ***** +---+ ***** +---+ +----+ +----+ |
* | | | | |
* | +---+--+ | +---+---+ |
* | | | | | | |
* | v v v v v |
* | +---+ +---+ +---+ +---+ ****** |
* | | 6 | | 7 | | 8 | | 9 | * 10 * |
* | +---+ +---+ +---+ +---+ ****** |
* | |
* +-----------------------------------------------------------+
*
* +-----------------------------------------------------------+
* | View Hierarchy |
* +-----------------------------------------------------------+
* | |
* | +---+ ****** |
* | | 1 | * 11 * |
* | +---+ ****** |
* | | | |
* | +------+------+------+------+ +---+---+ |
* | | | | | | | | |
* | v v v v v v v |
* | +---+ +---+ +---+ +---+ +---+ +----+ +----+ |
* | | 6 | | 7 | | 3 | | 8 | | 5 | | 12 | | 13 | |
* | +---+ +---+ +---+ +---+ +---+ +----+ +----+ |
* | | |
* | v |
* | +---+ |
* | | 9 | |
* | +---+ |
* | |
* +-----------------------------------------------------------+
*/
- (void)updateShadowViewWithReactTag:(NSNumber *)reactTag layoutOnly:(BOOL)isLayoutOnly childTags:(NSArray *)childTags
{
RCTShadowView *shadowView = _uiManager.shadowViewRegistry[reactTag];
shadowView.allProps = isLayoutOnly ? @{} : @{@"collapsible": @NO};
[childTags enumerateObjectsUsingBlock:^(NSNumber *childTag, NSUInteger idx, __unused BOOL *stop) {
[shadowView insertReactSubview:_uiManager.shadowViewRegistry[childTag] atIndex:idx];
}];
}
- (void)setUpShadowViewHierarchy
{
[self updateShadowViewWithReactTag:@1 layoutOnly:NO childTags:@[@2, @3, @4, @5]];
[self updateShadowViewWithReactTag:@2 layoutOnly:YES childTags:@[@6, @7]];
[self updateShadowViewWithReactTag:@3 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@4 layoutOnly:YES childTags:@[@8]];
[self updateShadowViewWithReactTag:@5 layoutOnly:NO childTags:@[@9, @10]];
[self updateShadowViewWithReactTag:@6 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@7 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@8 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@9 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@10 layoutOnly:YES childTags:nil];
[self updateShadowViewWithReactTag:@11 layoutOnly:YES childTags:@[@12, @13]];
[self updateShadowViewWithReactTag:@12 layoutOnly:NO childTags:nil];
[self updateShadowViewWithReactTag:@13 layoutOnly:NO childTags:nil];
}
- (void)testModifyIndices1
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@2] mutableCopy];
NSMutableArray *addIndices = [@[@3] mutableCopy];
NSMutableArray *removeIndices = [@[@0] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@6, @7]));
XCTAssertEqualObjects(addIndices, (@[@3, @4]));
XCTAssertEqualObjects(removeIndices, (@[@0, @1]));
}
- (void)testModifyIndices2
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@11] mutableCopy];
NSMutableArray *addIndices = [@[@4] mutableCopy];
NSMutableArray *removeIndices = [@[] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@12, @13]));
XCTAssertEqualObjects(addIndices, (@[@5, @6]));
XCTAssertEqualObjects(removeIndices, (@[]));
}
- (void)testModifyIndices3
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[] mutableCopy];
NSMutableArray *addIndices = [@[] mutableCopy];
NSMutableArray *removeIndices = [@[@2] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[]));
XCTAssertEqualObjects(addIndices, (@[]));
XCTAssertEqualObjects(removeIndices, (@[@3]));
}
- (void)testModifyIndices4
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@11] mutableCopy];
NSMutableArray *addIndices = [@[@3] mutableCopy];
NSMutableArray *removeIndices = [@[@2] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@12, @13]));
XCTAssertEqualObjects(addIndices, (@[@4, @5]));
XCTAssertEqualObjects(removeIndices, (@[@3]));
}
- (void)testModifyIndices5
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[] mutableCopy];
NSMutableArray *addIndices = [@[] mutableCopy];
NSMutableArray *removeIndices = [@[@0, @1, @2, @3] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[]));
XCTAssertEqualObjects(addIndices, (@[]));
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @2, @3, @4]));
}
- (void)testModifyIndices6
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@11] mutableCopy];
NSMutableArray *addIndices = [@[@0] mutableCopy];
NSMutableArray *removeIndices = [@[@0, @1, @2, @3] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@12, @13]));
XCTAssertEqualObjects(addIndices, (@[@0, @1]));
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @2, @3, @4]));
}
- (void)testModifyIndices7
{
[self setUpShadowViewHierarchy];
NSMutableArray *addTags = [@[@11] mutableCopy];
NSMutableArray *addIndices = [@[@1] mutableCopy];
NSMutableArray *removeIndices = [@[@0, @2, @3] mutableCopy];
[self.uiManager modifyManageChildren:@1
addChildReactTags:addTags
addAtIndices:addIndices
removeAtIndices:removeIndices];
XCTAssertEqualObjects(addTags, (@[@12, @13]));
XCTAssertEqualObjects(addIndices, (@[@1, @2]));
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @3, @4]));
}
- (void)testScenario1
{
RCTUIManager *uiManager = [[RCTUIManager alloc] init];
NSURL *bundleURL = [[NSBundle bundleForClass:self.class] URLForResource:@"Test.includeRequire.runModule" withExtension:nil];
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
NS_VALID_UNTIL_END_OF_SCOPE RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"Test"];
XCTestExpectation *expectation = [self expectationWithDescription:@""];
dispatch_queue_t shadowQueue = [uiManager valueForKey:@"shadowQueue"];
dispatch_async(shadowQueue, ^{
// Make sure root view finishes loading.
dispatch_sync(dispatch_get_main_queue(), ^{});
/* */[uiManager createView:@2 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
/* */[uiManager createView:@3 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
/* V */[uiManager createView:@4 viewName:@"RCTView" rootTag:@1 props:@{@"alignItems":@"center",@"backgroundColor":@"#F5FCFF",@"flex":@1,@"justifyContent":@"center"}];
/* V */[uiManager createView:@5 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"blue",@"height":@50,@"width":@50}];
/* */[uiManager createView:@6 viewName:@"RCTView" rootTag:@1 props:@{@"width":@250}];
/* V */[uiManager createView:@7 viewName:@"RCTView" rootTag:@1 props:@{@"borderWidth":@10,@"margin":@50}];
/* V */[uiManager createView:@8 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"yellow",@"height":@50}];
/* V */[uiManager createView:@9 viewName:@"RCTText" rootTag:@1 props:@{@"accessible":@1,@"fontSize":@20,@"isHighlighted":@0,@"margin":@10,@"textAlign":@"center"}];
/* */[uiManager createView:@10 viewName:@"RCTRawText" rootTag:@1 props:@{@"text":@"This tests removal of layout-only views."}];
/* */[uiManager manageChildren:@9 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@10] addAtIndices:@[@0] removeAtIndices:nil];
/* V */[uiManager createView:@12 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"green",@"height":@50}];
/* */[uiManager manageChildren:@7 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@8,@9,@12] addAtIndices:@[@0,@1,@2] removeAtIndices:nil];
/* */[uiManager manageChildren:@6 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@7] addAtIndices:@[@0] removeAtIndices:nil];
/* V */[uiManager createView:@13 viewName:@"RCTView" rootTag:@1 props:@{@"backgroundColor":@"red",@"height":@50,@"width":@50}];
/* */[uiManager manageChildren:@4 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@5,@6,@13] addAtIndices:@[@0,@1,@2] removeAtIndices:nil];
/* */[uiManager manageChildren:@3 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@4] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@2 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@3] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@1 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@2] addAtIndices:@[@0] removeAtIndices:nil];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)8);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
expectation = [self expectationWithDescription:@""];
dispatch_async(shadowQueue, ^{
[uiManager updateView:@7 viewName:@"RCTView" props:@{@"borderWidth":[NSNull null]}];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)7);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
expectation = [self expectationWithDescription:@""];
dispatch_async(shadowQueue, ^{
[uiManager updateView:@7 viewName:@"RCTView" props:@{@"borderWidth":@10}];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)12);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)8);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
}
- (void)testScenario2
{
RCTUIManager *uiManager = [[RCTUIManager alloc] init];
NSURL *bundleURL = [[NSBundle bundleForClass:self.class] URLForResource:@"Test.includeRequire.runModule" withExtension:nil];
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:bundleURL moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
NS_VALID_UNTIL_END_OF_SCOPE RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"Test"];
XCTestExpectation *expectation = [self expectationWithDescription:@""];
dispatch_queue_t shadowQueue = [uiManager valueForKey:@"shadowQueue"];
dispatch_async(shadowQueue, ^{
// Make sure root view finishes loading.
dispatch_sync(dispatch_get_main_queue(), ^{});
/* */[uiManager createView:@2 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
/* */[uiManager createView:@3 viewName:@"RCTView" rootTag:@1 props:@{@"bottom":@0,@"left":@0,@"position":@"absolute",@"right":@0,@"top":@0}];
/* V */[uiManager createView:@4 viewName:@"RCTView" rootTag:@1 props:@{@"alignItems":@"center",@"backgroundColor":@"#F5FCFF",@"flex":@1,@"justifyContent":@"center"}];
/* */[uiManager createView:@5 viewName:@"RCTView" rootTag:@1 props:@{@"width":@250}];
/* V */[uiManager createView:@6 viewName:@"RCTView" rootTag:@1 props:@{@"borderWidth":@1}];
/* V */[uiManager createView:@7 viewName:@"RCTText" rootTag:@1 props:@{@"accessible":@1,@"fontSize":@20,@"isHighlighted":@0,@"margin":@10,@"textAlign":@"center"}];
/* */[uiManager createView:@8 viewName:@"RCTRawText" rootTag:@1 props:@{@"text":@"This tests removal of layout-only views."}];
/* */[uiManager manageChildren:@7 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@8] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@6 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@7] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@5 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@6] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@4 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@5] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@3 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@4] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@2 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@3] addAtIndices:@[@0] removeAtIndices:nil];
/* */[uiManager manageChildren:@1 moveFromIndices:nil moveToIndices:nil addChildReactTags:@[@2] addAtIndices:@[@0] removeAtIndices:nil];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)4);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
expectation = [self expectationWithDescription:@""];
dispatch_async(shadowQueue, ^{
[uiManager updateView:@6 viewName:@"RCTView" props:@{@"borderWidth":[NSNull null]}];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)3);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
expectation = [self expectationWithDescription:@""];
dispatch_async(shadowQueue, ^{
[uiManager updateView:@6 viewName:@"RCTView" props:@{@"borderWidth":@1}];
[uiManager addUIBlock:^(RCTUIManager *uiManager_, __unused RCTSparseArray *viewRegistry) {
XCTAssertEqual(uiManager_.shadowViewRegistry.count, (NSUInteger)8);
XCTAssertEqual(uiManager_.viewRegistry.count, (NSUInteger)4);
[expectation fulfill];
}];
[uiManager flushUIBlocks];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
}
@end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
/**
* The examples provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/