@@ -566,6 +566,12 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
566566 // re-applying individual sub-values which weren't changed.
567567 [super updateLayoutMetrics: layoutMetrics oldLayoutMetrics: _layoutMetrics];
568568
569+ // Capture the frame size that was used by updateProps to resolve the
570+ // transform, before overwriting _layoutMetrics. This is important because
571+ // _layoutMetrics may be stale (e.g., from a recycled view) and differ from
572+ // the oldLayoutMetrics parameter (which comes from the shadow tree).
573+ auto previousFrameSize = _layoutMetrics.frame .size ;
574+
569575 _layoutMetrics = layoutMetrics;
570576 _needsInvalidateLayer = YES ;
571577
@@ -583,8 +589,12 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
583589 _backgroundColorLayer.frame = CGRectMake (0 , 0 , self.layer .bounds .size .width , self.layer .bounds .size .height );
584590 }
585591
592+ // Recompute the transform whenever the layout size differs from what was
593+ // used in updateProps. Using previousFrameSize (the stored _layoutMetrics)
594+ // instead of the oldLayoutMetrics parameter ensures correctness even when
595+ // the view was recycled with stale dimensions.
586596 if ((_props->transformOrigin .isSet () || !_props->transform .operations .empty ()) &&
587- layoutMetrics.frame .size != oldLayoutMetrics. frame . size ) {
597+ layoutMetrics.frame .size != previousFrameSize ) {
588598 auto newTransform = _props->resolveTransform (layoutMetrics);
589599 self.layer .transform = RCTCATransform3DFromTransformMatrix (newTransform);
590600 }
@@ -654,6 +664,7 @@ - (void)prepareForRecycle
654664 _isJSResponder = NO ;
655665 _removeClippedSubviews = NO ;
656666 _reactSubviews = [NSMutableArray new ];
667+ _layoutMetrics = {};
657668}
658669
659670- (void )setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN : (NSSet <NSString *> *_Nullable)props
0 commit comments