@@ -122,6 +122,7 @@ @implementation RCTViewComponentView {
122122 NSMutableSet <NSString *> *_accessibilityOrderNativeIDs;
123123 RCTSwiftUIContainerViewWrapper *_swiftUIWrapper;
124124 BOOL _focusable;
125+ RCTBackgroundImageURLLoader *_backgroundImageLoader;
125126}
126127
127128#ifdef RCT_DYNAMIC_FRAMEWORKS
@@ -136,6 +137,8 @@ - (instancetype)initWithFrame:(CGRect)frame
136137 if (self = [super initWithFrame: frame]) {
137138 _props = ViewShadowNode::defaultSharedProps ();
138139 _reactSubviews = [NSMutableArray new ];
140+ _backgroundImageLoader = [RCTBackgroundImageURLLoader new ];
141+ _backgroundImageLoader.delegate = self;
139142#if !TARGET_OS_TV
140143 self.multipleTouchEnabled = YES ;
141144#endif
@@ -675,6 +678,13 @@ - (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter
675678 _eventEmitter = std::static_pointer_cast<const ViewEventEmitter>(eventEmitter);
676679}
677680
681+ - (void )updateState : (const State::Shared &)state oldState : (const State::Shared &)oldState
682+ {
683+ auto newViewState = std::static_pointer_cast<const ViewShadowNode::ConcreteState>(state);
684+ auto oldViewState = oldState ? std::static_pointer_cast<const ViewShadowNode::ConcreteState>(oldState) : nullptr ;
685+ [_backgroundImageLoader updateStateWithNewState: newViewState oldState: oldViewState];
686+ }
687+
678688- (void )updateLayoutMetrics : (const LayoutMetrics &)layoutMetrics
679689 oldLayoutMetrics : (const LayoutMetrics &)oldLayoutMetrics
680690{
@@ -775,6 +785,9 @@ - (void)prepareForRecycle
775785 _filterLayer = nil ;
776786 [self clearExistingBackgroundImageLayers ];
777787
788+ // Clean up background image observers
789+ [_backgroundImageLoader reset ];
790+
778791 _propKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN = nil ;
779792 _eventEmitter.reset ();
780793 _isJSResponder = NO ;
@@ -1310,29 +1323,55 @@ - (void)invalidateLayer
13101323 backgroundRepeat = _props->backgroundRepeat [imageIndex % _props->backgroundRepeat.size ()];
13111324 }
13121325
1313- CGSize backgroundImageSize = [RCTBackgroundImageUtils calculateBackgroundImageSize: backgroundPositioningArea
1314- itemIntrinsicSize: backgroundPositioningArea.size
1315- backgroundSize: backgroundSize
1316- backgroundRepeat: backgroundRepeat];
1317-
1318- CALayer *gradientLayer;
1326+ CALayer *itemLayer = nil ;
13191327
13201328 if (std::holds_alternative<LinearGradient>(backgroundImage)) {
1329+ CGSize backgroundImageSize =
1330+ [RCTBackgroundImageUtils calculateBackgroundImageSize: backgroundPositioningArea
1331+ itemIntrinsicSize: backgroundPositioningArea.size
1332+ backgroundSize: backgroundSize
1333+ backgroundRepeat: backgroundRepeat];
13211334 const auto &linearGradient = std::get<LinearGradient>(backgroundImage);
1322- gradientLayer = [RCTLinearGradient gradientLayerWithSize: backgroundImageSize gradient: linearGradient];
1335+ itemLayer = [RCTLinearGradient gradientLayerWithSize: backgroundImageSize gradient: linearGradient];
13231336 } else if (std::holds_alternative<RadialGradient>(backgroundImage)) {
1337+ CGSize backgroundImageSize =
1338+ [RCTBackgroundImageUtils calculateBackgroundImageSize: backgroundPositioningArea
1339+ itemIntrinsicSize: backgroundPositioningArea.size
1340+ backgroundSize: backgroundSize
1341+ backgroundRepeat: backgroundRepeat];
13241342 const auto &radialGradient = std::get<RadialGradient>(backgroundImage);
1325- gradientLayer = [RCTRadialGradient gradientLayerWithSize: backgroundImageSize gradient: radialGradient];
1343+ itemLayer = [RCTRadialGradient gradientLayerWithSize: backgroundImageSize gradient: radialGradient];
1344+ } else if (std::holds_alternative<URLBackgroundImage>(backgroundImage)) {
1345+ const auto &urlBgImage = std::get<URLBackgroundImage>(backgroundImage);
1346+ NSString *uri = [NSString stringWithUTF8String: urlBgImage.uri.c_str ()];
1347+ UIImage *loadedImage = [_backgroundImageLoader loadedImageForUri: uri];
1348+ if (loadedImage != nil ) {
1349+ // Bundled assets carry their natural size in layout units. A plain url
1350+ // has none, so fall back to the decoded image.
1351+ CGSize intrinsicSize = (urlBgImage.intrinsicWidth > 0 && urlBgImage.intrinsicHeight > 0 )
1352+ ? CGSizeMake (urlBgImage.intrinsicWidth , urlBgImage.intrinsicHeight )
1353+ : loadedImage.size ;
1354+ CGSize backgroundImageSize = [RCTBackgroundImageUtils calculateBackgroundImageSize: backgroundPositioningArea
1355+ itemIntrinsicSize: intrinsicSize
1356+ backgroundSize: backgroundSize
1357+ backgroundRepeat: backgroundRepeat];
1358+ CALayer *imageLayer = [CALayer layer ];
1359+ imageLayer.frame = CGRectMake (0 , 0 , backgroundImageSize.width , backgroundImageSize.height );
1360+ imageLayer.contents = (__bridge id )loadedImage.CGImage ;
1361+ imageLayer.contentsGravity = kCAGravityResizeAspectFill ;
1362+ itemLayer = imageLayer;
1363+ }
13261364 }
13271365
1328- if (gradientLayer != nil ) {
1366+ if (itemLayer != nil ) {
1367+ CGSize itemSize = itemLayer.frame .size ;
13291368 CALayer *backgroundImageLayer =
13301369 [RCTBackgroundImageUtils createBackgroundImageLayerWithSize: backgroundPositioningArea
13311370 paintingArea: backgroundPaintingArea
1332- itemSize: backgroundImageSize
1371+ itemSize: itemSize
13331372 backgroundPosition: backgroundPosition
13341373 backgroundRepeat: backgroundRepeat
1335- itemLayer: gradientLayer ];
1374+ itemLayer: itemLayer ];
13361375 [self shapeLayerToMatchView: backgroundImageLayer borderMetrics: borderMetricsBI];
13371376 backgroundImageLayer.masksToBounds = YES ;
13381377 backgroundImageLayer.zPosition = BACKGROUND_COLOR_ZPOSITION ;
@@ -1446,6 +1485,13 @@ - (void)clearExistingBackgroundImageLayers
14461485 [_backgroundImageLayers removeAllObjects ];
14471486}
14481487
1488+ #pragma mark - RCTBackgroundImageURLLoaderDelegate
1489+
1490+ - (void )backgroundImagesDidLoad
1491+ {
1492+ [self invalidateLayer ];
1493+ }
1494+
14491495#pragma mark - Accessibility
14501496
14511497- (NSObject *)accessibilityElement
0 commit comments