From dc7a6202e505b4d26738e912f0e345482f6d93fd Mon Sep 17 00:00:00 2001 From: Ignacio Paradisi Date: Mon, 27 Jul 2026 16:44:18 -0700 Subject: [PATCH 1/3] Resolve dynamic background colors against node's trait collection Layer-backed nodes use CGColorRef instead of UIColor, so dynamic (e.g. dark mode) background colors were not resolved for the current trait collection. Resolve the UIColor via resolvedColorWithTraitCollection: using the node's primitive trait collection before extracting the CGColor, both when setting the background color and when reapplying it on trait collection changes. --- Source/ASDisplayNode.mm | 6 +++--- Source/Private/ASDisplayNode+UIViewBridge.mm | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index fa2d4c1bc..db459a7bb 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -458,10 +458,10 @@ - (void)asyncTraitCollectionDidChangeWithPreviousTraitCollection:(ASPrimitiveTra CGFloat cornerRadius = self->_cornerRadius; ASCornerRoundingType cornerRoundingType = self->_cornerRoundingType; UIColor *backgroundColor = self->_backgroundColor; + ASPrimitiveTraitCollection currentPrimitiveTraitCollection = self->_primitiveTraitCollection; self->__instanceLock__.unlock(); - // TODO: we should resolve color using node's trait collection - // but Texture changes it from many places, so we may receive the wrong one. - CGColorRef cgBackgroundColor = backgroundColor.CGColor; + UITraitCollection *traitCollection = ASPrimitiveTraitCollectionToUITraitCollection(currentPrimitiveTraitCollection); + CGColorRef cgBackgroundColor = [backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor; if (!CGColorEqualToColor(self->_layer.backgroundColor, cgBackgroundColor)) { // Background colors do not dynamically update for layer backed nodes since they utilize CGColorRef // instead of UIColor. Non layer backed node also receive color to the layer (see [_ASPendingState -applyToView:withSpecialPropertiesHandling:]). diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index aa8e0e814..55a0e7a9f 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -762,8 +762,9 @@ - (void)setBackgroundColor:(UIColor *)newBackgroundColor if (shouldApply) { UIColor *oldBackgroundColor = _backgroundColor; _backgroundColor = newBackgroundColor; + UITraitCollection *traitCollection = ASPrimitiveTraitCollectionToUITraitCollection(_primitiveTraitCollection); if (_flags.layerBacked) { - _layer.backgroundColor = _backgroundColor.CGColor; + _layer.backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor; } else { /* NOTE: Setting to the view and layer individually is necessary. @@ -775,7 +776,7 @@ - (void)setBackgroundColor:(UIColor *)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 - _layer.backgroundColor = _view.backgroundColor.CGColor; + _layer.backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor; } if (![oldBackgroundColor isEqual:newBackgroundColor]) { From a568de0e2b38b101b5a0b0de1e9f25df84794678 Mon Sep 17 00:00:00 2001 From: Ignacio Paradisi Date: Mon, 27 Jul 2026 17:58:07 -0700 Subject: [PATCH 2/3] Create ResolveBackgroundColorWithNodeTraits experiment --- Source/ASDisplayNode.mm | 11 +++++++++-- Source/ASExperimentalFeatures.h | 1 + Source/ASExperimentalFeatures.mm | 3 ++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/ASDisplayNode.mm b/Source/ASDisplayNode.mm index db459a7bb..de18942ed 100644 --- a/Source/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -460,8 +460,15 @@ - (void)asyncTraitCollectionDidChangeWithPreviousTraitCollection:(ASPrimitiveTra UIColor *backgroundColor = self->_backgroundColor; ASPrimitiveTraitCollection currentPrimitiveTraitCollection = self->_primitiveTraitCollection; self->__instanceLock__.unlock(); - UITraitCollection *traitCollection = ASPrimitiveTraitCollectionToUITraitCollection(currentPrimitiveTraitCollection); - CGColorRef cgBackgroundColor = [backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor; + CGColorRef cgBackgroundColor; + if (ASActivateExperimentalFeature(ASExperimentalResolveBackgroundColorWithNodeTraits)) { + UITraitCollection *traitCollection = ASPrimitiveTraitCollectionToUITraitCollection(currentPrimitiveTraitCollection); + cgBackgroundColor = [backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor; + } else { + // TODO: we should resolve color using node's trait collection + // but Texture changes it from many places, so we may receive the wrong one. + cgBackgroundColor = backgroundColor.CGColor; + } if (!CGColorEqualToColor(self->_layer.backgroundColor, cgBackgroundColor)) { // Background colors do not dynamically update for layer backed nodes since they utilize CGColorRef // instead of UIColor. Non layer backed node also receive color to the layer (see [_ASPendingState -applyToView:withSpecialPropertiesHandling:]). diff --git a/Source/ASExperimentalFeatures.h b/Source/ASExperimentalFeatures.h index 62e7cda26..2a37aa233 100644 --- a/Source/ASExperimentalFeatures.h +++ b/Source/ASExperimentalFeatures.h @@ -35,6 +35,7 @@ typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) { ASExperimentalLockTextRendererCache = 1 << 14, // exp_lock_text_renderer_cache ASExperimentalHierarchyDisplayDidFinishIsRecursive = 1 << 15, // exp_hierarchy_display_did_finish_is_recursive ASExperimentalCheckBatchFetchingOnScroll = 1 << 16, // exp_check_batch_fetching_on_scroll + ASExperimentalResolveBackgroundColorWithNodeTraits = 1 << 17, // exp_resolve_background_color_with_node_traits ASExperimentalFeatureAll = 0xFFFFFFFF }; diff --git a/Source/ASExperimentalFeatures.mm b/Source/ASExperimentalFeatures.mm index dd41e0981..d1a0b9431 100644 --- a/Source/ASExperimentalFeatures.mm +++ b/Source/ASExperimentalFeatures.mm @@ -28,7 +28,8 @@ @"exp_no_text_renderer_cache", @"exp_lock_text_renderer_cache", @"exp_hierarchy_display_did_finish_is_recursive", - @"exp_check_batch_fetching_on_scroll"])); + @"exp_check_batch_fetching_on_scroll", + @"exp_resolve_background_color_with_node_traits"])); if (flags == ASExperimentalFeatureAll) { return allNames; From 40c1dd8395fca91eb2073e4086f06ad13dd93271 Mon Sep 17 00:00:00 2001 From: Ignacio Paradisi Date: Mon, 27 Jul 2026 18:11:07 -0700 Subject: [PATCH 3/3] Put resolve background colors for UIViewBridge behind an experiment flag --- Source/Private/ASDisplayNode+UIViewBridge.mm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index 55a0e7a9f..19a656558 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -762,9 +762,10 @@ - (void)setBackgroundColor:(UIColor *)newBackgroundColor if (shouldApply) { UIColor *oldBackgroundColor = _backgroundColor; _backgroundColor = newBackgroundColor; - UITraitCollection *traitCollection = ASPrimitiveTraitCollectionToUITraitCollection(_primitiveTraitCollection); + BOOL resolveWithNodeTraits = ASActivateExperimentalFeature(ASExperimentalResolveBackgroundColorWithNodeTraits); + UITraitCollection *traitCollection = resolveWithNodeTraits ? ASPrimitiveTraitCollectionToUITraitCollection(_primitiveTraitCollection) : nil; if (_flags.layerBacked) { - _layer.backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor; + _layer.backgroundColor = resolveWithNodeTraits ? [_backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor : _backgroundColor.CGColor; } else { /* NOTE: Setting to the view and layer individually is necessary. @@ -776,7 +777,7 @@ - (void)setBackgroundColor:(UIColor *)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 - _layer.backgroundColor = [_backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor; + _layer.backgroundColor = resolveWithNodeTraits ? [_backgroundColor resolvedColorWithTraitCollection:traitCollection].CGColor : _view.backgroundColor.CGColor; } if (![oldBackgroundColor isEqual:newBackgroundColor]) {