diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm index 129440f4db66..cc042d8a5d60 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm @@ -169,6 +169,30 @@ - (void)didMoveToWindow [self _restoreTextSelection]; } +// [macOS +- (void)_updateDefaultTextAttributes +{ + const auto &props = static_cast(*_props); + NSMutableDictionary *attrs = + RCTNSTextAttributesFromTextAttributes(props.getEffectiveTextAttributes(RCTFontSizeMultiplier())); + + _backedTextInputView.defaultTextAttributes = attrs; + + // Also update the existing attributed text so the visible text re-renders + // with the new color (defaultTextAttributes only affects newly typed text). + // Wrap in _comingFromJS to prevent textInputDidChange from pushing a state + // update back to the shadow tree, which would overwrite our fresh colors + // with the stale cached attributed string. + NSString *currentText = _backedTextInputView.attributedText.string; + if (currentText.length > 0) { + NSAttributedString *updated = [[NSAttributedString alloc] initWithString:currentText attributes:attrs]; + _comingFromJS = YES; + _backedTextInputView.attributedText = updated; + _comingFromJS = NO; + } +} +// macOS] + #if !TARGET_OS_OSX // [macOS] // TODO: replace with registerForTraitChanges once iOS 17.0 is the lowest supported version - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection @@ -182,8 +206,20 @@ - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection _backedTextInputView.defaultTextAttributes = RCTNSTextAttributesFromTextAttributes(newTextInputProps.getEffectiveTextAttributes(RCTFontSizeMultiplier())); } + + // [macOS + if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) { + [self _updateDefaultTextAttributes]; + } + // macOS] +} +#else // [macOS +- (void)viewDidChangeEffectiveAppearance +{ + [super viewDidChangeEffectiveAppearance]; + [self _updateDefaultTextAttributes]; } -#endif // [macOS] +#endif // macOS] - (void)reactUpdateResponderOffsetForScrollView:(RCTScrollViewComponentView *)scrollView { diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm index 207a5f044e0e..9e27e70009c3 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm @@ -146,7 +146,14 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex { RCTPlatformColor *effectiveForegroundColor = RCTUIColorFromSharedColor(textAttributes.foregroundColor) ?: [RCTPlatformColor labelColor]; // [macOS] +#if TARGET_OS_OSX // [macOS + // On macOS, colorWithAlphaComponent: converts dynamic system colors (like + // NSColor.labelColor) to static resolved colors, preventing them from + // adapting to appearance changes. Skip when opacity is 1.0 (a no-op). + if (!isnan(textAttributes.opacity) && textAttributes.opacity != 1.0f) { +#else // macOS] if (!isnan(textAttributes.opacity)) { +#endif effectiveForegroundColor = [effectiveForegroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveForegroundColor.CGColor) * textAttributes.opacity]; } @@ -158,7 +165,11 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex { RCTPlatformColor *effectiveBackgroundColor = RCTUIColorFromSharedColor(textAttributes.backgroundColor); // [macOS] +#if TARGET_OS_OSX // [macOS + if (effectiveBackgroundColor && !isnan(textAttributes.opacity) && textAttributes.opacity != 1.0f) { +#else // macOS] if (effectiveBackgroundColor && !isnan(textAttributes.opacity)) { +#endif effectiveBackgroundColor = [effectiveBackgroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveBackgroundColor.CGColor) * textAttributes.opacity]; }