Skip to content

Commit

Permalink
Track backgroundColor in ASDisplayNode (#1640)
Browse files Browse the repository at this point in the history
To preserve dynamic color capabilities for layer-backed
nodes, we need to track backgroundColor as a UIColor. In layer backed nodes we lose the dynamic color logic since we
extract a CGColor from the UIColor applied to ASDisplayNode. With this
change we can respond to changes in UITraitCollection.userInterfaceStyle
for backgroundColor.
  • Loading branch information
rahul-malik committed Aug 27, 2019
1 parent 1961a5a commit bda0a5b
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 37 deletions.
9 changes: 9 additions & 0 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,15 @@ - (void)asyncTraitCollectionDidChangeWithPreviousTraitCollection:(ASPrimitiveTra
if (self.primitiveTraitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle) {
// When changing between light and dark mode, often the entire node needs to re-render.
// This change doesn't happen frequently so it's fairly safe to render nodes again
if (_loaded(self) && self.isLayerBacked) {
// Background colors do not dynamically update for layer backed nodes since they utilize CGColorRef
// instead of UIColor. We utilize the _backgroundColor instance variable to track the full dynamic color
// and apply any changes here when trait collection updates occur.
CGColorRef cgBackgroundColor = _backgroundColor.CGColor;
if (!CGColorEqualToColor(_layer.backgroundColor, cgBackgroundColor)) {
_layer.backgroundColor = cgBackgroundColor;
}
}
__instanceLock__.unlock();
[self setNeedsDisplay];
return;
Expand Down
22 changes: 10 additions & 12 deletions Source/Private/ASDisplayNode+UIViewBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -746,10 +746,10 @@ - (UIColor *)backgroundColor
Note: We can no longer rely simply on the layers backgroundColor value if the color is set directly on `_view`
There is no longer a 1:1 mapping between _view.backgroundColor and _layer.backgroundColor after testing in iOS 13 / Xcode 11 so we should prefer one or the other depending on the backing type for the node (view or layer)
*/
if (!_flags.layerBacked) {
return _view.backgroundColor;
if (_flags.layerBacked) {
return _backgroundColor;
} else {
return [UIColor colorWithCGColor:_layer.backgroundColor];
return _view.backgroundColor;
}
}
return ASDisplayNodeGetPendingState(self).backgroundColor;
Expand All @@ -759,13 +759,11 @@ - (void)setBackgroundColor:(UIColor *)newBackgroundColor
{
_bridge_prologue_write;
BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self);
CGColorRef newBackgroundCGColor = nil;
if (shouldApply) {
CGColorRef oldBackgroundCGColor = _layer.backgroundColor;

UIColor *oldBackgroundColor = _backgroundColor;
_backgroundColor = newBackgroundColor;
if (_flags.layerBacked) {
_layer.backgroundColor = newBackgroundColor.CGColor;
newBackgroundCGColor = _layer.backgroundColor;
_layer.backgroundColor = _backgroundColor.CGColor;
} else {
/*
NOTE: Setting to the view and layer individually is necessary.
Expand All @@ -775,19 +773,19 @@ - (void)setBackgroundColor:(UIColor *)newBackgroundColor
Given that UIColor / UIView has dynamic capabilties now, we should set directly to the view and make sure that the layers value is consistent here.
*/
_view.backgroundColor = newBackgroundColor;
_view.backgroundColor = _backgroundColor;
// Gather the CGColorRef from the view incase there are any changes it might apply to which CGColorRef is returned for dynamic colors
newBackgroundCGColor = _view.backgroundColor.CGColor;
_layer.backgroundColor = newBackgroundCGColor;
_layer.backgroundColor = _view.backgroundColor.CGColor;
}

if (!CGColorEqualToColor(oldBackgroundCGColor, newBackgroundCGColor)) {
if (![oldBackgroundColor isEqual:newBackgroundColor]) {
[self setNeedsDisplay];
}
} else {
// NOTE: If we're in the background, we cannot read the current value of bgcolor (if loaded).
// When the pending state is applied to the view on main, we will call `setNeedsDisplay` if
// the new background color doesn't match the one on the layer.
_backgroundColor = newBackgroundColor;
ASDisplayNodeGetPendingState(self).backgroundColor = newBackgroundColor;
}
}
Expand Down
8 changes: 6 additions & 2 deletions Source/Private/ASDisplayNodeInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ static constexpr CACornerMask kASCACornerAllCorners =
ASCornerRoundingType _cornerRoundingType;
ASDisplayNodePerformanceMeasurementOptions _measurementOptions;
ASDisplayNodeMethodOverrides _methodOverrides;
// Tinting support
UIColor *_tintColor;

// Dynamic colors support
UIColor *_backgroundColor;

@protected
ASDisplayNode * __weak _supernode;
Expand Down Expand Up @@ -253,8 +258,7 @@ static constexpr CACornerMask kASCACornerAllCorners =
// These properties are used on iOS 10 and lower, where safe area is not supported by UIKit.
UIEdgeInsets _fallbackSafeAreaInsets;

// Tinting support
UIColor *_tintColor;


#pragma mark - ASDisplayNode (Debugging)
ASLayout *_unflattenedLayout;
Expand Down
51 changes: 28 additions & 23 deletions Tests/ASDisplayNodeSnapshotTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -76,28 +76,33 @@ - (void)testClippingCornerRounding
}
}

// Enable test when background color on ASDisplayNode PR merges
//- (void)testUserInterfaceStyleSnapshotTesting
//{
// if (@available(iOS 13.0, *)) {
// UITraitCollection.currentTraitCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight];
//
// ASDisplayNode *node = [[ASDisplayNode alloc] init];
// [node setLayerBacked:YES];
//
// node.backgroundColor = [UIColor systemBackgroundColor];
//
// node.style.preferredSize = CGSizeMake(100, 100);
// ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)));
//
// [[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight] performAsCurrentTraitCollection:^{
// ASSnapshotVerifyNode(node, @"user_interface_style_light");
// }];
//
// [[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark] performAsCurrentTraitCollection:^{
// ASSnapshotVerifyNode(node, @"user_interface_style_dark");
// }];
// }
//}
#if AS_AT_LEAST_IOS13

- (void)testUserInterfaceStyleSnapshotTesting
{
if (@available(iOS 13.0, *)) {
ASConfiguration *config = [ASConfiguration new];
config.experimentalFeatures = ASExperimentalTraitCollectionDidChangeWithPreviousCollection;
[ASConfigurationManager test_resetWithConfiguration:config];

ASDisplayNode *node = [[ASDisplayNode alloc] init];
[node setLayerBacked:YES];

node.backgroundColor = [UIColor systemBackgroundColor];

node.style.preferredSize = CGSizeMake(100, 100);
ASDisplayNodeSizeToFitSizeRange(node, ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)));

[[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight] performAsCurrentTraitCollection:^{
ASSnapshotVerifyNode(node, @"user_interface_style_light");
}];

[[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark] performAsCurrentTraitCollection:^{
ASSnapshotVerifyNode(node, @"user_interface_style_dark");
}];
}
}

#endif // #if AS_AT_LEAST_IOS13

@end
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit bda0a5b

Please sign in to comment.