From da36946c11b8fb2c5ec92bcd589241b591495925 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 11:44:41 -0400 Subject: [PATCH 01/11] refactor: move "checkLayerExists:" to NSView+React And rename it to "ensureLayerExists" --- React/Views/NSView+React.h | 3 +++ React/Views/NSView+React.m | 14 ++++++++++++++ React/Views/RCTView.m | 12 ------------ React/Views/RCTViewManager.m | 22 ++++++---------------- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/React/Views/NSView+React.h b/React/Views/NSView+React.h index 3fee72d909..61ece098e3 100644 --- a/React/Views/NSView+React.h +++ b/React/Views/NSView+React.h @@ -114,4 +114,7 @@ */ @property (nonatomic, assign) BOOL clipsToBounds; +/** Populate the `layer` ivar when nil */ +- (void)ensureLayerExists; + @end diff --git a/React/Views/NSView+React.m b/React/Views/NSView+React.m index 382e3f3afe..8a9caf24fd 100644 --- a/React/Views/NSView+React.m +++ b/React/Views/NSView+React.m @@ -295,4 +295,18 @@ - (NSView *)reactAccessibilityElement return self; } +#pragma mark - Other + +- (void)ensureLayerExists +{ + if (!self.layer) { + // Set `wantsLayer` first to create a "layer-backed view" instead of a "layer-hosting view". + self.wantsLayer = YES; + + CALayer *layer = [CALayer layer]; + layer.delegate = (id)self; + self.layer = layer; + } +} + @end diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index 5446f0e680..78396a5dbf 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -576,18 +576,6 @@ - (void)reactSetFrame:(CGRect)frame } } -- (void)ensureLayerExists -{ - if (!self.layer) { - // Set `wantsLayer` first to create a "layer-backed view" instead of a "layer-hosting view". - self.wantsLayer = YES; - - CALayer *layer = [CALayer layer]; - layer.delegate = self; - self.layer = layer; - } -} - - (void)displayLayer:(CALayer *)layer { if (self.shouldBeTransformed) { diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index b172e7a330..734673d81b 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -88,16 +88,6 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio return nil; } -- (void)checkLayerExists:(NSView *)view -{ - if (!view.layer) { - [view setWantsLayer:YES]; - CALayer *viewLayer = [CALayer layer]; - viewLayer.delegate = (id)view; - [view setLayer:viewLayer]; - } -} - #pragma mark - View properties #if TARGET_OS_TV @@ -168,7 +158,7 @@ - (void)checkLayerExists:(NSView *)view RCT_CUSTOM_VIEW_PROPERTY(opacity, float, RCTView) { if (json) { - [self checkLayerExists:view]; + [view ensureLayerExists]; [view.layer setOpacity:[RCTConvert float:json]]; } else { [view.layer setOpacity:1]; @@ -193,7 +183,7 @@ - (void)checkLayerExists:(NSView *)view } RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) { if ([view respondsToSelector:@selector(setBorderRadius:)]) { - [self checkLayerExists:view]; + [view ensureLayerExists]; view.borderRadius = json ? [RCTConvert CGFloat:json] : defaultView.borderRadius; } else { view.layer.cornerRadius = json ? [RCTConvert CGFloat:json] : defaultView.layer.cornerRadius; @@ -202,7 +192,7 @@ - (void)checkLayerExists:(NSView *)view RCT_CUSTOM_VIEW_PROPERTY(borderColor, CGColor, RCTView) { if ([view respondsToSelector:@selector(setBorderColor:)]) { - [self checkLayerExists:view]; + [view ensureLayerExists]; view.borderColor = json ? [RCTConvert CGColor:json] : defaultView.borderColor; } else { view.layer.borderColor = json ? [RCTConvert CGColor:json] : defaultView.layer.borderColor; @@ -211,7 +201,7 @@ - (void)checkLayerExists:(NSView *)view RCT_CUSTOM_VIEW_PROPERTY(borderWidth, float, RCTView) { if ([view respondsToSelector:@selector(setBorderWidth:)]) { - [self checkLayerExists:view]; + [view ensureLayerExists]; view.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.borderWidth; } else { view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth; @@ -262,14 +252,14 @@ - (void)checkLayerExists:(NSView *)view RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, float, RCTView) \ { \ if ([view respondsToSelector:@selector(setBorder##SIDE##Width:)]) { \ - [self checkLayerExists:view]; \ + [view ensureLayerExists]; \ view.border##SIDE##Width = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Width; \ } \ } \ RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Color, NSColor, RCTView) \ { \ if ([view respondsToSelector:@selector(setBorder##SIDE##Color:)]) { \ - [self checkLayerExists:view]; \ + [view ensureLayerExists]; \ view.border##SIDE##Color = json ? [RCTConvert CGColor:json] : defaultView.border##SIDE##Color; \ } \ } From 642fad3afe931b7bfaa42f388986252b5ad1bc83 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 14:20:42 -0400 Subject: [PATCH 02/11] refactor: simplify "ensureLayerExists" --- React/Views/NSView+React.m | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/React/Views/NSView+React.m b/React/Views/NSView+React.m index 8a9caf24fd..088acee673 100644 --- a/React/Views/NSView+React.m +++ b/React/Views/NSView+React.m @@ -300,12 +300,8 @@ - (NSView *)reactAccessibilityElement - (void)ensureLayerExists { if (!self.layer) { - // Set `wantsLayer` first to create a "layer-backed view" instead of a "layer-hosting view". self.wantsLayer = YES; - - CALayer *layer = [CALayer layer]; - layer.delegate = (id)self; - self.layer = layer; + self.layer.delegate = (id)self; } } From 91f4cb26be566b0cec6c6762bdb49f07ce7b88e3 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 11:47:08 -0400 Subject: [PATCH 03/11] Enable edge antialiasing only for transforms with perspective Taken from: https://github.com/facebook/react-native/commit/cbf65f2cf424b38f1449b504849bad367d2856dc --- React/Views/RCTViewManager.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 734673d81b..3c327470b5 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -141,8 +141,8 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio view.layer.transform = transform; } - // TODO: Improve this by enabling edge antialiasing only for transforms with rotation or skewing - view.layer.edgeAntialiasingMask = !CATransform3DIsIdentity(transform); + // Enable edge antialiasing in perspective transforms + view.layer.edgeAntialiasingMask = !(transform.m34 == 0.0f); } RCT_CUSTOM_VIEW_PROPERTY(draggedTypes, NSArray*, RCTView) From b3f6888a04997065ee9d685cd7dec0f3ca68ff38 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 11:46:15 -0400 Subject: [PATCH 04/11] fix: "transform" prop of RCTView We cannot set "layer.transform" directly except from the "displayLayer:" method, because AppKit often sets it without respecting explicit values. For example, the "setFrame:" method is one spot, but there are others. --- React/Views/RCTView.h | 4 ---- React/Views/RCTView.m | 11 +++++++---- React/Views/RCTViewManager.m | 14 +------------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h index 5ed0ffd558..d3b697929e 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -113,11 +113,7 @@ @property (nonatomic, assign) CGFloat borderEndWidth; @property (nonatomic, assign) CGFloat borderWidth; -/** - * Initial tranformation for a view which is not rendered yet - */ @property (nonatomic, assign) CATransform3D transform; -@property (nonatomic, assign) bool shouldBeTransformed; @property (nonatomic, copy) RCTDirectEventBlock onDragEnter; @property (nonatomic, copy) RCTDirectEventBlock onDragLeave; diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index 78396a5dbf..bb7f363bfc 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -124,6 +124,7 @@ - (instancetype)initWithFrame:(CGRect)frame _borderBottomStartRadius = -1; _borderBottomEndRadius = -1; _borderStyle = RCTBorderStyleSolid; + _transform = CATransform3DIdentity; self.clipsToBounds = NO; } @@ -192,6 +193,7 @@ - (void)setPointerEvents:(RCTPointerEvents)pointerEvents - (void)setTransform:(CATransform3D)transform { _transform = transform; + [self setNeedsDisplay:YES]; } - (NSView *)hitTest:(CGPoint)point @@ -578,11 +580,12 @@ - (void)reactSetFrame:(CGRect)frame - (void)displayLayer:(CALayer *)layer { - if (self.shouldBeTransformed) { - self.layer.transform = self.transform; - self.shouldBeTransformed = NO; + if (!CATransform3DEqualToTransform(_transform, layer.transform)) { + layer.transform = _transform; + // Enable edge antialiasing in perspective transforms + layer.edgeAntialiasingMask = !(_transform.m34 == 0.0f); } - + if (CGSizeEqualToSize(layer.bounds.size, CGSizeZero)) { return; } diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index 3c327470b5..b3474a3737 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -131,19 +131,7 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio view.layer.rasterizationScale = view.layer.shouldRasterize ? [NSScreen mainScreen].backingScaleFactor : defaultView.layer.rasterizationScale; } -RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, RCTView) -{ - CATransform3D transform = json ? [RCTConvert CATransform3D:json] : defaultView.layer.transform; - if ([view respondsToSelector:@selector(shouldBeTransformed)] && !view.superview) { - view.shouldBeTransformed = YES; - view.transform = transform; - } else { - view.layer.transform = transform; - } - - // Enable edge antialiasing in perspective transforms - view.layer.edgeAntialiasingMask = !(transform.m34 == 0.0f); -} +RCT_EXPORT_VIEW_PROPERTY(transform, CATransform3D) RCT_CUSTOM_VIEW_PROPERTY(draggedTypes, NSArray*, RCTView) { From 2a2c404cc97ee4cfc85188df536ea9cb199e93a8 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 14:33:45 -0400 Subject: [PATCH 05/11] fix: add "transform" property to NSView This prevents a crash when a RCTViewManager represents a NSView subclass (instead of a RCTView subclass), which requires the subclass to implement its own "transform" property and override its "displayLayer:" method. --- React/Views/NSView+React.h | 1 + React/Views/NSView+React.m | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/React/Views/NSView+React.h b/React/Views/NSView+React.h index 61ece098e3..f9d469debf 100644 --- a/React/Views/NSView+React.h +++ b/React/Views/NSView+React.h @@ -113,6 +113,7 @@ * UIKit replacement */ @property (nonatomic, assign) BOOL clipsToBounds; +@property (nonatomic, assign) CATransform3D transform; /** Populate the `layer` ivar when nil */ - (void)ensureLayerExists; diff --git a/React/Views/NSView+React.m b/React/Views/NSView+React.m index 088acee673..ea08ab4236 100644 --- a/React/Views/NSView+React.m +++ b/React/Views/NSView+React.m @@ -305,4 +305,16 @@ - (void)ensureLayerExists } } +- (CATransform3D)transform +{ + return CATransform3DIdentity; +} + +- (void)setTransform:(__unused CATransform3D)transform +{ + // Do nothing by default. + // Native views must synthesize their own "transform" property, + // override "displayLayer:", and apply the transform there. +} + @end From c7a2bc3fc0e733159f249cdba7f73f5ac1afff13 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 11:49:14 -0400 Subject: [PATCH 06/11] fix: backfaceVisibility and shadow* props Ensure a layer exists before setting these props. --- React/Views/RCTViewManager.h | 20 ++++++++++++++++++++ React/Views/RCTViewManager.m | 23 ++++++----------------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index 23d50cd915..99c41555e9 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -119,4 +119,24 @@ RCT_REMAP_VIEW_PROPERTY(name, __custom__, type) \ RCT_REMAP_SHADOW_PROPERTY(name, __custom__, type) \ - (void)set_##name:(id)json forShadowView:(viewClass *)view +/** + * This macro maps a named property to an arbitrary key path in the view's layer. + */ +#define RCT_REMAP_LAYER_PROPERTY(name, keyPath, type) \ +RCT_CUSTOM_VIEW_PROPERTY(name, type, RCTView) \ +{ \ + if (json) { \ + [view ensureLayerExists]; \ + view.layer.keyPath = [RCTConvert type:json]; \ + } else { \ + view.layer.keyPath = defaultView.layer.keyPath; \ + } \ +} + +/** + * This handles the simple case, where JS and native property names match. + */ +#define RCT_EXPORT_LAYER_PROPERTY(name, type) \ +RCT_REMAP_LAYER_PROPERTY(name, name, type) + @end diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index b3474a3737..f78c9fea58 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -111,11 +111,12 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio RCT_REMAP_VIEW_PROPERTY(testID, reactAccessibilityElement.accessibilityIdentifier, NSString) RCT_EXPORT_VIEW_PROPERTY(backgroundColor, NSColor) -RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t) -RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor) -RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize) -RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float) -RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat) +RCT_REMAP_LAYER_PROPERTY(backfaceVisibility, doubleSided, css_backface_visibility_t) +RCT_EXPORT_LAYER_PROPERTY(opacity, float) +RCT_EXPORT_LAYER_PROPERTY(shadowColor, CGColor) +RCT_EXPORT_LAYER_PROPERTY(shadowOffset, CGSize) +RCT_EXPORT_LAYER_PROPERTY(shadowOpacity, float) +RCT_EXPORT_LAYER_PROPERTY(shadowRadius, CGFloat) RCT_REMAP_VIEW_PROPERTY(toolTip, toolTip, NSString) RCT_CUSTOM_VIEW_PROPERTY(overflow, YGOverflow, RCTView) { @@ -142,18 +143,6 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio [view registerForDraggedTypes:defaultView.registeredDraggedTypes]; } } - -RCT_CUSTOM_VIEW_PROPERTY(opacity, float, RCTView) -{ - if (json) { - [view ensureLayerExists]; - [view.layer setOpacity:[RCTConvert float:json]]; - } else { - [view.layer setOpacity:1]; - } -} - - RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTPointerEvents, RCTView) { if ([view respondsToSelector:@selector(setPointerEvents:)]) { From a8c8218570ec4d1f38a1a11c6fb8e95c2d0fd2ee Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 11:50:23 -0400 Subject: [PATCH 07/11] fix: call "ensureLayerExists" wherever a layer is accessed --- React/Views/RCTViewManager.m | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index f78c9fea58..6605ae98c1 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -128,6 +128,9 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio } RCT_CUSTOM_VIEW_PROPERTY(shouldRasterizeIOS, BOOL, RCTView) { + if (json) { + [view ensureLayerExists]; + } view.layer.shouldRasterize = json ? [RCTConvert BOOL:json] : defaultView.layer.shouldRasterize; view.layer.rasterizationScale = view.layer.shouldRasterize ? [NSScreen mainScreen].backingScaleFactor : defaultView.layer.rasterizationScale; } @@ -150,17 +153,18 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio return; } } - - RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView) { if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) { view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews; } } -RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) { - if ([view respondsToSelector:@selector(setBorderRadius:)]) { +RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) +{ + if (json) { [view ensureLayerExists]; + } + if ([view respondsToSelector:@selector(setBorderRadius:)]) { view.borderRadius = json ? [RCTConvert CGFloat:json] : defaultView.borderRadius; } else { view.layer.cornerRadius = json ? [RCTConvert CGFloat:json] : defaultView.layer.cornerRadius; @@ -168,8 +172,10 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio } RCT_CUSTOM_VIEW_PROPERTY(borderColor, CGColor, RCTView) { - if ([view respondsToSelector:@selector(setBorderColor:)]) { + if (json) { [view ensureLayerExists]; + } + if ([view respondsToSelector:@selector(setBorderColor:)]) { view.borderColor = json ? [RCTConvert CGColor:json] : defaultView.borderColor; } else { view.layer.borderColor = json ? [RCTConvert CGColor:json] : defaultView.layer.borderColor; @@ -177,8 +183,10 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio } RCT_CUSTOM_VIEW_PROPERTY(borderWidth, float, RCTView) { - if ([view respondsToSelector:@selector(setBorderWidth:)]) { + if (json) { [view ensureLayerExists]; + } + if ([view respondsToSelector:@selector(setBorderWidth:)]) { view.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.borderWidth; } else { view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth; @@ -186,6 +194,9 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictio } RCT_CUSTOM_VIEW_PROPERTY(borderStyle, RCTBorderStyle, RCTView) { + if (json) { + [view ensureLayerExists]; + } if ([view respondsToSelector:@selector(setBorderStyle:)]) { view.borderStyle = json ? [RCTConvert RCTBorderStyle:json] : defaultView.borderStyle; } From 905b944ed7786c8b073bf580ad19ddde9b4243d2 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 14:20:16 -0400 Subject: [PATCH 08/11] fix: ensure layer exists in "reactSetFrame:" Otherwise, the anchor point won't be set. --- React/Views/NSView+React.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/React/Views/NSView+React.m b/React/Views/NSView+React.m index ea08ab4236..daae6304b3 100644 --- a/React/Views/NSView+React.m +++ b/React/Views/NSView+React.m @@ -175,10 +175,10 @@ - (void)reactSetFrame:(CGRect)frame return; } + [self ensureLayerExists]; self.frame = frame; - - // TODO: why position matters? It's only produce bugs - // OSX requires position and anchor point to rotate from center + + // Ensure the anchorPoint is in the center. self.layer.position = position; self.layer.bounds = bounds; self.layer.anchorPoint = anchor; From fa28701469188d2b757af84934e13014dacbed9d Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Sun, 17 Mar 2019 14:50:01 -0400 Subject: [PATCH 09/11] fix: set "position" and "anchorPoint" in "displayLayer:" Otherwise, the anchorPoint may be reverted to the top-left. --- React/Views/RCTView.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index bb7f363bfc..fa76455d14 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -586,6 +586,10 @@ - (void)displayLayer:(CALayer *)layer layer.edgeAntialiasingMask = !(_transform.m34 == 0.0f); } + // Ensure the anchorPoint is in the center. + layer.position = (CGPoint){CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)}; + layer.anchorPoint = (CGPoint){0.5, 0.5}; + if (CGSizeEqualToSize(layer.bounds.size, CGSizeZero)) { return; } From 572a27491206a1bdc62fd108fccefc4b8a7b4b37 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Tue, 19 Mar 2019 13:47:40 -0400 Subject: [PATCH 10/11] fix: apply `_transform` in reactSetFrame The "setFrame:" method overrides our transform, so it needs to be reapplied. --- React/Views/RCTView.m | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index fa76455d14..dcaea210d6 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -575,16 +575,24 @@ - (void)reactSetFrame:(CGRect)frame [super reactSetFrame:frame]; if (!CGSizeEqualToSize(self.bounds.size, oldSize)) { [self.layer setNeedsDisplay]; + } else if (!CATransform3DIsIdentity(_transform)) { + [self applyTransform:self.layer]; } } -- (void)displayLayer:(CALayer *)layer +- (void)applyTransform:(CALayer *)layer { if (!CATransform3DEqualToTransform(_transform, layer.transform)) { layer.transform = _transform; // Enable edge antialiasing in perspective transforms layer.edgeAntialiasingMask = !(_transform.m34 == 0.0f); } +} + +- (void)displayLayer:(CALayer *)layer +{ + // Applying the transform here ensures it's not overridden by AppKit internals. + [self applyTransform:layer]; // Ensure the anchorPoint is in the center. layer.position = (CGPoint){CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)}; From 33ac749551478d61d5f0240aba799847ec405064 Mon Sep 17 00:00:00 2001 From: Alec Larson Date: Tue, 19 Mar 2019 13:37:35 -0400 Subject: [PATCH 11/11] fix: RCTView setBackgroundColor - The "_backgroundColor" ivar was not being set when called with nil. - The method assumed that the "layer" property could be nullified when "backgroundColor" is nil, which is definitely not the case. --- React/Views/RCTView.h | 14 +++++++------- React/Views/RCTView.m | 19 ++++--------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h index d3b697929e..fb70bd8d55 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -46,8 +46,6 @@ */ + (NSEdgeInsets)contentInsetsForView:(NSView *)curView; -- (void)setBackgroundColor:(NSColor *)backgroundColor; - /** * Layout direction of the view. * This is inherited from UIView+React, but we override it here @@ -113,16 +111,18 @@ @property (nonatomic, assign) CGFloat borderEndWidth; @property (nonatomic, assign) CGFloat borderWidth; +/** + * Border styles. + */ +@property (nonatomic, assign) RCTBorderStyle borderStyle; + + @property (nonatomic, assign) CATransform3D transform; +@property (nonatomic, copy) NSColor *backgroundColor; @property (nonatomic, copy) RCTDirectEventBlock onDragEnter; @property (nonatomic, copy) RCTDirectEventBlock onDragLeave; @property (nonatomic, copy) RCTDirectEventBlock onDrop; @property (nonatomic, copy) RCTDirectEventBlock onContextMenuItemClick; -/** - * Border styles. - */ -@property (nonatomic, assign) RCTBorderStyle borderStyle; - @end diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index dcaea210d6..90f42709c0 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -426,22 +426,11 @@ - (NSColor *)backgroundColor - (void)setBackgroundColor:(NSColor *)backgroundColor { - if ([_backgroundColor isEqual:backgroundColor]) { - return; - } - if (backgroundColor == nil) { - [self setWantsLayer:NO]; - self.layer = NULL; - return; - } - if (![self wantsLayer] || self.layer == nil) { - [self setWantsLayer:YES]; - self.layer.delegate = self; - } - [self.layer setBackgroundColor:[backgroundColor CGColor]]; - [self.layer setNeedsDisplay]; - [self setNeedsDisplay:YES]; _backgroundColor = backgroundColor; + + [self ensureLayerExists]; + self.layer.backgroundColor = backgroundColor.CGColor; + [self.layer setNeedsDisplay]; } static CGFloat RCTDefaultIfNegativeTo(CGFloat defaultValue, CGFloat x) {