Skip to content

Commit

Permalink
Support shared element transition for images with different resizeMode (
Browse files Browse the repository at this point in the history
#7045)

This PR adds support for animating images with different `resizeMode` which already supported on Android and was missing on iOS.
  • Loading branch information
yogevbd authored Apr 7, 2021
1 parent ea1088d commit e184102
Show file tree
Hide file tree
Showing 35 changed files with 727 additions and 226 deletions.
3 changes: 3 additions & 0 deletions lib/ios/AnimatedImageView.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@

@interface AnimatedImageView : AnimatedReactView

@property(nonatomic, strong) UIImageView *fromImageView;
@property(nonatomic, strong) UIImageView *toImageView;

@end
64 changes: 62 additions & 2 deletions lib/ios/AnimatedImageView.m
Original file line number Diff line number Diff line change
@@ -1,14 +1,74 @@
#import "AnimatedImageView.h"
#import "BoundsTransition.h"
#import "CenterTransition.h"
#import "UIImageView+Transition.h"

@implementation AnimatedImageView
@implementation AnimatedImageView {
SharedElementTransitionOptions *_transitionOptions;
CGRect _originalBounds;
CGPoint _originalCenter;
UIViewContentMode _originalContentMode;
}

- (instancetype)initElement:(UIView *)element
toElement:(UIView *)toElement
transitionOptions:(SharedElementTransitionOptions *)transitionOptions {
self = [super initElement:element toElement:toElement transitionOptions:transitionOptions];
self.contentMode = element.contentMode;

_transitionOptions = transitionOptions;

_fromImageView = [self findImageView:element];
_toImageView = [self findImageView:toElement];
_originalBounds = _fromImageView.bounds;
_originalContentMode = _fromImageView.contentMode;
_originalCenter = _fromImageView.center;
_fromImageView.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

return self;
}

- (UIImageView *)findImageView:(UIView *)view {
if ([view isKindOfClass:UIImageView.class]) {
return (UIImageView *)view;
}

for (UIView *subview in view.subviews) {
return [self findImageView:subview];
}

return nil;
}

- (NSArray<id<DisplayLinkAnimation>> *)extraAnimations {
NSMutableArray *animations = NSMutableArray.new;
CGFloat startDelay = [_transitionOptions.startDelay withDefault:0];
CGFloat duration = [_transitionOptions.duration withDefault:300];
id<Interpolator> interpolator = _transitionOptions.interpolator;

[animations addObject:[[BoundsTransition alloc] initWithView:_fromImageView
from:_fromImageView.resolveBounds
to:_toImageView.resolveBounds
startDelay:startDelay
duration:duration
interpolator:interpolator]];

[animations addObject:[[CenterTransition alloc] initWithView:_fromImageView
from:_fromImageView.center
to:_toImageView.center
startDelay:startDelay
duration:duration
interpolator:interpolator]];
_fromImageView.contentMode = UIViewContentModeScaleToFill;

return animations;
}

- (void)reset {
[super reset];
_fromImageView.bounds = _originalBounds;
_fromImageView.contentMode = _originalContentMode;
_fromImageView.center = _originalCenter;
}

@end
3 changes: 3 additions & 0 deletions lib/ios/AnimatedReactView.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#import "DisplayLinkAnimation.h"
#import "RNNViewLocation.h"
#import "SharedElementTransitionOptions.h"

Expand All @@ -14,4 +15,6 @@

- (void)reset;

- (NSArray<id<DisplayLinkAnimation>> *)extraAnimations;

@end
26 changes: 17 additions & 9 deletions lib/ios/AnimatedReactView.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ @implementation AnimatedReactView {
CGRect _originalFrame;
CGFloat _originalCornerRadius;
CGRect _originalLayoutBounds;
CGPoint _originalCenter;
CATransform3D _originalTransform;
UIView *_toElement;
UIColor *_fromColor;
NSInteger _zIndex;
UIViewContentMode _originalContentMode;
SharedElementTransitionOptions *_transitionOptions;
}

Expand Down Expand Up @@ -45,34 +47,40 @@ - (NSNumber *)reactZIndex {

- (void)hijackReactElement:(UIView *)element {
_reactView = element;
_originalContentMode = _reactView.contentMode;
_originalFrame = _reactView.frame;
_originalTransform = element.layer.transform;
_originalLayoutBounds = element.layer.bounds;
self.contentMode = element.contentMode;
self.frame = self.location.fromFrame;
_originalParent = _reactView.superview;
_originalCenter = _reactView.center;
_originalCornerRadius = element.layer.cornerRadius;
_reactView.frame = self.bounds;
_originalTransform = _reactView.layer.transform;
_originalLayoutBounds = _reactView.bounds;
_reactView.layer.transform = CATransform3DIdentity;
_reactView.layer.cornerRadius = self.location.fromCornerRadius;

[self addSubview:_reactView];
}

- (void)layoutSubviews {
[super layoutSubviews];
self.reactView.bounds = self.bounds;
self.reactView.center = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
}

- (void)reset {
_reactView.frame = _originalFrame;
_reactView.center = _originalCenter;
_reactView.layer.cornerRadius = _originalCornerRadius;
_reactView.bounds = _originalLayoutBounds;
_reactView.layer.bounds = _originalLayoutBounds;
_reactView.layer.transform = _originalTransform;
_reactView.contentMode = _originalContentMode;
[_originalParent insertSubview:_reactView atIndex:self.location.index];
_toElement.hidden = NO;
_reactView.backgroundColor = _fromColor;
[self removeFromSuperview];
}

- (void)layoutSubviews {
[super layoutSubviews];
_reactView.frame = self.bounds;
- (NSArray<id<DisplayLinkAnimation>> *)extraAnimations {
return @[];
}

@end
5 changes: 5 additions & 0 deletions lib/ios/AnimatedUIImageView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import "AnimatedImageView.h"

@interface AnimatedUIImageView : AnimatedImageView

@end
8 changes: 8 additions & 0 deletions lib/ios/AnimatedUIImageView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#import "AnimatedUIImageView.h"

@implementation AnimatedUIImageView

- (void)layoutSubviews {
}

@end
5 changes: 5 additions & 0 deletions lib/ios/AnimatedViewFactory.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import "AnimatedViewFactory.h"
#import "AnimatedImageView.h"
#import "AnimatedTextView.h"
#import "AnimatedUIImageView.h"
#import "UIVIew+Utils.h"

@implementation AnimatedViewFactory
Expand All @@ -13,6 +14,10 @@ + (AnimatedReactView *)createFromElement:(UIView *)element
return [[AnimatedImageView alloc] initElement:element
toElement:toElement
transitionOptions:transitionOptions];
case ViewTypeUIImage:
return [[AnimatedUIImageView alloc] initElement:element
toElement:toElement
transitionOptions:transitionOptions];
case ViewTypeText:
return [[AnimatedTextView alloc] initElement:element
toElement:toElement
Expand Down
15 changes: 15 additions & 0 deletions lib/ios/BoundsTransition.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import "ElementBaseTransition.h"

@interface BoundsTransition : ElementBaseTransition

@property(nonatomic) CGRect fromBounds;
@property(nonatomic) CGRect toBounds;

- (instancetype)initWithView:(UIView *)view
from:(CGRect)from
to:(CGRect)to
startDelay:(NSTimeInterval)startDelay
duration:(NSTimeInterval)duration
interpolator:(id<Interpolator>)interpolator;

@end
30 changes: 30 additions & 0 deletions lib/ios/BoundsTransition.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#import "BoundsTransition.h"

@implementation BoundsTransition

- (instancetype)initWithView:(UIView *)view
from:(CGRect)from
to:(CGRect)to
startDelay:(NSTimeInterval)startDelay
duration:(NSTimeInterval)duration
interpolator:(id<Interpolator>)interpolator {
self = [super initWithView:view
startDelay:startDelay
duration:duration
interpolator:interpolator];
_fromBounds = from;
_toBounds = to;
return self;
}

- (CATransform3D)animateWithProgress:(CGFloat)p {
CGRect toBounds = [RNNInterpolator fromRect:_fromBounds
toRect:_toBounds
precent:p
interpolator:self.interpolator];
self.view.bounds = toBounds;

return CATransform3DIdentity;
}

@end
15 changes: 15 additions & 0 deletions lib/ios/CenterTransition.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import "ElementBaseTransition.h"

@interface CenterTransition : ElementBaseTransition

@property(nonatomic) CGPoint fromCenter;
@property(nonatomic) CGPoint toCenter;

- (instancetype)initWithView:(UIView *)view
from:(CGPoint)from
to:(CGPoint)to
startDelay:(NSTimeInterval)startDelay
duration:(NSTimeInterval)duration
interpolator:(id<Interpolator>)interpolator;

@end
29 changes: 29 additions & 0 deletions lib/ios/CenterTransition.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#import "CenterTransition.h"

@implementation CenterTransition

- (instancetype)initWithView:(UIView *)view
from:(CGPoint)from
to:(CGPoint)to
startDelay:(NSTimeInterval)startDelay
duration:(NSTimeInterval)duration
interpolator:(id<Interpolator>)interpolator {
self = [super initWithView:view
startDelay:startDelay
duration:duration
interpolator:interpolator];
self.fromCenter = from;
self.toCenter = to;
return self;
}

- (CATransform3D)animateWithProgress:(CGFloat)p {
CGPoint toCenter = [RNNInterpolator fromPoint:self.fromCenter
toPoint:self.toCenter
precent:p
interpolator:self.interpolator];
self.view.center = toCenter;
return CATransform3DIdentity;
}

@end
17 changes: 17 additions & 0 deletions lib/ios/PathTransition.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#import "RectTransition.h"

@interface PathTransition : RectTransition

@property(nonatomic) CGFloat fromCornerRadius;
@property(nonatomic) CGFloat toCornerRadius;

- (instancetype)initWithView:(UIView *)view
fromPath:(CGRect)fromPath
toPath:(CGRect)toPath
fromCornerRadius:(CGFloat)fromCornerRadius
toCornerRadius:(CGFloat)toCornerRadius
startDelay:(NSTimeInterval)startDelay
duration:(NSTimeInterval)duration
interpolator:(id<Interpolator>)interpolator;

@end
40 changes: 40 additions & 0 deletions lib/ios/PathTransition.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#import "PathTransition.h"

@implementation PathTransition

- (instancetype)initWithView:(UIView *)view
fromPath:(CGRect)fromPath
toPath:(CGRect)toPath
fromCornerRadius:(CGFloat)fromCornerRadius
toCornerRadius:(CGFloat)toCornerRadius
startDelay:(NSTimeInterval)startDelay
duration:(NSTimeInterval)duration
interpolator:(id<Interpolator>)interpolator {
self = [super initWithView:view
from:fromPath
to:toPath
startDelay:startDelay
duration:duration
interpolator:interpolator];
self.fromCornerRadius = fromCornerRadius;
self.toCornerRadius = toCornerRadius;

return self;
}

- (CATransform3D)animateWithProgress:(CGFloat)p {
CGRect toPath = [RNNInterpolator fromRect:self.from
toRect:self.to
precent:p
interpolator:self.interpolator];
CGFloat toRadius = [RNNInterpolator fromFloat:self.fromCornerRadius
toFloat:self.toCornerRadius
precent:p
interpolator:self.interpolator];
CAShapeLayer *mask = [CAShapeLayer new];
mask.path = [UIBezierPath bezierPathWithRoundedRect:toPath cornerRadius:toRadius].CGPath;
self.view.layer.mask = mask;
return CATransform3DIdentity;
}

@end
5 changes: 5 additions & 0 deletions lib/ios/RNNInterpolator.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@

@interface RNNInterpolator : NSObject

+ (CGPoint)fromPoint:(CGPoint)from
toPoint:(CGPoint)to
precent:(CGFloat)p
interpolator:(id<Interpolator>)interpolator;

+ (UIColor *)fromColor:(UIColor *)fromColor toColor:(UIColor *)toColor precent:(CGFloat)precent;

+ (CGFloat)fromFloat:(CGFloat)from
Expand Down
8 changes: 8 additions & 0 deletions lib/ios/RNNInterpolator.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

@implementation RNNInterpolator

+ (CGPoint)fromPoint:(CGPoint)from
toPoint:(CGPoint)to
precent:(CGFloat)p
interpolator:(id<Interpolator>)interpolator {
return CGPointMake(RNNInterpolate(from.x, to.x, p, interpolator),
RNNInterpolate(from.y, to.y, p, interpolator));
}

+ (UIColor *)fromColor:(UIColor *)fromColor toColor:(UIColor *)toColor precent:(CGFloat)precent {
return [fromColor ?: UIColor.clearColor
interpolateToValue:toColor ?: UIColor.clearColor
Expand Down
4 changes: 1 addition & 3 deletions lib/ios/RNNScreenTransitionsCreator.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
containerView:(UIView *)containerView
contentTransition:(RNNEnterExitAnimation *)contentTransitionOptions
elementTransitions:
(NSArray<ElementTransitionOptions *> *)elementTransitionsOptions
sharedElementTransitions:
(NSArray<SharedElementTransitionOptions *> *)sharedElementTransitionsOptions;
(NSArray<ElementTransitionOptions *> *)elementTransitionsOptions;

@end
Loading

0 comments on commit e184102

Please sign in to comment.