diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index d2b3784bb..ba1a02b73 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -1014,12 +1014,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp; PRODUCT_NAME = UIExplorer; TARGETED_DEVICE_FAMILY = "1,2"; - WARNING_CFLAGS = ( - "-Wextra", - "-Wall", - "-Wincompatible-pointer-types", - "-Wincompatible-pointer-types-discards-qualifiers", - ); }; name = Debug; }; @@ -1039,12 +1033,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.facebook.react.uiapp; PRODUCT_NAME = UIExplorer; TARGETED_DEVICE_FAMILY = "1,2"; - WARNING_CFLAGS = ( - "-Wextra", - "-Wall", - "-Wincompatible-pointer-types", - "-Wincompatible-pointer-types-discards-qualifiers", - ); }; name = Release; }; @@ -1149,6 +1137,8 @@ "-Wextra", "-Wall", "-Wincompatible-pointer-types", + "-Wincompatible-pointer-types-discards-qualifiers", + "-Wshadow", ); }; name = Debug; @@ -1212,6 +1202,8 @@ "-Wextra", "-Wall", "-Wincompatible-pointer-types", + "-Wincompatible-pointer-types-discards-qualifiers", + "-Wshadow", ); }; name = Release; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 11112f92a..033df4de6 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -57,8 +57,6 @@ NSString *const RCTUIManagerRootViewKey = @"RCTUIManagerRootViewKey"; @property (nonatomic, readonly) NSTimeInterval duration; @property (nonatomic, readonly) NSTimeInterval delay; @property (nonatomic, readonly, copy) NSString *property; -@property (nonatomic, readonly) id fromValue; -@property (nonatomic, readonly) id toValue; @property (nonatomic, readonly) CGFloat springDamping; @property (nonatomic, readonly) CGFloat initialVelocity; @property (nonatomic, readonly) RCTAnimationType animationType; @@ -136,8 +134,6 @@ static UIViewAnimationOptions UIViewAnimationOptionsFromRCTAnimationType(RCTAnim _springDamping = [RCTConvert CGFloat:config[@"springDamping"]]; _initialVelocity = [RCTConvert CGFloat:config[@"initialVelocity"]]; } - _fromValue = config[@"fromValue"]; - _toValue = config[@"toValue"]; } return self; } @@ -552,42 +548,47 @@ dispatch_queue_t RCTGetUIManagerQueue(void) return nil; } - // Parallel arrays are built and then handed off to main thread - NSMutableArray *frameReactTags = - [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; - NSMutableArray *frames = - [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; - NSMutableArray *areNew = - [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; - NSMutableArray *parentsAreNew = - [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; - NSMutableDictionary *updateBlocks = - [NSMutableDictionary new]; - NSMutableArray *areHidden = - [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; + typedef struct { + CGRect frame; + BOOL isNew; + BOOL parentIsNew; + BOOL isHidden; + } RCTFrameData; - for (RCTShadowView *shadowView in viewsWithNewFrames) { - [frameReactTags addObject:shadowView.reactTag]; - [frames addObject:[NSValue valueWithCGRect:shadowView.frame]]; - [areNew addObject:@(shadowView.isNewView)]; - [parentsAreNew addObject:@(shadowView.superview.isNewView)]; - [areHidden addObject:@(shadowView.isHidden)]; - } - - for (RCTShadowView *shadowView in viewsWithNewFrames) { - // We have to do this after we build the parentsAreNew array. - shadowView.newView = NO; + // Construct arrays then hand off to main thread + NSInteger count = viewsWithNewFrames.count; + NSMutableArray *reactTags = [[NSMutableArray alloc] initWithCapacity:count]; + NSMutableData *framesData = [[NSMutableData alloc] initWithLength:sizeof(RCTFrameData) * count]; + { + NSInteger index = 0; + RCTFrameData *frameDataArray = (RCTFrameData *)framesData.mutableBytes; + for (RCTShadowView *shadowView in viewsWithNewFrames) { + reactTags[index] = shadowView.reactTag; + frameDataArray[index++] = (RCTFrameData){ + shadowView.frame, + shadowView.isNewView, + shadowView.superview.isNewView, + shadowView.isHidden, + }; + } } // These are blocks to be executed on each view, immediately after // reactSetFrame: has been called. Note that if reactSetFrame: is not called, // these won't be called either, so this is not a suitable place to update // properties that aren't related to layout. + NSMutableDictionary *updateBlocks = + [NSMutableDictionary new]; for (RCTShadowView *shadowView in viewsWithNewFrames) { + + // We have to do this after we build the parentsAreNew array. + shadowView.newView = NO; + + NSNumber *reactTag = shadowView.reactTag; RCTViewManager *manager = [_componentDataByName[shadowView.viewName] manager]; RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView]; if (block) { - updateBlocks[shadowView.reactTag] = block; + updateBlocks[reactTag] = block; } if (shadowView.onLayout) { @@ -602,8 +603,7 @@ dispatch_queue_t RCTGetUIManagerQueue(void) }); } - if (RCTIsReactRootView(shadowView.reactTag)) { - NSNumber *reactTag = shadowView.reactTag; + if (RCTIsReactRootView(reactTag)) { CGSize contentSize = shadowView.frame.size; dispatch_async(dispatch_get_main_queue(), ^{ @@ -618,23 +618,28 @@ dispatch_queue_t RCTGetUIManagerQueue(void) // Perform layout (possibly animated) return ^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { - RCTLayoutAnimation *layoutAnimation = _layoutAnimation; + + const RCTFrameData *frameDataArray = (const RCTFrameData *)framesData.bytes; + RCTLayoutAnimation *layoutAnimation = uiManager->_layoutAnimation; __block NSUInteger completionsCalled = 0; - for (NSUInteger ii = 0; ii < frames.count; ii++) { - NSNumber *reactTag = frameReactTags[ii]; - UIView *view = viewRegistry[reactTag]; - CGRect frame = [frames[ii] CGRectValue]; - BOOL isHidden = [areHidden[ii] boolValue]; - BOOL isNew = [areNew[ii] boolValue]; + NSInteger index = 0; + for (NSNumber *reactTag in reactTags) { + RCTFrameData frameData = frameDataArray[index++]; + + UIView *view = viewRegistry[reactTag]; + CGRect frame = frameData.frame; + + BOOL isHidden = frameData.isHidden; + BOOL isNew = frameData.isNew; RCTAnimation *updateAnimation = isNew ? nil : layoutAnimation.updateAnimation; - BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue]; + BOOL shouldAnimateCreation = isNew && !frameData.parentIsNew; RCTAnimation *createAnimation = shouldAnimateCreation ? layoutAnimation.createAnimation : nil; void (^completion)(BOOL) = ^(BOOL finished) { completionsCalled++; - if (layoutAnimation.callback && completionsCalled == frames.count) { + if (layoutAnimation.callback && completionsCalled == count) { layoutAnimation.callback(@[@(finished)]); // It's unsafe to call this callback more than once, so we nil it out here @@ -647,58 +652,59 @@ dispatch_queue_t RCTGetUIManagerQueue(void) view.hidden = isHidden; } - // Animate view creation + RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag]; if (createAnimation) { + + // Animate view creation [view reactSetFrame:frame]; CATransform3D finalTransform = view.layer.transform; CGFloat finalOpacity = view.layer.opacity; - if ([createAnimation.property isEqualToString:@"scaleXY"]) { + + NSString *property = createAnimation.property; + if ([property isEqualToString:@"scaleXY"]) { view.layer.transform = CATransform3DMakeScale(0, 0, 0); - } else if ([createAnimation.property isEqualToString:@"opacity"]) { + } else if ([property isEqualToString:@"opacity"]) { view.layer.opacity = 0.0; + } else { + RCTLogError(@"Unsupported layout animation createConfig property %@", + createAnimation.property); } [createAnimation performAnimations:^{ - if ([createAnimation.property isEqual:@"scaleXY"]) { + if ([property isEqualToString:@"scaleXY"]) { view.layer.transform = finalTransform; - } else if ([createAnimation.property isEqual:@"opacity"]) { + } else if ([property isEqualToString:@"opacity"]) { view.layer.opacity = finalOpacity; - } else { - RCTLogError(@"Unsupported layout animation createConfig property %@", - createAnimation.property); } - - RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag]; if (updateBlock) { - updateBlock(self, _viewRegistry); + updateBlock(self, viewRegistry); } } withCompletionBlock:completion]; - // Animate view update } else if (updateAnimation) { + + // Animate view update [updateAnimation performAnimations:^{ [view reactSetFrame:frame]; - - RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag]; if (updateBlock) { - updateBlock(self, _viewRegistry); + updateBlock(self, viewRegistry); } } withCompletionBlock:completion]; - // Update without animation } else { - [view reactSetFrame:frame]; - RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag]; + // Update without animation + [view reactSetFrame:frame]; if (updateBlock) { - updateBlock(self, _viewRegistry); + updateBlock(self, viewRegistry); } completion(YES); } } - _layoutAnimation = nil; + // Clean up + uiManager->_layoutAnimation = nil; }; } @@ -802,10 +808,11 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe // the view events anyway. view.userInteractionEnabled = NO; + NSString *property = deleteAnimation.property; [deleteAnimation performAnimations:^{ - if ([deleteAnimation.property isEqual:@"scaleXY"]) { + if ([property isEqualToString:@"scaleXY"]) { view.layer.transform = CATransform3DMakeScale(0, 0, 0); - } else if ([deleteAnimation.property isEqual:@"opacity"]) { + } else if ([property isEqualToString:@"opacity"]) { view.layer.opacity = 0.0; } else { RCTLogError(@"Unsupported layout animation createConfig property %@",