Skip to content

Commit 863a4a6

Browse files
committed
Review changes
1 parent 587dea7 commit 863a4a6

1 file changed

Lines changed: 28 additions & 26 deletions

File tree

packages/react-native-gesture-handler/apple/RNGestureHandlerButtonComponentView.mm

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,13 @@ - (instancetype)initWithFrame:(CGRect)frame
7070

7171
- (void)prepareForRecycle
7272
{
73-
// Reset the wrapper layer's transform (and any in-flight transform animation)
74-
// before super resets _props, so the recycled view starts in a clean visual
75-
// state regardless of whether the next updateProps: re-runs applyStartAnimationState.
7673
[self.layer removeAnimationForKey:@"transform"];
7774
self.layer.transform = CATransform3DIdentity;
7875

7976
[_buttonView prepareForRecycle];
8077

81-
// The next updateProps: comparison treats this view as having "previous"
82-
// props equal to defaults, so identical defaults across mounts won't trigger
83-
// applyStartAnimationState — leaving non-1.0 default opacity/scale/underlay
84-
// values unapplied. Force a one-shot reset on the next updateProps:.
78+
// Force the next updateProps: to re-run applyStartAnimationState even if
79+
// the new mount's defaults match the previous mount's.
8580
_needsAnimationStateReset = YES;
8681

8782
[super prepareForRecycle];
@@ -163,24 +158,29 @@ - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetric
163158

164159
- (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask
165160
{
166-
// super.finalizeUpdates calls invalidateLayer, which unconditionally sets
167-
// self.layer.opacity = _props->opacity (the React style.opacity, default
168-
// 1.0). That overwrites the alpha set by applyStartAnimationState for non-
169-
// 1.0 defaultOpacity, and would also interrupt in-flight press animations
170-
// on a mid-press React re-render. Snapshot opacity (and any animation) and
171-
// restore it atomically.
161+
// super's invalidateLayer (called via finalizeUpdates) unconditionally sets
162+
// self.layer.opacity to the React style.opacity, overwriting our
163+
// applyStartAnimationState alpha and interrupting in-flight press
164+
// animations. Save/restore around super, but only touch what super actually
165+
// disturbed — re-adding an unchanged animation resets its progress.
172166
float savedOpacity = self.layer.opacity;
173-
CAAnimation *savedOpacityAnimation = [[self.layer animationForKey:@"opacity"] copy];
167+
CAAnimation *savedOpacityAnimation = [self.layer animationForKey:@"opacity"];
174168

175169
[super finalizeUpdates:updateMask];
176170

177-
if (savedOpacity != self.layer.opacity || savedOpacityAnimation != nil) {
171+
BOOL opacityChanged = savedOpacity != self.layer.opacity;
172+
BOOL animationChanged = savedOpacityAnimation != [self.layer animationForKey:@"opacity"];
173+
if (opacityChanged || animationChanged) {
178174
[CATransaction begin];
179175
[CATransaction setDisableActions:YES];
180-
[self.layer removeAnimationForKey:@"opacity"];
181-
self.layer.opacity = savedOpacity;
182-
if (savedOpacityAnimation) {
183-
[self.layer addAnimation:savedOpacityAnimation forKey:@"opacity"];
176+
if (animationChanged) {
177+
[self.layer removeAnimationForKey:@"opacity"];
178+
if (savedOpacityAnimation) {
179+
[self.layer addAnimation:savedOpacityAnimation forKey:@"opacity"];
180+
}
181+
}
182+
if (opacityChanged) {
183+
self.layer.opacity = savedOpacity;
184184
}
185185
[CATransaction commit];
186186
}
@@ -307,13 +307,15 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
307307
{
308308
const auto &newProps = *std::static_pointer_cast<const RNGestureHandlerButtonProps>(props);
309309

310-
// Re-apply the idle visual state only on first mount, after recycling, or
311-
// when one of the default values actually changed. Doing it on every commit
312-
// interrupts in-flight press animations whenever React re-renders the children
313-
// mid-press.
314-
BOOL shouldApplyStartAnimationState = !oldProps || _needsAnimationStateReset;
310+
// After recycling, treat diffing branches as a fresh mount so values that
311+
// survived recycling on _buttonView (e.g. pointerEvents) get re-applied.
312+
BOOL treatAsFirstMount = !oldProps || _needsAnimationStateReset;
315313
_needsAnimationStateReset = NO;
316-
if (oldProps && !shouldApplyStartAnimationState) {
314+
315+
// Avoid re-running applyStartAnimationState on every commit — it would
316+
// interrupt in-flight press animations during mid-press re-renders.
317+
BOOL shouldApplyStartAnimationState = treatAsFirstMount;
318+
if (!treatAsFirstMount) {
317319
const auto &oldButtonProps = *std::static_pointer_cast<const RNGestureHandlerButtonProps>(oldProps);
318320
shouldApplyStartAnimationState = oldButtonProps.defaultOpacity != newProps.defaultOpacity ||
319321
oldButtonProps.defaultScale != newProps.defaultScale ||
@@ -345,7 +347,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
345347
// This is necessary because pointerEvents is redefined in the spec,
346348
// which shadows the base property with a different, incompatible type.
347349
const auto &newViewProps = static_cast<const ViewProps &>(newProps);
348-
if (!oldProps) {
350+
if (treatAsFirstMount) {
349351
_buttonView.pointerEvents = RCTPointerEventsToEnum(newViewProps.pointerEvents);
350352
} else {
351353
const auto &oldButtonProps = *std::static_pointer_cast<const RNGestureHandlerButtonProps>(oldProps);

0 commit comments

Comments
 (0)