diff --git a/CHANGELOG.md b/CHANGELOG.md index 96e9769fbe25e..a7dbb5bb79bfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Package-specific changes not released in any SDK will be added here just before ### 📚 3rd party library updates - Updated `react-native-reanimated` from `1.7.0` to `1.9.0`. ([#8424](https://github.com/expo/expo/pull/8424) by [@sjchmiela](https://github.com/sjchmiela)) +- Updated `react-native-shared-element` from `0.5.6` to `0.7.0`. ([#8427](https://github.com/expo/expo/pull/8427) by [@IjzerenHein](https://github.com/IjzerenHein)) ### 🛠 Breaking changes diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementContent.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementContent.java index f93006f63156d..c7e10d80f605c 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementContent.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementContent.java @@ -10,36 +10,35 @@ import com.facebook.drawee.interfaces.DraweeController; class RNSharedElementContent { - View view; - RectF size; + View view; + RectF size; - static RectF getSize(View view) { - if (view instanceof GenericDraweeView) { - GenericDraweeView imageView = (GenericDraweeView) view; - DraweeController controller = imageView.getController(); - GenericDraweeHierarchy hierarchy = imageView.getHierarchy(); - String controllerDetails = controller.toString(); - if (controllerDetails.contains("fetchedImage=0,")) { - return null; - } - Drawable drawable = imageView.getDrawable(); - RectF imageBounds = new RectF(); - hierarchy.getActualImageBounds(imageBounds); - return imageBounds; - } - else if (view instanceof ImageView) { - ImageView imageView = (ImageView) view; - Drawable drawable = imageView.getDrawable(); - if (drawable == null) return null; - int width = drawable.getIntrinsicWidth(); - int height = drawable.getIntrinsicHeight(); - if ((width <= 0) || (height <= 0)) { - return null; - } - return new RectF(0, 0, width, height); - } - return new RectF(0, 0, view.getWidth(), view.getHeight()); + static RectF getSize(View view) { + if (view instanceof GenericDraweeView) { + GenericDraweeView imageView = (GenericDraweeView) view; + DraweeController controller = imageView.getController(); + GenericDraweeHierarchy hierarchy = imageView.getHierarchy(); + String controllerDetails = controller.toString(); + if (controllerDetails.contains("fetchedImage=0,")) { + return null; + } + Drawable drawable = imageView.getDrawable(); + RectF imageBounds = new RectF(); + hierarchy.getActualImageBounds(imageBounds); + return imageBounds; + } else if (view instanceof ImageView) { + ImageView imageView = (ImageView) view; + Drawable drawable = imageView.getDrawable(); + if (drawable == null) return null; + int width = drawable.getIntrinsicWidth(); + int height = drawable.getIntrinsicHeight(); + if ((width <= 0) || (height <= 0)) { + return null; + } + return new RectF(0, 0, width, height); } + return new RectF(0, 0, view.getWidth(), view.getHeight()); + } /* static public Rect getLayout(Rect layout, RectF contentSize, ScaleType scaleType, boolean reverse) { diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementDrawable.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementDrawable.java index 483d922a6f62f..9d08f5fe65c4b 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementDrawable.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementDrawable.java @@ -31,8 +31,14 @@ enum ViewType { GENERIC("generic"); private final String value; - ViewType(final String newValue) { value = newValue;} - public String getValue() { return value; } + + ViewType(final String newValue) { + value = newValue; + } + + public String getValue() { + return value; + } } static private String LOG_TAG = "RNSharedElementDrawable"; @@ -58,10 +64,10 @@ float getPosition() { } ViewType update( - RNSharedElementContent content, - RNSharedElementStyle style, - float position - ) { + RNSharedElementContent content, + RNSharedElementStyle style, + float position + ) { boolean invalidated = false; // Update content @@ -83,9 +89,9 @@ ViewType update( case REACTIMAGEVIEW: case IMAGEVIEW: if ((mStyle.compare(style) & - (RNSharedElementStyle.PROP_BORDER - | RNSharedElementStyle.PROP_BACKGROUND_COLOR - | RNSharedElementStyle.PROP_SCALETYPE)) != 0) { + (RNSharedElementStyle.PROP_BORDER + | RNSharedElementStyle.PROP_BACKGROUND_COLOR + | RNSharedElementStyle.PROP_SCALETYPE)) != 0) { //Log.d(LOG_TAG, "drawableChanged, viewType: " + viewType + ", changes: " + mStyle.compare(style)); invalidated = true; } else { @@ -94,8 +100,8 @@ ViewType update( break; case PLAIN: if ((mStyle.compare(style) & - (RNSharedElementStyle.PROP_BORDER - | RNSharedElementStyle.PROP_BACKGROUND_COLOR)) != 0) { + (RNSharedElementStyle.PROP_BORDER + | RNSharedElementStyle.PROP_BACKGROUND_COLOR)) != 0) { //Log.d(LOG_TAG, "drawableChanged, viewType: " + viewType + ", changes: " + mStyle.compare(style)); invalidated = true; } else { @@ -156,9 +162,9 @@ public void getOutline(Outline outline) { return; } if ((mStyle.borderTopLeftRadius == 0) && - (mStyle.borderTopRightRadius == 0) && - (mStyle.borderBottomLeftRadius == 0) && - (mStyle.borderBottomRightRadius == 0)) { + (mStyle.borderTopRightRadius == 0) && + (mStyle.borderBottomLeftRadius == 0) && + (mStyle.borderBottomRightRadius == 0)) { outline.setRect(getBounds()); return; } @@ -170,18 +176,18 @@ public void getOutline(Outline outline) { } float extraRadiusForOutline = mStyle.borderWidth / 2f; mPathForBorderRadiusOutline.addRoundRect( - new RectF(getBounds()), - new float[]{ - mStyle.borderTopLeftRadius + extraRadiusForOutline, - mStyle.borderTopLeftRadius + extraRadiusForOutline, - mStyle.borderTopRightRadius + extraRadiusForOutline, - mStyle.borderTopRightRadius + extraRadiusForOutline, - mStyle.borderBottomRightRadius + extraRadiusForOutline, - mStyle.borderBottomRightRadius + extraRadiusForOutline, - mStyle.borderBottomLeftRadius + extraRadiusForOutline, - mStyle.borderBottomLeftRadius + extraRadiusForOutline - }, - Path.Direction.CW + new RectF(getBounds()), + new float[]{ + mStyle.borderTopLeftRadius + extraRadiusForOutline, + mStyle.borderTopLeftRadius + extraRadiusForOutline, + mStyle.borderTopRightRadius + extraRadiusForOutline, + mStyle.borderTopRightRadius + extraRadiusForOutline, + mStyle.borderBottomRightRadius + extraRadiusForOutline, + mStyle.borderBottomRightRadius + extraRadiusForOutline, + mStyle.borderBottomLeftRadius + extraRadiusForOutline, + mStyle.borderBottomLeftRadius + extraRadiusForOutline + }, + Path.Direction.CW ); outline.setConvexPath(mPathForBorderRadiusOutline); } @@ -249,10 +255,10 @@ private void drawReactImageView(Canvas canvas) { roundingParams.setBorderWidth(style.borderWidth); roundingParams.setRoundingMethod(RoundingParams.RoundingMethod.BITMAP_ONLY); roundingParams.setCornersRadii( - style.borderTopLeftRadius, - style.borderTopRightRadius, - style.borderBottomRightRadius, - style.borderBottomLeftRadius + style.borderTopLeftRadius, + style.borderTopRightRadius, + style.borderBottomRightRadius, + style.borderBottomLeftRadius ); hierarchy.setRoundingParams(roundingParams); hierarchy.setBackgroundImage(null); diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementModule.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementModule.java index e7b719a6b82ce..d67869b815df3 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementModule.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementModule.java @@ -12,38 +12,38 @@ @ReactModule(name = RNSharedElementModule.MODULE_NAME) public class RNSharedElementModule extends ReactContextBaseJavaModule { - public static final String MODULE_NAME = "RNSharedElementTransition"; - static String LOG_TAG = "RNSharedElementModule"; - - private RNSharedElementNodeManager mNodeManager; - - public RNSharedElementModule(ReactApplicationContext reactContext) { - super(reactContext); - mNodeManager = new RNSharedElementNodeManager(reactContext); - } - - @Override - public String getName() { - return MODULE_NAME; - } - - RNSharedElementNodeManager getNodeManager() { - return mNodeManager; - } - - @ReactMethod - public void configure(final ReadableMap config, final Promise promise) { - - // Store a reference to the native view manager in the node-manager. - // This is done so that we can efficiently resolve a view when the - // start- and end props are set on the Transition view. - final ReactApplicationContext context = getReactApplicationContext(); - final UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class); - uiManager.prependUIBlock(new UIBlock() { - @Override - public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { - mNodeManager.setNativeViewHierarchyManager(nativeViewHierarchyManager); - } - }); - } + public static final String MODULE_NAME = "RNSharedElementTransition"; + static String LOG_TAG = "RNSharedElementModule"; + + private RNSharedElementNodeManager mNodeManager; + + public RNSharedElementModule(ReactApplicationContext reactContext) { + super(reactContext); + mNodeManager = new RNSharedElementNodeManager(reactContext); + } + + @Override + public String getName() { + return MODULE_NAME; + } + + RNSharedElementNodeManager getNodeManager() { + return mNodeManager; + } + + @ReactMethod + public void configure(final ReadableMap config, final Promise promise) { + + // Store a reference to the native view manager in the node-manager. + // This is done so that we can efficiently resolve a view when the + // start- and end props are set on the Transition view. + final ReactApplicationContext context = getReactApplicationContext(); + final UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class); + uiManager.prependUIBlock(new UIBlock() { + @Override + public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { + mNodeManager.setNativeViewHierarchyManager(nativeViewHierarchyManager); + } + }); + } } \ No newline at end of file diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementNode.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementNode.java index ad04d47a9fc14..90a9ab22b5a6d 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementNode.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementNode.java @@ -1,6 +1,7 @@ package versioned.host.exp.exponent.modules.api.components.sharedelement; import java.util.ArrayList; + import javax.annotation.Nullable; import android.os.Handler; @@ -24,348 +25,339 @@ import com.facebook.drawee.backends.pipeline.PipelineDraweeController; abstract class RetryRunnable implements Runnable { - int numRetries = 0; + int numRetries = 0; } class RNSharedElementNode { - static private String LOG_TAG = "RNSharedElementNode"; - - private Context mContext; - private int mReactTag; - private View mView; - private View mAncestorView; - private boolean mIsParent; - private ReadableMap mStyleConfig; - private RNSharedElementStyle mResolveStyle; - private View mResolvedView; - private int mRefCount; - private int mHideRefCount; - private float mHideAlpha; - private RNSharedElementStyle mStyleCache; - private ArrayList mStyleCallbacks; - private RNSharedElementContent mContentCache; - private ArrayList mContentCallbacks; - private BaseControllerListener mDraweeControllerListener; - private Handler mRetryHandler; - - RNSharedElementNode(Context context, int reactTag, View view, boolean isParent, View ancestorView, ReadableMap styleConfig) { - mReactTag = reactTag; - mView = view; - mAncestorView = ancestorView; - mIsParent = isParent; - mStyleConfig = styleConfig; - mResolveStyle = new RNSharedElementStyle(styleConfig, context); - mContext = context; - mRefCount = 1; - mHideRefCount = 0; - mHideAlpha = 1; - mStyleCache = null; - mStyleCallbacks = null; - mContentCache = null; - mContentCallbacks = null; - mResolvedView = null; - mDraweeControllerListener = null; - mRetryHandler = null; - } - - int getReactTag() { - return mReactTag; - } - - int addRef() { - return ++mRefCount; + static private String LOG_TAG = "RNSharedElementNode"; + + private Context mContext; + private int mReactTag; + private View mView; + private View mAncestorView; + private boolean mIsParent; + private ReadableMap mStyleConfig; + private RNSharedElementStyle mResolveStyle; + private View mResolvedView; + private int mRefCount; + private int mHideRefCount; + private float mHideAlpha; + private RNSharedElementStyle mStyleCache; + private ArrayList mStyleCallbacks; + private RNSharedElementContent mContentCache; + private ArrayList mContentCallbacks; + private BaseControllerListener mDraweeControllerListener; + private Handler mRetryHandler; + + RNSharedElementNode(Context context, int reactTag, View view, boolean isParent, View ancestorView, ReadableMap styleConfig) { + mReactTag = reactTag; + mView = view; + mAncestorView = ancestorView; + mIsParent = isParent; + mStyleConfig = styleConfig; + mResolveStyle = new RNSharedElementStyle(styleConfig, context); + mContext = context; + mRefCount = 1; + mHideRefCount = 0; + mHideAlpha = 1; + mStyleCache = null; + mStyleCallbacks = null; + mContentCache = null; + mContentCallbacks = null; + mResolvedView = null; + mDraweeControllerListener = null; + mRetryHandler = null; + } + + int getReactTag() { + return mReactTag; + } + + int addRef() { + return ++mRefCount; + } + + int releaseRef() { + if (--mRefCount == 0) { + removeDraweeControllerListener(mResolvedView); + stopRetryLoop(); + mView = null; + mAncestorView = null; + mStyleConfig = null; + mResolvedView = null; + mContentCache = null; + mContentCallbacks = null; + mStyleCache = null; + mStyleCallbacks = null; } - - int releaseRef() { - if (--mRefCount == 0) { - removeDraweeControllerListener(mResolvedView); - stopRetryLoop(); - mView = null; - mAncestorView = null; - mStyleConfig = null; - mResolvedView = null; - mContentCache = null; - mContentCallbacks = null; - mStyleCache = null; - mStyleCallbacks = null; - } - return mRefCount; - } - - void addHideRef() { - mHideRefCount++; - if (mHideRefCount == 1) { - mHideAlpha = mView.getAlpha(); - mView.setAlpha(0); - } + return mRefCount; + } + + void addHideRef() { + mHideRefCount++; + if (mHideRefCount == 1) { + mHideAlpha = mView.getAlpha(); + mView.setAlpha(0); } + } - void releaseHideRef() { - mHideRefCount--; - if (mHideRefCount == 0) { - mView.setAlpha(mHideAlpha); - } + void releaseHideRef() { + mHideRefCount--; + if (mHideRefCount == 0) { + mView.setAlpha(mHideAlpha); } - - private static View resolveView(View view, RNSharedElementStyle style) { - if (view == null) return null; - - // If the view is a ViewGroup and it contains exactly one - // imageview with the same size, then use that imageview - if (view instanceof ViewGroup) { - ViewGroup viewGroup = (ViewGroup) view; - if (viewGroup.getChildCount() == 1) { - View childView = viewGroup.getChildAt(0); - if (childView instanceof ImageView) { - int left = childView.getLeft(); - int top = childView.getTop(); - int width = childView.getWidth(); - int height = childView.getHeight(); - int expectedLeft = Math.round(style.borderWidth); - int expectedTop = Math.round(style.borderWidth); - int expectedWidth = Math.round((float)viewGroup.getWidth() - (style.borderWidth * 2)); - int expectedHeight = Math.round((float)viewGroup.getHeight() - (style.borderWidth * 2)); - if (((left >= expectedLeft - 1) && (left <= expectedLeft + 1)) && - ((top >= expectedTop - 1) && (top <= expectedTop + 1)) && - ((width >= expectedWidth - 1) && (width <= expectedWidth + 1)) && - ((height >= expectedHeight - 1) && (height <= expectedHeight + 1))) { - return childView; - } - } - } + } + + private static View resolveView(View view, RNSharedElementStyle style) { + if (view == null) return null; + + // If the view is a ViewGroup and it contains exactly one + // imageview with the same size, then use that imageview + if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + if (viewGroup.getChildCount() == 1) { + View childView = viewGroup.getChildAt(0); + if (childView instanceof ImageView) { + int left = childView.getLeft(); + int top = childView.getTop(); + int width = childView.getWidth(); + int height = childView.getHeight(); + int expectedLeft = Math.round(style.borderWidth); + int expectedTop = Math.round(style.borderWidth); + int expectedWidth = Math.round((float) viewGroup.getWidth() - (style.borderWidth * 2)); + int expectedHeight = Math.round((float) viewGroup.getHeight() - (style.borderWidth * 2)); + if (((left >= expectedLeft - 1) && (left <= expectedLeft + 1)) && + ((top >= expectedTop - 1) && (top <= expectedTop + 1)) && + ((width >= expectedWidth - 1) && (width <= expectedWidth + 1)) && + ((height >= expectedHeight - 1) && (height <= expectedHeight + 1))) { + return childView; + } } - return view; + } } - - View getAncestorView() { - return mAncestorView; + return view; + } + + View getAncestorView() { + return mAncestorView; + } + + View getResolvedView() { + if (mResolvedView != null) return mResolvedView; + View view = mView; + if (mIsParent) { + int childCount = ((ViewGroup) mView).getChildCount(); + if (childCount == 1) { + view = ((ViewGroup) mView).getChildAt(0); + } else if (childCount <= 0) { + Log.d(LOG_TAG, "Child for parent doesnt exist"); + return null; + } } - - View getResolvedView() { - if (mResolvedView != null) return mResolvedView; - View view = mView; - if (mIsParent) { - int childCount = ((ViewGroup)mView).getChildCount(); - if (childCount == 1) { - view = ((ViewGroup)mView).getChildAt(0); - } else if (childCount <= 0) { - Log.d(LOG_TAG, "Child for parent doesnt exist"); - return null; - } - } - mResolvedView = RNSharedElementNode.resolveView(view, mResolveStyle); - return mResolvedView; + mResolvedView = RNSharedElementNode.resolveView(view, mResolveStyle); + return mResolvedView; + } + + void requestStyle(Callback callback) { + if (mStyleCache != null) { + callback.invoke(mStyleCache, this); + return; } - - void requestStyle(Callback callback) { - if (mStyleCache != null) { - callback.invoke(mStyleCache, this); - return; - } - if (mStyleCallbacks == null) mStyleCallbacks = new ArrayList(); - mStyleCallbacks.add(callback); - if (!fetchInitialStyle()) { - startRetryLoop(); - } + if (mStyleCallbacks == null) mStyleCallbacks = new ArrayList(); + mStyleCallbacks.add(callback); + if (!fetchInitialStyle()) { + startRetryLoop(); } - - private boolean fetchInitialStyle() { - View view = getResolvedView(); - if (view == null) return false; - if (mStyleCallbacks == null) return true; - - // Get relative size and position within parent - int left = view.getLeft(); - int top = view.getTop(); - int width = view.getWidth(); - int height = view.getHeight(); - if (width == 0 && height == 0) return false; - Matrix transform = RNSharedElementStyle.getAbsoluteViewTransform(view, true); - Matrix ancestorTransform = RNSharedElementStyle.getAbsoluteViewTransform(mAncestorView, true); - if ((transform == null) || (ancestorTransform == null)) return false; - Rect frame = new Rect(left, top, left + width, top + height); - - // Get absolute position on screen (left/top) - int[] location = new int[2]; - view.getLocationOnScreen(location); - - // Calculate the optional translation that was performed on the ancestor. - // This corrects for any scene translation that was performed by the navigator. - // E.g. when the incoming scene starts to the right and moves to the left - // to enter the screen - float[] f = new float[9]; - ancestorTransform.getValues(f); - int ancestorTranslateX = (int) f[Matrix.MTRANS_X]; - int ancestorTranslateY = (int) f[Matrix.MTRANS_Y]; - left = location[0] - ancestorTranslateX; - top = location[1] - ancestorTranslateY; - - // In case the view has a scale transform applied, the calculate - // the correct visual width & height of the view - transform.getValues(f); - float scaleX = f[Matrix.MSCALE_X]; - float scaleY = f[Matrix.MSCALE_Y]; - width = (int) ((float)width * scaleX); - height = (int) ((float)height * scaleY); - - // Create absolute layout rect - Rect layout = new Rect(left, top, left + width, top + height); - - // Create style - RNSharedElementStyle style = new RNSharedElementStyle(mStyleConfig, mContext); - style.layout = layout; - style.frame = frame; - style.transform = transform; - style.ancestorTransform = ancestorTransform; - - // Get opacity - style.opacity = view.getAlpha(); - - // Get elevation + } + + private boolean fetchInitialStyle() { + View view = getResolvedView(); + if (view == null) return false; + if (mStyleCallbacks == null) return true; + + // Get relative size and position within parent + int left = view.getLeft(); + int top = view.getTop(); + int width = view.getWidth(); + int height = view.getHeight(); + if (width == 0 && height == 0) return false; + Matrix transform = RNSharedElementStyle.getAbsoluteViewTransform(view, true); + Matrix ancestorTransform = RNSharedElementStyle.getAbsoluteViewTransform(mAncestorView, true); + if ((transform == null) || (ancestorTransform == null)) return false; + Rect frame = new Rect(left, top, left + width, top + height); + + // Get absolute position on screen (left/top) + int[] location = new int[2]; + view.getLocationOnScreen(location); + left = location[0]; + top = location[1]; + + // In case the view has a scale transform applied, the calculate + // the correct visual width & height of the view + float[] f = new float[9]; + transform.getValues(f); + float scaleX = f[Matrix.MSCALE_X]; + float scaleY = f[Matrix.MSCALE_Y]; + width = (int) ((float) width * scaleX); + height = (int) ((float) height * scaleY); + + // Create absolute layout rect + Rect layout = new Rect(left, top, left + width, top + height); + + // Create style + RNSharedElementStyle style = new RNSharedElementStyle(mStyleConfig, mContext); + style.layout = layout; + style.frame = frame; + style.transform = transform; + style.ancestorTransform = ancestorTransform; + + // Get opacity + style.opacity = view.getAlpha(); + + // Get elevation /*if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { style.elevation = view.getElevation(); }*/ - // Update initial style cache - mStyleCache = style; + // Update initial style cache + mStyleCache = style; - //Log.d(LOG_TAG, "Style fetched: " + style); + //Log.d(LOG_TAG, "Style fetched: " + style); - // Notify callbacks - ArrayList callbacks = mStyleCallbacks; - mStyleCallbacks = null; - for (Callback callback : callbacks) { - callback.invoke(style, this); - } - return true; + // Notify callbacks + ArrayList callbacks = mStyleCallbacks; + mStyleCallbacks = null; + for (Callback callback : callbacks) { + callback.invoke(style, this); } + return true; + } - void requestContent(Callback callback) { - if (mContentCache != null) { - callback.invoke(mContentCache, this); - return; - } - if (mContentCallbacks == null) mContentCallbacks = new ArrayList(); - mContentCallbacks.add(callback); - if (!fetchInitialContent()) { - startRetryLoop(); - } + void requestContent(Callback callback) { + if (mContentCache != null) { + callback.invoke(mContentCache, this); + return; + } + if (mContentCallbacks == null) mContentCallbacks = new ArrayList(); + mContentCallbacks.add(callback); + if (!fetchInitialContent()) { + startRetryLoop(); + } + } + + private boolean fetchInitialContent() { + View view = getResolvedView(); + if (view == null) return false; + if (mContentCallbacks == null) return true; + + // Verify view size + int width = view.getWidth(); + int height = view.getHeight(); + if (width == 0 && height == 0) return false; + + // Get content size (e.g. the size of the underlying image of an image-view) + RectF contentSize = RNSharedElementContent.getSize(view); + if (contentSize == null) { + // Image has not yet been fetched, listen for it + addDraweeControllerListener(view); + return false; } - private boolean fetchInitialContent() { - View view = getResolvedView(); - if (view == null) return false; - if (mContentCallbacks == null) return true; - - // Verify view size - int width = view.getWidth(); - int height = view.getHeight(); - if (width == 0 && height == 0) return false; - - // Get content size (e.g. the size of the underlying image of an image-view) - RectF contentSize = RNSharedElementContent.getSize(view); - if (contentSize == null) { - // Image has not yet been fetched, listen for it - addDraweeControllerListener(view); - return false; - } + // Create content + RNSharedElementContent content = new RNSharedElementContent(); + content.view = view; + content.size = contentSize; - // Create content - RNSharedElementContent content = new RNSharedElementContent(); - content.view = view; - content.size = contentSize; - - // Update cache - mContentCache = content; - - // Log.d(LOG_TAG, "Content fetched: " + content); - - // Notify callbacks - ArrayList callbacks = mContentCallbacks; - mContentCallbacks = null; - for (Callback callback : callbacks) { - callback.invoke(content, this); - } - return true; - } + // Update cache + mContentCache = content; - private void startRetryLoop() { - if (mRetryHandler != null) return; - - //Log.d(LOG_TAG, "Starting retry loop..."); - - mRetryHandler = new Handler(); - final long startTime = System.nanoTime(); - mRetryHandler.postDelayed(new RetryRunnable() { - - @Override - public void run() { - if (mRetryHandler == null) return; - final RetryRunnable runnable = this; - - runnable.numRetries++; - //Log.d(LOG_TAG, "Retry loop #" + runnable.numRetries + " ..."); - boolean isContentFetched = fetchInitialContent(); - boolean isStyleFetched = fetchInitialStyle(); - if (!isContentFetched || !isStyleFetched) { - mRetryHandler.postDelayed(runnable, 8); - } - else { - //Log.d(LOG_TAG, "Style/content fetch completed after #" + runnable.numRetries + " ..."); - mRetryHandler = null; - } - } - }, 4); - } + // Log.d(LOG_TAG, "Content fetched: " + content); - private void stopRetryLoop() { - if (mRetryHandler != null) { - //Log.d(LOG_TAG, "Stopping retry loop..."); - mRetryHandler = null; + // Notify callbacks + ArrayList callbacks = mContentCallbacks; + mContentCallbacks = null; + for (Callback callback : callbacks) { + callback.invoke(content, this); + } + return true; + } + + private void startRetryLoop() { + if (mRetryHandler != null) return; + + //Log.d(LOG_TAG, "Starting retry loop..."); + + mRetryHandler = new Handler(); + final long startTime = System.nanoTime(); + mRetryHandler.postDelayed(new RetryRunnable() { + + @Override + public void run() { + if (mRetryHandler == null) return; + final RetryRunnable runnable = this; + + runnable.numRetries++; + //Log.d(LOG_TAG, "Retry loop #" + runnable.numRetries + " ..."); + boolean isContentFetched = fetchInitialContent(); + boolean isStyleFetched = fetchInitialStyle(); + if (!isContentFetched || !isStyleFetched) { + mRetryHandler.postDelayed(runnable, 8); + } else { + //Log.d(LOG_TAG, "Style/content fetch completed after #" + runnable.numRetries + " ..."); + mRetryHandler = null; } + } + }, 4); + } + + private void stopRetryLoop() { + if (mRetryHandler != null) { + //Log.d(LOG_TAG, "Stopping retry loop..."); + mRetryHandler = null; } + } + + private void addDraweeControllerListener(final View view) { + if (mDraweeControllerListener != null) return; + + if (!(view instanceof GenericDraweeView)) return; + GenericDraweeView imageView = (GenericDraweeView) view; + DraweeController controller = imageView.getController(); + if (!(controller instanceof PipelineDraweeController)) return; + PipelineDraweeController pipelineController = (PipelineDraweeController) controller; - private void addDraweeControllerListener(final View view) { - if (mDraweeControllerListener != null) return; - - if (!(view instanceof GenericDraweeView)) return; - GenericDraweeView imageView = (GenericDraweeView) view; - DraweeController controller = imageView.getController(); - if (!(controller instanceof PipelineDraweeController)) return; - PipelineDraweeController pipelineController = (PipelineDraweeController) controller; - - mDraweeControllerListener = new BaseControllerListener() { - @Override - public void onSubmit(String id, Object callerContext) { - //Log.d(LOG_TAG, "mDraweeControllerListener.onSubmit: " + id + ", callerContext: " + callerContext); - } - - @Override - public void onFinalImageSet( + mDraweeControllerListener = new BaseControllerListener() { + @Override + public void onSubmit(String id, Object callerContext) { + //Log.d(LOG_TAG, "mDraweeControllerListener.onSubmit: " + id + ", callerContext: " + callerContext); + } + + @Override + public void onFinalImageSet( String id, @Nullable final ImageInfo imageInfo, @Nullable Animatable animatable) { - //Log.d(LOG_TAG, "mDraweeControllerListener.onFinalImageSet: " + id + ", imageInfo: " + imageInfo); - removeDraweeControllerListener(view); - fetchInitialContent(); - } - - @Override - public void onFailure(String id, Throwable throwable) { - Log.d(LOG_TAG, "mDraweeControllerListener.onFailure: " + id + ", throwable: " + throwable); - } - }; - - pipelineController.addControllerListener(mDraweeControllerListener); - } - - private void removeDraweeControllerListener(final View view) { - if (mDraweeControllerListener == null) return; - GenericDraweeView imageView = (GenericDraweeView) view; - DraweeController controller = imageView.getController(); - if (!(controller instanceof PipelineDraweeController)) return; - PipelineDraweeController pipelineController = (PipelineDraweeController) controller; - pipelineController.removeControllerListener(mDraweeControllerListener); - mDraweeControllerListener = null; - } + //Log.d(LOG_TAG, "mDraweeControllerListener.onFinalImageSet: " + id + ", imageInfo: " + imageInfo); + removeDraweeControllerListener(view); + fetchInitialContent(); + } + + @Override + public void onFailure(String id, Throwable throwable) { + Log.d(LOG_TAG, "mDraweeControllerListener.onFailure: " + id + ", throwable: " + throwable); + } + }; + + pipelineController.addControllerListener(mDraweeControllerListener); + } + + private void removeDraweeControllerListener(final View view) { + if (mDraweeControllerListener == null) return; + GenericDraweeView imageView = (GenericDraweeView) view; + DraweeController controller = imageView.getController(); + if (!(controller instanceof PipelineDraweeController)) return; + PipelineDraweeController pipelineController = (PipelineDraweeController) controller; + pipelineController.removeControllerListener(mDraweeControllerListener); + mDraweeControllerListener = null; + } } diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementNodeManager.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementNodeManager.java index 0240b29633626..594048fca2edc 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementNodeManager.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementNodeManager.java @@ -10,42 +10,42 @@ import com.facebook.react.uimanager.NativeViewHierarchyManager; class RNSharedElementNodeManager { - private Map mNodes = new HashMap(); - private NativeViewHierarchyManager mNativeViewHierarchyManager; - private Context mContext; - - RNSharedElementNodeManager(Context context) { - mContext = context; - } - - void setNativeViewHierarchyManager(NativeViewHierarchyManager nativeViewHierarchyManager) { - mNativeViewHierarchyManager = nativeViewHierarchyManager; - } - - NativeViewHierarchyManager getNativeViewHierarchyManager() { - return mNativeViewHierarchyManager; - } - - RNSharedElementNode acquire(int reactTag, View view, boolean isParent, View ancestor, ReadableMap styleConfig) { - synchronized (mNodes) { - RNSharedElementNode node = mNodes.get(reactTag); - if (node != null) { - node.addRef(); - return node; - } - node = new RNSharedElementNode(mContext, reactTag, view, isParent, ancestor, styleConfig); - mNodes.put(reactTag, node); - return node; - } + private Map mNodes = new HashMap(); + private NativeViewHierarchyManager mNativeViewHierarchyManager; + private Context mContext; + + RNSharedElementNodeManager(Context context) { + mContext = context; + } + + void setNativeViewHierarchyManager(NativeViewHierarchyManager nativeViewHierarchyManager) { + mNativeViewHierarchyManager = nativeViewHierarchyManager; + } + + NativeViewHierarchyManager getNativeViewHierarchyManager() { + return mNativeViewHierarchyManager; + } + + RNSharedElementNode acquire(int reactTag, View view, boolean isParent, View ancestor, ReadableMap styleConfig) { + synchronized (mNodes) { + RNSharedElementNode node = mNodes.get(reactTag); + if (node != null) { + node.addRef(); + return node; + } + node = new RNSharedElementNode(mContext, reactTag, view, isParent, ancestor, styleConfig); + mNodes.put(reactTag, node); + return node; } - - int release(RNSharedElementNode node) { - synchronized (mNodes) { - int refCount = node.releaseRef(); - if (refCount == 0) { - mNodes.remove(node.getReactTag()); - } - return refCount; - } + } + + int release(RNSharedElementNode node) { + synchronized (mNodes) { + int refCount = node.releaseRef(); + if (refCount == 0) { + mNodes.remove(node.getReactTag()); + } + return refCount; } + } } \ No newline at end of file diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementPackage.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementPackage.java index 62cd14adbafb2..2f0d8946dc6cb 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementPackage.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementPackage.java @@ -9,13 +9,13 @@ public class RNSharedElementPackage implements ReactPackage { - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - return Arrays.asList(new RNSharedElementModule(reactContext)); - } + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + return Arrays.asList(new RNSharedElementModule(reactContext)); + } - @Override - public List createViewManagers(ReactApplicationContext reactContext) { - return Arrays.asList(new RNSharedElementTransitionManager(reactContext)); - } + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Arrays.asList(new RNSharedElementTransitionManager(reactContext)); + } } diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementStyle.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementStyle.java index 4d74539c983ea..d146ca076445c 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementStyle.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementStyle.java @@ -19,218 +19,260 @@ import com.facebook.react.modules.i18nmanager.I18nUtil; public class RNSharedElementStyle { - static private String LOG_TAG = "RNSharedElementStyle"; - - static int PROP_OPACITY = 1 << 0; - static int PROP_ELEVATION = 1 << 1; - static int PROP_BACKGROUND_COLOR = 1 << 2; - static int PROP_BORDER_COLOR = 1 << 3; - static int PROP_BORDER_WIDTH = 1 << 4; - static int PROP_BORDER_STYLE = 1 << 5; - static int PROP_BORDER_TOPLEFTRADIUS = 1 << 6; - static int PROP_BORDER_TOPRIGHTRADIUS = 1 << 7; - static int PROP_BORDER_BOTTOMLEFTRADIUS = 1 << 8; - static int PROP_BORDER_BOTTOMRIGHT_RADIUS = 1 << 9; - static int PROP_BORDER = - PROP_BORDER_COLOR + - PROP_BORDER_WIDTH + - PROP_BORDER_STYLE + - PROP_BORDER_TOPLEFTRADIUS + - PROP_BORDER_TOPRIGHTRADIUS + - PROP_BORDER_BOTTOMLEFTRADIUS + - PROP_BORDER_BOTTOMRIGHT_RADIUS; - static int PROP_SCALETYPE = 1 << 10; - - Rect layout = new Rect(); // absolute layout on screen - Rect frame = new Rect(); // frame rect relative to parent - Matrix transform = new Matrix(); - Matrix ancestorTransform = new Matrix(); - ScaleType scaleType = ScaleType.FIT_XY; - int backgroundColor = Color.TRANSPARENT; - float opacity = 1; - float borderTopLeftRadius = 0; - float borderTopRightRadius = 0; - float borderBottomLeftRadius = 0; - float borderBottomRightRadius = 0; - float borderWidth = 0; - int borderColor = Color.TRANSPARENT; - String borderStyle = "solid"; - float elevation = 0; - - RNSharedElementStyle() { - // nop - } + static private String LOG_TAG = "RNSharedElementStyle"; - RNSharedElementStyle(ReadableMap config, Context context) { - // Pre-fill the style with the style-config - if (config.hasKey("opacity")) opacity = (float) config.getDouble("opacity"); - if (config.hasKey("backgroundColor")) backgroundColor = config.getInt("backgroundColor"); - if (config.hasKey("borderColor")) borderColor = config.getInt("borderColor"); - if (config.hasKey("borderWidth")) borderWidth = PixelUtil.toPixelFromDIP((float) config.getDouble("borderWidth")); - if (config.hasKey("borderStyle")) borderStyle = config.getString("borderStyle"); - if (config.hasKey("resizeMode")) scaleType = ImageResizeMode.toScaleType(config.getString("resizeMode")); - if (config.hasKey("elevation")) elevation = PixelUtil.toPixelFromDIP((float) config.getDouble("elevation")); - - // Border-radius - boolean isRTL = I18nUtil.getInstance().isRTL(context); - if (config.hasKey("borderRadius")) { - float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderRadius")); - borderTopLeftRadius = borderRadius; - borderTopRightRadius = borderRadius; - borderBottomLeftRadius = borderRadius; - borderBottomRightRadius = borderRadius; - } - if (config.hasKey("borderTopEndRadius")) { - float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderTopEndRadius")); - if (isRTL) { - borderTopLeftRadius = borderRadius; - } else { - borderTopRightRadius = borderRadius; - } - } - if (config.hasKey("borderTopStartRadius")) { - float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderTopStartRadius")); - if (isRTL) { - borderTopRightRadius = borderRadius; - } else { - borderTopLeftRadius = borderRadius; - } - } - if (config.hasKey("borderBottomEndRadius")) { - float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderBottomEndRadius")); - if (isRTL) { - borderBottomLeftRadius = borderRadius; - } else { - borderBottomRightRadius = borderRadius; - } - } - if (config.hasKey("borderBottomStartRadius")) { - float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderBottomStartRadius")); - if (isRTL) { - borderBottomRightRadius = borderRadius; - } else { - borderBottomLeftRadius = borderRadius; - } - } - if (config.hasKey("borderTopLeftRadius")) borderTopLeftRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderTopLeftRadius")); - if (config.hasKey("borderTopRightRadius")) borderTopRightRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderTopRightRadius")); - if (config.hasKey("borderBottomLeftRadius")) borderBottomLeftRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderBottomLeftRadius")); - if (config.hasKey("borderBottomRightRadius")) borderBottomRightRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderBottomRightRadius")); - } + static Rect EMPTY_RECT = new Rect(); - int compare(RNSharedElementStyle style) { - int res = 0; - if (opacity != style.opacity) res += PROP_OPACITY; - if (backgroundColor != style.backgroundColor) res += PROP_BACKGROUND_COLOR; - if (borderColor != style.borderColor) res += PROP_BORDER_COLOR; - if (borderWidth != style.borderWidth) res += PROP_BORDER_WIDTH; - if (!borderStyle.equals(style.borderStyle)) res += PROP_BORDER_STYLE; - if (borderTopLeftRadius != style.borderTopLeftRadius) res += PROP_BORDER_TOPLEFTRADIUS; - if (borderTopRightRadius != style.borderTopRightRadius) res += PROP_BORDER_TOPRIGHTRADIUS; - if (borderBottomLeftRadius != style.borderBottomLeftRadius) res += PROP_BORDER_BOTTOMLEFTRADIUS; - if (borderBottomRightRadius != style.borderBottomRightRadius) res += PROP_BORDER_BOTTOMRIGHT_RADIUS; - if (elevation != style.elevation) res += PROP_ELEVATION; - if (!RNSharedElementStyle.equalsScaleType(scaleType, style.scaleType)) res += PROP_SCALETYPE; - return res; - } + static int PROP_OPACITY = 1 << 0; + static int PROP_ELEVATION = 1 << 1; + static int PROP_BACKGROUND_COLOR = 1 << 2; + static int PROP_BORDER_COLOR = 1 << 3; + static int PROP_BORDER_WIDTH = 1 << 4; + static int PROP_BORDER_STYLE = 1 << 5; + static int PROP_BORDER_TOPLEFTRADIUS = 1 << 6; + static int PROP_BORDER_TOPRIGHTRADIUS = 1 << 7; + static int PROP_BORDER_BOTTOMLEFTRADIUS = 1 << 8; + static int PROP_BORDER_BOTTOMRIGHT_RADIUS = 1 << 9; + static int PROP_BORDER = + PROP_BORDER_COLOR + + PROP_BORDER_WIDTH + + PROP_BORDER_STYLE + + PROP_BORDER_TOPLEFTRADIUS + + PROP_BORDER_TOPRIGHTRADIUS + + PROP_BORDER_BOTTOMLEFTRADIUS + + PROP_BORDER_BOTTOMRIGHT_RADIUS; + static int PROP_SCALETYPE = 1 << 10; + + Rect layout = new Rect(); // absolute layout on screen + Rect frame = new Rect(); // frame rect relative to parent + Matrix transform = new Matrix(); + Matrix ancestorTransform = new Matrix(); + ScaleType scaleType = ScaleType.FIT_XY; + int backgroundColor = Color.TRANSPARENT; + float opacity = 1; + float borderTopLeftRadius = 0; + float borderTopRightRadius = 0; + float borderBottomLeftRadius = 0; + float borderBottomRightRadius = 0; + float borderWidth = 0; + int borderColor = Color.TRANSPARENT; + String borderStyle = "solid"; + float elevation = 0; - boolean isVisible() { - if (opacity <= 0) return false; - if (elevation > 0) return true; - return (Color.alpha(backgroundColor) > 0) || (Color.alpha(borderColor) > 0); + RNSharedElementStyle() { + // nop + } + + RNSharedElementStyle(ReadableMap config, Context context) { + // Pre-fill the style with the style-config + if (config.hasKey("opacity")) opacity = (float) config.getDouble("opacity"); + if (config.hasKey("backgroundColor")) backgroundColor = config.getInt("backgroundColor"); + if (config.hasKey("borderColor")) borderColor = config.getInt("borderColor"); + if (config.hasKey("borderWidth")) + borderWidth = PixelUtil.toPixelFromDIP((float) config.getDouble("borderWidth")); + if (config.hasKey("borderStyle")) borderStyle = config.getString("borderStyle"); + if (config.hasKey("resizeMode")) + scaleType = ImageResizeMode.toScaleType(config.getString("resizeMode")); + if (config.hasKey("elevation")) + elevation = PixelUtil.toPixelFromDIP((float) config.getDouble("elevation")); + + // Border-radius + boolean isRTL = I18nUtil.getInstance().isRTL(context); + if (config.hasKey("borderRadius")) { + float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderRadius")); + borderTopLeftRadius = borderRadius; + borderTopRightRadius = borderRadius; + borderBottomLeftRadius = borderRadius; + borderBottomRightRadius = borderRadius; + } + if (config.hasKey("borderTopEndRadius")) { + float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderTopEndRadius")); + if (isRTL) { + borderTopLeftRadius = borderRadius; + } else { + borderTopRightRadius = borderRadius; + } } + if (config.hasKey("borderTopStartRadius")) { + float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderTopStartRadius")); + if (isRTL) { + borderTopRightRadius = borderRadius; + } else { + borderTopLeftRadius = borderRadius; + } + } + if (config.hasKey("borderBottomEndRadius")) { + float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderBottomEndRadius")); + if (isRTL) { + borderBottomLeftRadius = borderRadius; + } else { + borderBottomRightRadius = borderRadius; + } + } + if (config.hasKey("borderBottomStartRadius")) { + float borderRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderBottomStartRadius")); + if (isRTL) { + borderBottomRightRadius = borderRadius; + } else { + borderBottomLeftRadius = borderRadius; + } + } + if (config.hasKey("borderTopLeftRadius")) + borderTopLeftRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderTopLeftRadius")); + if (config.hasKey("borderTopRightRadius")) + borderTopRightRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderTopRightRadius")); + if (config.hasKey("borderBottomLeftRadius")) + borderBottomLeftRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderBottomLeftRadius")); + if (config.hasKey("borderBottomRightRadius")) + borderBottomRightRadius = PixelUtil.toPixelFromDIP((float) config.getDouble("borderBottomRightRadius")); + } + + int compare(RNSharedElementStyle style) { + int res = 0; + if (opacity != style.opacity) res += PROP_OPACITY; + if (backgroundColor != style.backgroundColor) res += PROP_BACKGROUND_COLOR; + if (borderColor != style.borderColor) res += PROP_BORDER_COLOR; + if (borderWidth != style.borderWidth) res += PROP_BORDER_WIDTH; + if (!borderStyle.equals(style.borderStyle)) res += PROP_BORDER_STYLE; + if (borderTopLeftRadius != style.borderTopLeftRadius) res += PROP_BORDER_TOPLEFTRADIUS; + if (borderTopRightRadius != style.borderTopRightRadius) res += PROP_BORDER_TOPRIGHTRADIUS; + if (borderBottomLeftRadius != style.borderBottomLeftRadius) res += PROP_BORDER_BOTTOMLEFTRADIUS; + if (borderBottomRightRadius != style.borderBottomRightRadius) + res += PROP_BORDER_BOTTOMRIGHT_RADIUS; + if (elevation != style.elevation) res += PROP_ELEVATION; + if (!RNSharedElementStyle.equalsScaleType(scaleType, style.scaleType)) res += PROP_SCALETYPE; + return res; + } + + boolean isVisible() { + if (opacity <= 0) return false; + if (elevation > 0) return true; + return (Color.alpha(backgroundColor) > 0) || (Color.alpha(borderColor) > 0); + } + + static Rect normalizeLayout(RNSharedElementStyle style, RNSharedElementStyle otherStyle) { + if (style == null) return EMPTY_RECT; + return normalizeLayout(style.layout, style, otherStyle); + } - static boolean equalsScaleType(ScaleType scaleType1, ScaleType scaleType2) { - if (scaleType1 == scaleType2) return true; - return false; + static Rect normalizeLayout(Rect layout, RNSharedElementStyle style, RNSharedElementStyle otherStyle) { + if ((layout == null) || (style == null)) return EMPTY_RECT; + + // Get ancestor translation + float[] f = new float[9]; + style.ancestorTransform.getValues(f); + int ancestorTranslateX = (int) f[Matrix.MTRANS_X]; + int ancestorTranslateY = (int) f[Matrix.MTRANS_Y]; + + // Get other translation + int otherAncestorTranslateX = ancestorTranslateX; + int otherAncestorTranslateY = ancestorTranslateY; + if (otherStyle != null) { + otherStyle.ancestorTransform.getValues(f); + otherAncestorTranslateX = (int) f[Matrix.MTRANS_X]; + otherAncestorTranslateY = (int) f[Matrix.MTRANS_Y]; } - static ScaleType getInterpolatingScaleType(RNSharedElementStyle style1, RNSharedElementStyle style2, float position) { - if (style1.scaleType == style2.scaleType) return style1.scaleType; - InterpolatingScaleType scaleType = new InterpolatingScaleType( + // Calculate the optional translation that was performed on the ancestor. + // This corrects for any scene translation that was performed by the navigator. + // E.g. when the incoming scene starts to the right and moves to the left + // to enter the screen + int left = layout.left - ((ancestorTranslateX != otherAncestorTranslateX) ? ancestorTranslateX : 0); + int top = layout.top - ((ancestorTranslateY != otherAncestorTranslateY) ? ancestorTranslateY : 0); + return new Rect(left, top, left + layout.width(), top + layout.height()); + } + + static boolean equalsScaleType(ScaleType scaleType1, ScaleType scaleType2) { + if (scaleType1 == scaleType2) return true; + return false; + } + + static ScaleType getInterpolatingScaleType(RNSharedElementStyle style1, RNSharedElementStyle style2, float position) { + if (style1.scaleType == style2.scaleType) return style1.scaleType; + InterpolatingScaleType scaleType = new InterpolatingScaleType( style1.scaleType, style2.scaleType, new Rect(0, 0, style1.layout.width(), style1.layout.height()), new Rect(0, 0, style2.layout.width(), style2.layout.height()) - ); - scaleType.setValue(position); - return scaleType; - } + ); + scaleType.setValue(position); + return scaleType; + } - static RectF getInterpolatedLayout(Rect layout1, Rect layout2, float position) { - return new RectF( + static RectF getInterpolatedLayout(Rect layout1, Rect layout2, float position) { + return new RectF( (layout1.left + ((layout2.left - layout1.left) * position)), (layout1.top + ((layout2.top - layout1.top) * position)), (layout1.right + ((layout2.right - layout1.right) * position)), (layout1.bottom + ((layout2.bottom - layout1.bottom) * position)) - ); - } + ); + } - static int getInterpolatedColor(int color1, int color2, float position) { - int redA = Color.red(color1); - int greenA = Color.green(color1); - int blueA = Color.blue(color1); - int alphaA = Color.alpha(color1); - int redB = Color.red(color2); - int greenB = Color.green(color2); - int blueB = Color.blue(color2); - int alphaB = Color.alpha(color2); - return Color.argb( + static int getInterpolatedColor(int color1, int color2, float position) { + int redA = Color.red(color1); + int greenA = Color.green(color1); + int blueA = Color.blue(color1); + int alphaA = Color.alpha(color1); + int redB = Color.red(color2); + int greenB = Color.green(color2); + int blueB = Color.blue(color2); + int alphaB = Color.alpha(color2); + return Color.argb( (int) (alphaA + ((alphaB - alphaA) * position)), (int) (redA + ((redB - redA) * position)), (int) (greenA + ((greenB - greenA) * position)), (int) (blueA + ((blueB - blueA) * position)) - ); - } + ); + } - static RNSharedElementStyle getInterpolatedStyle( - RNSharedElementStyle style1, - RNSharedElementStyle style2, - float position - ) { - RNSharedElementStyle result = new RNSharedElementStyle(); - result.scaleType = RNSharedElementStyle.getInterpolatingScaleType(style1, style2, position); - result.opacity = style1.opacity + ((style2.opacity - style1.opacity) * position); - result.backgroundColor = RNSharedElementStyle.getInterpolatedColor(style1.backgroundColor, style2.backgroundColor, position); - result.borderTopLeftRadius = style1.borderTopLeftRadius + ((style2.borderTopLeftRadius - style1.borderTopLeftRadius) * position); - result.borderTopRightRadius = style1.borderTopRightRadius + ((style2.borderTopRightRadius - style1.borderTopRightRadius) * position); - result.borderBottomLeftRadius = style1.borderBottomLeftRadius + ((style2.borderBottomLeftRadius - style1.borderBottomLeftRadius) * position); - result.borderBottomRightRadius = style1.borderBottomRightRadius + ((style2.borderBottomRightRadius - style1.borderBottomRightRadius) * position); - result.borderWidth = style1.borderWidth + ((style2.borderWidth - style1.borderWidth) * position); - result.borderColor = RNSharedElementStyle.getInterpolatedColor(style1.borderColor, style2.borderColor, position); - result.borderStyle = style1.borderStyle; - result.elevation = style1.elevation + ((style2.elevation - style1.elevation) * position); - return result; - } + static RNSharedElementStyle getInterpolatedStyle( + RNSharedElementStyle style1, + RNSharedElementStyle style2, + float position + ) { + RNSharedElementStyle result = new RNSharedElementStyle(); + result.scaleType = RNSharedElementStyle.getInterpolatingScaleType(style1, style2, position); + result.opacity = style1.opacity + ((style2.opacity - style1.opacity) * position); + result.backgroundColor = RNSharedElementStyle.getInterpolatedColor(style1.backgroundColor, style2.backgroundColor, position); + result.borderTopLeftRadius = style1.borderTopLeftRadius + ((style2.borderTopLeftRadius - style1.borderTopLeftRadius) * position); + result.borderTopRightRadius = style1.borderTopRightRadius + ((style2.borderTopRightRadius - style1.borderTopRightRadius) * position); + result.borderBottomLeftRadius = style1.borderBottomLeftRadius + ((style2.borderBottomLeftRadius - style1.borderBottomLeftRadius) * position); + result.borderBottomRightRadius = style1.borderBottomRightRadius + ((style2.borderBottomRightRadius - style1.borderBottomRightRadius) * position); + result.borderWidth = style1.borderWidth + ((style2.borderWidth - style1.borderWidth) * position); + result.borderColor = RNSharedElementStyle.getInterpolatedColor(style1.borderColor, style2.borderColor, position); + result.borderStyle = style1.borderStyle; + result.elevation = style1.elevation + ((style2.elevation - style1.elevation) * position); + return result; + } + + static Matrix getAbsoluteViewTransform(View view, boolean failIfNotMounted) { + Matrix matrix = new Matrix(view.getMatrix()); + float[] vals = new float[9]; + matrix.getValues(vals); + + float[] vals2 = new float[9]; + ViewParent parentView = view.getParent(); - static Matrix getAbsoluteViewTransform(View view, boolean failIfNotMounted) { - Matrix matrix = new Matrix(view.getMatrix()); - float[] vals = new float[9]; - matrix.getValues(vals); - - float[] vals2 = new float[9]; - ViewParent parentView = view.getParent(); - - while (parentView != null && parentView instanceof View) { - Matrix parentMatrix = ((View)parentView).getMatrix(); - parentMatrix.getValues(vals2); - - //vals[Matrix.MPERSP_0] *= vals2[Matrix.MPERSP_0]; - //vals[Matrix.MPERSP_1] *= vals2[Matrix.MPERSP_1]; - //vals[Matrix.MPERSP_2] *= vals2[Matrix.MPERSP_3]; - vals[Matrix.MSCALE_X] *= vals2[Matrix.MSCALE_X]; - vals[Matrix.MSCALE_Y] *= vals2[Matrix.MSCALE_Y]; - vals[Matrix.MSKEW_X] += vals2[Matrix.MSKEW_X]; - vals[Matrix.MSKEW_Y] += vals2[Matrix.MSKEW_Y]; - vals[Matrix.MTRANS_X] += vals2[Matrix.MTRANS_X]; - vals[Matrix.MTRANS_Y] += vals2[Matrix.MTRANS_Y]; - - parentView = parentView.getParent(); - } - if (parentView == null && failIfNotMounted) { - return null; - } - matrix.setValues(vals); - return matrix; + while (parentView != null && parentView instanceof View) { + Matrix parentMatrix = ((View) parentView).getMatrix(); + parentMatrix.getValues(vals2); + + //vals[Matrix.MPERSP_0] *= vals2[Matrix.MPERSP_0]; + //vals[Matrix.MPERSP_1] *= vals2[Matrix.MPERSP_1]; + //vals[Matrix.MPERSP_2] *= vals2[Matrix.MPERSP_3]; + vals[Matrix.MSCALE_X] *= vals2[Matrix.MSCALE_X]; + vals[Matrix.MSCALE_Y] *= vals2[Matrix.MSCALE_Y]; + vals[Matrix.MSKEW_X] += vals2[Matrix.MSKEW_X]; + vals[Matrix.MSKEW_Y] += vals2[Matrix.MSKEW_Y]; + vals[Matrix.MTRANS_X] += vals2[Matrix.MTRANS_X]; + vals[Matrix.MTRANS_Y] += vals2[Matrix.MTRANS_Y]; + + parentView = parentView.getParent(); + } + if (parentView == null && failIfNotMounted) { + return null; } + matrix.setValues(vals); + return matrix; + } } diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransition.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransition.java index c7f7c1ca9f0ba..ab7e8f7be3142 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransition.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransition.java @@ -21,429 +21,440 @@ import com.facebook.react.uimanager.events.RCTEventEmitter; public class RNSharedElementTransition extends ViewGroup { - static private String LOG_TAG = "RNSharedElementTransition"; - static private Rect EMPTY_RECT = new Rect(); + static private String LOG_TAG = "RNSharedElementTransition"; - enum Item { - START(0), - END(1); + enum Item { + START(0), + END(1); - private final int value; - Item(final int newValue) {value = newValue;} - public int getValue() { return value; } - } + private final int value; - private RNSharedElementNodeManager mNodeManager = null; - private RNSharedElementAnimation mAnimation = RNSharedElementAnimation.MOVE; - private RNSharedElementResize mResize = RNSharedElementResize.STRETCH; - private RNSharedElementAlign mAlign = RNSharedElementAlign.CENTER_CENTER; - private float mNodePosition = 0.0f; - private boolean mReactLayoutSet = false; - private boolean mInitialLayoutPassCompleted = false; - private boolean mInitialNodePositionSet = false; - private ArrayList mItems = new ArrayList(); - private int[] mParentOffset = new int[2]; - private boolean mRequiresClipping = false; - private RNSharedElementView mStartView; - private RNSharedElementView mEndView; - - public RNSharedElementTransition(ThemedReactContext context, RNSharedElementNodeManager nodeManager) { - super(context); - mNodeManager = nodeManager; - mItems.add(new RNSharedElementTransitionItem(nodeManager, "start")); - mItems.add(new RNSharedElementTransitionItem(nodeManager, "end")); - - mStartView = new RNSharedElementView(context); - addView(mStartView); - - mEndView = new RNSharedElementView(context); - addView(mEndView); + Item(final int newValue) { + value = newValue; } - void releaseData() { - for (RNSharedElementTransitionItem item : mItems) { - item.setNode(null); - } + public int getValue() { + return value; } - - RNSharedElementNodeManager getNodeManager() { - return mNodeManager; + } + + private RNSharedElementNodeManager mNodeManager = null; + private RNSharedElementAnimation mAnimation = RNSharedElementAnimation.MOVE; + private RNSharedElementResize mResize = RNSharedElementResize.STRETCH; + private RNSharedElementAlign mAlign = RNSharedElementAlign.CENTER_CENTER; + private float mNodePosition = 0.0f; + private boolean mReactLayoutSet = false; + private boolean mInitialLayoutPassCompleted = false; + private boolean mInitialNodePositionSet = false; + private ArrayList mItems = new ArrayList(); + private int[] mParentOffset = new int[2]; + private boolean mRequiresClipping = false; + private RNSharedElementView mStartView; + private RNSharedElementView mEndView; + + public RNSharedElementTransition(ThemedReactContext context, RNSharedElementNodeManager nodeManager) { + super(context); + mNodeManager = nodeManager; + mItems.add(new RNSharedElementTransitionItem(nodeManager, "start")); + mItems.add(new RNSharedElementTransitionItem(nodeManager, "end")); + + mStartView = new RNSharedElementView(context); + addView(mStartView); + + mEndView = new RNSharedElementView(context); + addView(mEndView); + } + + void releaseData() { + for (RNSharedElementTransitionItem item : mItems) { + item.setNode(null); } + } - void setItemNode(Item item, RNSharedElementNode node) { - mItems.get(item.getValue()).setNode(node); - requestStylesAndContent(false); - } + RNSharedElementNodeManager getNodeManager() { + return mNodeManager; + } - void setAnimation(final RNSharedElementAnimation animation) { - if (mAnimation != animation) { - mAnimation = animation; - updateLayout(); - } - } + void setItemNode(Item item, RNSharedElementNode node) { + mItems.get(item.getValue()).setNode(node); + requestStylesAndContent(false); + } - void setResize(final RNSharedElementResize resize) { - if (mResize != resize) { - mResize = resize; - updateLayout(); - } + void setAnimation(final RNSharedElementAnimation animation) { + if (mAnimation != animation) { + mAnimation = animation; + updateLayout(); } + } - void setAlign(final RNSharedElementAlign align) { - if (mAlign != align) { - mAlign = align; - updateLayout(); - } + void setResize(final RNSharedElementResize resize) { + if (mResize != resize) { + mResize = resize; + updateLayout(); } + } - void setNodePosition(final float nodePosition) { - if (mNodePosition != nodePosition) { - //Log.d(LOG_TAG, "setNodePosition " + nodePosition + ", mInitialLayoutPassCompleted: " + mInitialLayoutPassCompleted); - mNodePosition = nodePosition; - mInitialNodePositionSet = true; - updateLayout(); - } + void setAlign(final RNSharedElementAlign align) { + if (mAlign != align) { + mAlign = align; + updateLayout(); } - - @Override - @SuppressLint("MissingSuperCall") - public void requestLayout() { - // No-op, terminate `requestLayout` here, all layout is updated in the - // `updateLayout` function - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (!mReactLayoutSet) { - mReactLayoutSet = true; - - // Wait for the whole layout pass to have completed before - // requesting the layout and content - requestStylesAndContent(true); - mInitialLayoutPassCompleted = true; - updateLayout(); - updateNodeVisibility(); - } + } + + void setNodePosition(final float nodePosition) { + if (mNodePosition != nodePosition) { + //Log.d(LOG_TAG, "setNodePosition " + nodePosition + ", mInitialLayoutPassCompleted: " + mInitialLayoutPassCompleted); + mNodePosition = nodePosition; + mInitialNodePositionSet = true; + updateLayout(); } - - @Override - public boolean hasOverlappingRendering() { - return false; + } + + @Override + @SuppressLint("MissingSuperCall") + public void requestLayout() { + // No-op, terminate `requestLayout` here, all layout is updated in the + // `updateLayout` function + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (!mReactLayoutSet) { + mReactLayoutSet = true; + + // Wait for the whole layout pass to have completed before + // requesting the layout and content + requestStylesAndContent(true); + mInitialLayoutPassCompleted = true; + updateLayout(); + updateNodeVisibility(); } - - @Override - protected void dispatchDraw(Canvas canvas) { - //Log.d(LOG_TAG, "dispatchDraw, mRequiresClipping: " + mRequiresClipping + ", width: " + getWidth() + ", height: " + getHeight()); - if (mRequiresClipping) { - canvas.clipRect(0, 0, getWidth(), getHeight()); - } - super.dispatchDraw(canvas); - - // Draw content - //Paint backgroundPaint = new Paint(); - //backgroundPaint.setColor(Color.argb(128, 255, 0, 0)); - //canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint); + } + + @Override + public boolean hasOverlappingRendering() { + return false; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + //Log.d(LOG_TAG, "dispatchDraw, mRequiresClipping: " + mRequiresClipping + ", width: " + getWidth() + ", height: " + getHeight()); + if (mRequiresClipping) { + canvas.clipRect(0, 0, getWidth(), getHeight()); } - - private void requestStylesAndContent(boolean force) { - if (!mInitialLayoutPassCompleted && !force) return; - for (final RNSharedElementTransitionItem item : mItems) { - if (item.getNeedsStyle()) { - item.setNeedsStyle(false); - item.getNode().requestStyle(new Callback() { - @Override - public void invoke(Object... args) { - RNSharedElementStyle style = (RNSharedElementStyle) args[0]; - item.setStyle(style); - updateLayout(); - updateNodeVisibility(); - } - }); - } - if (item.getNeedsContent()) { - item.setNeedsContent(false); - item.getNode().requestContent(new Callback() { - @Override - public void invoke(Object... args) { - RNSharedElementContent content = (RNSharedElementContent) args[0]; - item.setContent(content); - updateLayout(); - updateNodeVisibility(); - } - }); - } - } + super.dispatchDraw(canvas); + + // Draw content + //Paint backgroundPaint = new Paint(); + //backgroundPaint.setColor(Color.argb(128, 255, 0, 0)); + //canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint); + } + + private void requestStylesAndContent(boolean force) { + if (!mInitialLayoutPassCompleted && !force) return; + for (final RNSharedElementTransitionItem item : mItems) { + if (item.getNeedsStyle()) { + item.setNeedsStyle(false); + item.getNode().requestStyle(new Callback() { + @Override + public void invoke(Object... args) { + RNSharedElementStyle style = (RNSharedElementStyle) args[0]; + item.setStyle(style); + updateLayout(); + updateNodeVisibility(); + } + }); + } + if (item.getNeedsContent()) { + item.setNeedsContent(false); + item.getNode().requestContent(new Callback() { + @Override + public void invoke(Object... args) { + RNSharedElementContent content = (RNSharedElementContent) args[0]; + item.setContent(content); + updateLayout(); + updateNodeVisibility(); + } + }); + } } + } - private void updateLayout() { - if (!mInitialLayoutPassCompleted) return; + private void updateLayout() { + if (!mInitialLayoutPassCompleted) return; - // Local data - RNSharedElementTransitionItem startItem = mItems.get(Item.START.getValue()); - RNSharedElementTransitionItem endItem = mItems.get(Item.END.getValue()); + // Local data + RNSharedElementTransitionItem startItem = mItems.get(Item.START.getValue()); + RNSharedElementTransitionItem endItem = mItems.get(Item.END.getValue()); - // Get styles - RNSharedElementStyle startStyle = startItem.getStyle(); - RNSharedElementStyle endStyle = endItem.getStyle(); - if ((startStyle == null) && (endStyle == null)) return; + // Get styles + RNSharedElementStyle startStyle = startItem.getStyle(); + RNSharedElementStyle endStyle = endItem.getStyle(); + if ((startStyle == null) && (endStyle == null)) return; - // Get content - RNSharedElementContent startContent = startItem.getContent(); - RNSharedElementContent endContent = endItem.getContent(); - if ((mAnimation == RNSharedElementAnimation.MOVE) && (startContent == null) && (endContent != null)) { - startContent = endContent; - } + // Get content + RNSharedElementContent startContent = startItem.getContent(); + RNSharedElementContent endContent = endItem.getContent(); + if ((mAnimation == RNSharedElementAnimation.MOVE) && (startContent == null) && (endContent != null)) { + startContent = endContent; + } - // Get layout - Rect startLayout = (startStyle != null) ? startStyle.layout : EMPTY_RECT; - Rect startFrame = (startStyle != null) ? startStyle.frame : EMPTY_RECT; - Rect endLayout = (endStyle != null) ? endStyle.layout : EMPTY_RECT; - Rect endFrame = (endStyle != null) ? endStyle.frame : EMPTY_RECT; - RectF parentLayout = new RectF(startLayout); - parentLayout.union(new RectF(endLayout)); - - // Get clipped areas - Rect startClippedLayout = (startStyle != null) ? startItem.getClippedLayout() : EMPTY_RECT; - Rect startClipInsets = getClipInsets(startLayout, startClippedLayout); - Rect endClippedLayout = (endStyle != null) ? endItem.getClippedLayout() : EMPTY_RECT; - Rect endClipInsets = getClipInsets(endLayout, endClippedLayout); - - // Get interpolated layout - RectF interpolatedLayout; - RectF interpolatedClipInsets; - RNSharedElementStyle interpolatedStyle; - if ((startStyle != null) && (endStyle != null)) { - interpolatedLayout = RNSharedElementStyle.getInterpolatedLayout(startLayout, endLayout, mNodePosition); - interpolatedClipInsets = getInterpolatedClipInsets(parentLayout, startClipInsets, startClippedLayout, endClipInsets, endClippedLayout, mNodePosition); - interpolatedStyle = RNSharedElementStyle.getInterpolatedStyle(startStyle, endStyle, mNodePosition); - } else if (startStyle != null) { - interpolatedLayout = new RectF(startLayout); - interpolatedStyle = startStyle; - interpolatedClipInsets = new RectF(startClipInsets); - } else { - if (!mInitialNodePositionSet) { - mNodePosition = 1.0f; - mInitialNodePositionSet = true; - } - interpolatedLayout = new RectF(endLayout); - interpolatedStyle = endStyle; - interpolatedClipInsets = new RectF(endClipInsets); - } + // Get layout + Rect startLayout = RNSharedElementStyle.normalizeLayout(startStyle, endStyle); + Rect startFrame = (startStyle != null) ? startStyle.frame : RNSharedElementStyle.EMPTY_RECT; + Rect endLayout = RNSharedElementStyle.normalizeLayout(endStyle, startStyle); + Rect endFrame = (endStyle != null) ? endStyle.frame : RNSharedElementStyle.EMPTY_RECT; + RectF parentLayout = new RectF(startLayout); + parentLayout.union(new RectF(endLayout)); + + // Get clipped areas + Rect startClippedLayout = RNSharedElementStyle.normalizeLayout((startStyle != null) ? startItem.getClippedLayout() : RNSharedElementStyle.EMPTY_RECT, startStyle, endStyle); + Rect startClipInsets = getClipInsets(startLayout, startClippedLayout); + Rect endClippedLayout = RNSharedElementStyle.normalizeLayout((endStyle != null) ? endItem.getClippedLayout() : RNSharedElementStyle.EMPTY_RECT, endStyle, startStyle); + Rect endClipInsets = getClipInsets(endLayout, endClippedLayout); + + // Get interpolated layout + RectF interpolatedLayout; + RectF interpolatedClipInsets; + RNSharedElementStyle interpolatedStyle; + if ((startStyle != null) && (endStyle != null)) { + interpolatedLayout = RNSharedElementStyle.getInterpolatedLayout(startLayout, endLayout, mNodePosition); + interpolatedClipInsets = getInterpolatedClipInsets(parentLayout, startClipInsets, startClippedLayout, endClipInsets, endClippedLayout, mNodePosition); + interpolatedStyle = RNSharedElementStyle.getInterpolatedStyle(startStyle, endStyle, mNodePosition); + } else if (startStyle != null) { + interpolatedLayout = new RectF(startLayout); + interpolatedStyle = startStyle; + interpolatedClipInsets = new RectF(startClipInsets); + } else { + if (!mInitialNodePositionSet) { + mNodePosition = 1.0f; + mInitialNodePositionSet = true; + } + interpolatedLayout = new RectF(endLayout); + interpolatedStyle = endStyle; + interpolatedClipInsets = new RectF(endClipInsets); + } - // Apply clipping insets - parentLayout.left += interpolatedClipInsets.left; - parentLayout.top += interpolatedClipInsets.top; - parentLayout.right -= interpolatedClipInsets.right; - parentLayout.bottom -= interpolatedClipInsets.bottom; - - // Calculate clipped layout - mRequiresClipping = !parentLayout.contains(interpolatedLayout); - - //Log.d(LOG_TAG, "updateLayout: " + mNodePosition); - - // Update outer viewgroup layout. The outer viewgroup hosts 2 inner views - // which draw the content & elevation. The outer viewgroup performs additional - // clipping on these views. - ((View)getParent()).getLocationOnScreen(mParentOffset); - super.layout( - -mParentOffset[0], - -mParentOffset[1], - (int) Math.ceil(parentLayout.width() - mParentOffset[0]), - (int) Math.ceil(parentLayout.height() - mParentOffset[1]) - ); - setTranslationX(parentLayout.left); - setTranslationY(parentLayout.top); - - // Determine opacity - float startAlpha = 1.0f; - float endAlpha = 1.0f; - switch (mAnimation) { - case MOVE: - startAlpha = interpolatedStyle.opacity; - endAlpha = (startStyle == null) ? interpolatedStyle.opacity : 0.0f; - break; - case FADE: - startAlpha = ((startStyle != null) ? startStyle.opacity : 1) * (1 - mNodePosition); - endAlpha = ((endStyle != null) ? endStyle.opacity : 1) * mNodePosition; - break; - case FADE_IN: - startAlpha = 0.0f; - endAlpha = ((endStyle != null) ? endStyle.opacity : 1) * mNodePosition; - break; - case FADE_OUT: - startAlpha = ((startStyle != null) ? startStyle.opacity : 1) * (1 - mNodePosition); - endAlpha = 0.0f; - break; - } + // Apply clipping insets + // TODO: Fix clipping when end-layout is larger than + // start-layout in all dimensions. + // TEST: ScrollViews & Clipping > Clip Bottom --> Full reveal + parentLayout.left += interpolatedClipInsets.left; + parentLayout.top += interpolatedClipInsets.top; + parentLayout.right -= interpolatedClipInsets.right; + parentLayout.bottom -= interpolatedClipInsets.bottom; + + // Calculate clipped layout + mRequiresClipping = !parentLayout.contains(interpolatedLayout); + + //Log.d(LOG_TAG, "updateLayout: " + mNodePosition); + + // Update outer viewgroup layout. The outer viewgroup hosts 2 inner views + // which draw the content & elevation. The outer viewgroup performs additional + // clipping on these views. + ((View) getParent()).getLocationOnScreen(mParentOffset); + super.layout( + -mParentOffset[0], + -mParentOffset[1], + (int) Math.ceil(parentLayout.width() - mParentOffset[0]), + (int) Math.ceil(parentLayout.height() - mParentOffset[1]) + ); + setTranslationX(parentLayout.left); + setTranslationY(parentLayout.top); + + // Determine opacity + float startAlpha = 1.0f; + float endAlpha = 1.0f; + switch (mAnimation) { + case MOVE: + startAlpha = interpolatedStyle.opacity; + endAlpha = (startStyle == null) ? interpolatedStyle.opacity : 0.0f; + break; + case FADE: + startAlpha = ((startStyle != null) ? startStyle.opacity : 1) * (1 - mNodePosition); + endAlpha = ((endStyle != null) ? endStyle.opacity : 1) * mNodePosition; + break; + case FADE_IN: + startAlpha = 0.0f; + endAlpha = ((endStyle != null) ? endStyle.opacity : 1) * mNodePosition; + break; + case FADE_OUT: + startAlpha = ((startStyle != null) ? startStyle.opacity : 1) * (1 - mNodePosition); + endAlpha = 0.0f; + break; + } - // Render the start view - if (mAnimation != RNSharedElementAnimation.FADE_IN) { - mStartView.updateViewAndDrawable( - interpolatedLayout, - parentLayout, - startLayout, - startFrame, - startContent, - interpolatedStyle, - startAlpha, - mResize, - mAlign, - mNodePosition - ); - } - - // Render the end view as well for the "cross-fade" animations - if ((mAnimation == RNSharedElementAnimation.FADE) - || (mAnimation == RNSharedElementAnimation.FADE_IN) - || ((mAnimation == RNSharedElementAnimation.MOVE) && (startStyle == null)) - ) { - mEndView.updateViewAndDrawable( - interpolatedLayout, - parentLayout, - endLayout, - endFrame, - endContent, - interpolatedStyle, - endAlpha, - mResize, - mAlign, - mNodePosition - ); - - // Also apply a fade effect on the elevation. This reduces the shadow visibility - // underneath the view which becomes visible when the transparency of the view - // is set. This in turn makes the shadow very visible and gives the whole view - // a "grayish" appearance. The following code tries to reduce that visual artefact. - if (interpolatedStyle.elevation > 0) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - mStartView.setOutlineAmbientShadowColor(Color.argb(startAlpha, 0, 0, 0)); - mStartView.setOutlineSpotShadowColor(Color.argb(startAlpha, 0, 0, 0)); - mEndView.setOutlineAmbientShadowColor(Color.argb(endAlpha, 0, 0, 0)); - mEndView.setOutlineSpotShadowColor(Color.argb(endAlpha, 0, 0, 0)); - } - } - } else { - mEndView.reset(); - } + // Render the start view + if (mAnimation != RNSharedElementAnimation.FADE_IN) { + mStartView.updateViewAndDrawable( + interpolatedLayout, + parentLayout, + startLayout, + startFrame, + startContent, + interpolatedStyle, + startAlpha, + mResize, + mAlign, + mNodePosition + ); + } - // Fire events - if ((startStyle != null) && !startItem.getHasCalledOnMeasure()) { - startItem.setHasCalledOnMeasure(true); - fireMeasureEvent("startNode", startItem, startClippedLayout); - } - if ((endStyle != null) && !endItem.getHasCalledOnMeasure()) { - endItem.setHasCalledOnMeasure(true); - fireMeasureEvent("endNode", endItem, endClippedLayout); + // Render the end view as well for the "cross-fade" animations + if ((mAnimation == RNSharedElementAnimation.FADE) + || (mAnimation == RNSharedElementAnimation.FADE_IN) + || ((mAnimation == RNSharedElementAnimation.MOVE) && (startStyle == null)) + ) { + mEndView.updateViewAndDrawable( + interpolatedLayout, + parentLayout, + endLayout, + endFrame, + endContent, + interpolatedStyle, + endAlpha, + mResize, + mAlign, + mNodePosition + ); + + // Also apply a fade effect on the elevation. This reduces the shadow visibility + // underneath the view which becomes visible when the transparency of the view + // is set. This in turn makes the shadow very visible and gives the whole view + // a "grayish" appearance. The following code tries to reduce that visual artefact. + if (interpolatedStyle.elevation > 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + mStartView.setOutlineAmbientShadowColor(Color.argb(startAlpha, 0, 0, 0)); + mStartView.setOutlineSpotShadowColor(Color.argb(startAlpha, 0, 0, 0)); + mEndView.setOutlineAmbientShadowColor(Color.argb(endAlpha, 0, 0, 0)); + mEndView.setOutlineSpotShadowColor(Color.argb(endAlpha, 0, 0, 0)); } + } + } else { + mEndView.reset(); } - private void updateNodeVisibility() { - RNSharedElementTransitionItem startItem = mItems.get(Item.START.getValue()); - RNSharedElementTransitionItem endItem = mItems.get(Item.END.getValue()); - boolean hidden = mInitialLayoutPassCompleted - && (((startItem.getStyle() != null) && (startItem.getContent() != null)) - || ((endItem.getStyle() != null) && (endItem.getContent() != null))); - for (RNSharedElementTransitionItem item : mItems) { - if (hidden && (mAnimation == RNSharedElementAnimation.FADE_IN) && item.getName().equals("start")) hidden = false; - if (hidden && (mAnimation == RNSharedElementAnimation.FADE_OUT) && item.getName().equals("end")) hidden = false; - item.setHidden(hidden); - } + // Fire events + if ((startStyle != null) && !startItem.getHasCalledOnMeasure()) { + startItem.setHasCalledOnMeasure(true); + fireMeasureEvent("startNode", startItem, startLayout, startClippedLayout); + } + if ((endStyle != null) && !endItem.getHasCalledOnMeasure()) { + endItem.setHasCalledOnMeasure(true); + fireMeasureEvent("endNode", endItem, endLayout, endClippedLayout); + } + } + + private void updateNodeVisibility() { + RNSharedElementTransitionItem startItem = mItems.get(Item.START.getValue()); + RNSharedElementTransitionItem endItem = mItems.get(Item.END.getValue()); + boolean hidden = mInitialLayoutPassCompleted + && (((startItem.getStyle() != null) && (startItem.getContent() != null)) + || ((endItem.getStyle() != null) && (endItem.getContent() != null))); + for (RNSharedElementTransitionItem item : mItems) { + if (hidden && (mAnimation == RNSharedElementAnimation.FADE_IN) && item.getName().equals("start")) + hidden = false; + if (hidden && (mAnimation == RNSharedElementAnimation.FADE_OUT) && item.getName().equals("end")) + hidden = false; + item.setHidden(hidden); } + } - private Rect getClipInsets(Rect layout, Rect clippedLayout) { - return new Rect( + private Rect getClipInsets(Rect layout, Rect clippedLayout) { + return new Rect( clippedLayout.left - layout.left, clippedLayout.top - layout.top, - clippedLayout.right - layout.right, - clippedLayout.bottom - layout.bottom - ); + layout.right - clippedLayout.right, + layout.bottom - clippedLayout.bottom + ); + } + + private RectF getInterpolatedClipInsets( + RectF interpolatedLayout, + Rect startClipInsets, + Rect startClippedLayout, + Rect endClipInsets, + Rect endClippedLayout, + float position) { + RectF clipInsets = new RectF(); + + // Top + if ((endClipInsets.top == 0) && (startClipInsets.top != 0) && (startClippedLayout.top <= endClippedLayout.top)) { + clipInsets.top = Math.max(0, startClippedLayout.top - interpolatedLayout.top); + } else if ((startClipInsets.top == 0) && (endClipInsets.top != 0) && (endClippedLayout.top <= startClippedLayout.top)) { + clipInsets.top = Math.max(0, endClippedLayout.top - interpolatedLayout.top); + } else { + clipInsets.top = (startClipInsets.top + ((endClipInsets.top - startClipInsets.top) * position)); } - private RectF getInterpolatedClipInsets( - RectF interpolatedLayout, - Rect startClipInsets, - Rect startClippedLayout, - Rect endClipInsets, - Rect endClippedLayout, - float position) { - RectF clipInsets = new RectF(); - - // Top - if ((endClipInsets.top == 0) && (startClipInsets.top != 0) && (startClippedLayout.top <= endClippedLayout.top)) { - clipInsets.top = Math.max(0, startClippedLayout.top - interpolatedLayout.top); - } else if ((startClipInsets.top == 0) && (endClipInsets.top != 0) && (endClippedLayout.top <= startClippedLayout.top)) { - clipInsets.top = Math.max(0, endClippedLayout.top - interpolatedLayout.top); - } else { - clipInsets.top = (startClipInsets.top + ((endClipInsets.top - startClipInsets.top) * position)); - } - - // Bottom - if ((endClipInsets.bottom == 0) && (startClipInsets.bottom != 0) && (startClippedLayout.bottom >= endClippedLayout.bottom)) { - clipInsets.bottom = Math.max(0, interpolatedLayout.bottom - startClippedLayout.bottom); - } else if ((startClipInsets.bottom == 0) && (endClipInsets.bottom != 0) && (endClippedLayout.bottom >= startClippedLayout.bottom)) { - clipInsets.bottom = Math.max(0, interpolatedLayout.bottom - endClippedLayout.bottom); - } else { - clipInsets.bottom = (startClipInsets.bottom + ((endClipInsets.bottom - startClipInsets.bottom) * position)); - } - - // Left - if ((endClipInsets.left == 0) && (startClipInsets.left != 0) && (startClippedLayout.left <= endClippedLayout.left)) { - clipInsets.left = Math.max(0, startClippedLayout.left - interpolatedLayout.left); - } else if ((startClipInsets.left == 0) && (endClipInsets.left != 0) && (endClippedLayout.left <= startClippedLayout.left)) { - clipInsets.left = Math.max(0, endClippedLayout.left - interpolatedLayout.left); - } else { - clipInsets.left = (startClipInsets.left + ((endClipInsets.left - startClipInsets.left) * position)); - } + // Bottom + if ((endClipInsets.bottom == 0) && (startClipInsets.bottom != 0) && (startClippedLayout.bottom >= endClippedLayout.bottom)) { + clipInsets.bottom = Math.max(0, interpolatedLayout.bottom - startClippedLayout.bottom); + } else if ((startClipInsets.bottom == 0) && (endClipInsets.bottom != 0) && (endClippedLayout.bottom >= startClippedLayout.bottom)) { + clipInsets.bottom = Math.max(0, interpolatedLayout.bottom - endClippedLayout.bottom); + } else { + clipInsets.bottom = (startClipInsets.bottom + ((endClipInsets.bottom - startClipInsets.bottom) * position)); + } - // Right - if ((endClipInsets.right == 0) && (startClipInsets.right != 0) && (startClippedLayout.right >= endClippedLayout.right)) { - clipInsets.right = Math.max(0, interpolatedLayout.right - startClippedLayout.right); - } else if ((startClipInsets.right == 0) && (endClipInsets.right != 0) && (endClippedLayout.right >= startClippedLayout.right)) { - clipInsets.right = Math.max(0, interpolatedLayout.right - endClippedLayout.right); - } else { - clipInsets.right = (startClipInsets.right + ((endClipInsets.right - startClipInsets.right) * position)); - } + // Left + if ((endClipInsets.left == 0) && (startClipInsets.left != 0) && (startClippedLayout.left <= endClippedLayout.left)) { + clipInsets.left = Math.max(0, startClippedLayout.left - interpolatedLayout.left); + } else if ((startClipInsets.left == 0) && (endClipInsets.left != 0) && (endClippedLayout.left <= startClippedLayout.left)) { + clipInsets.left = Math.max(0, endClippedLayout.left - interpolatedLayout.left); + } else { + clipInsets.left = (startClipInsets.left + ((endClipInsets.left - startClipInsets.left) * position)); + } - return clipInsets; + // Right + if ((endClipInsets.right == 0) && (startClipInsets.right != 0) && (startClippedLayout.right >= endClippedLayout.right)) { + clipInsets.right = Math.max(0, interpolatedLayout.right - startClippedLayout.right); + } else if ((startClipInsets.right == 0) && (endClipInsets.right != 0) && (endClippedLayout.right >= startClippedLayout.right)) { + clipInsets.right = Math.max(0, interpolatedLayout.right - endClippedLayout.right); + } else { + clipInsets.right = (startClipInsets.right + ((endClipInsets.right - startClipInsets.right) * position)); } - private void fireMeasureEvent(String name, RNSharedElementTransitionItem item, Rect clippedLayout) { - ReactContext reactContext = (ReactContext)getContext(); - RNSharedElementStyle style = item.getStyle(); - RNSharedElementContent content = item.getContent(); - - WritableMap layoutData = Arguments.createMap(); - layoutData.putDouble("x", PixelUtil.toDIPFromPixel(style.layout.left)); - layoutData.putDouble("y", PixelUtil.toDIPFromPixel(style.layout.top)); - layoutData.putDouble("width", PixelUtil.toDIPFromPixel(style.layout.width())); - layoutData.putDouble("height", PixelUtil.toDIPFromPixel(style.layout.height())); - layoutData.putDouble("visibleX", PixelUtil.toDIPFromPixel(clippedLayout.left)); - layoutData.putDouble("visibleY", PixelUtil.toDIPFromPixel(clippedLayout.top)); - layoutData.putDouble("visibleWidth", PixelUtil.toDIPFromPixel(clippedLayout.width())); - layoutData.putDouble("visibleHeight", PixelUtil.toDIPFromPixel(clippedLayout.height())); - layoutData.putDouble("contentX", PixelUtil.toDIPFromPixel(style.layout.left)); // TODO - layoutData.putDouble("contentY", PixelUtil.toDIPFromPixel(style.layout.top)); // TODO - layoutData.putDouble("contentWidth", PixelUtil.toDIPFromPixel(style.layout.width())); // TODO - layoutData.putDouble("contentHeight", PixelUtil.toDIPFromPixel(style.layout.height())); // TODO - - WritableMap styleData = Arguments.createMap(); - styleData.putDouble("borderTopLeftRadius", PixelUtil.toDIPFromPixel(style.borderTopLeftRadius)); - styleData.putDouble("borderTopRightRadius", PixelUtil.toDIPFromPixel(style.borderTopRightRadius)); - styleData.putDouble("borderBottomLeftRadius", PixelUtil.toDIPFromPixel(style.borderBottomLeftRadius)); - styleData.putDouble("borderBottomRightRadius", PixelUtil.toDIPFromPixel(style.borderBottomRightRadius)); - - WritableMap eventData = Arguments.createMap(); - eventData.putString("node", name); - eventData.putMap("layout", layoutData); - RNSharedElementDrawable.ViewType viewType = (content != null) + return clipInsets; + } + + private void fireMeasureEvent(String name, RNSharedElementTransitionItem item, Rect layout, Rect clippedLayout) { + ReactContext reactContext = (ReactContext) getContext(); + RNSharedElementStyle style = item.getStyle(); + RNSharedElementContent content = item.getContent(); + + WritableMap layoutData = Arguments.createMap(); + layoutData.putDouble("x", PixelUtil.toDIPFromPixel(layout.left - mParentOffset[0])); + layoutData.putDouble("y", PixelUtil.toDIPFromPixel(layout.top - mParentOffset[1])); + layoutData.putDouble("width", PixelUtil.toDIPFromPixel(layout.width())); + layoutData.putDouble("height", PixelUtil.toDIPFromPixel(layout.height())); + layoutData.putDouble("visibleX", PixelUtil.toDIPFromPixel(clippedLayout.left - mParentOffset[0])); + layoutData.putDouble("visibleY", PixelUtil.toDIPFromPixel(clippedLayout.top - mParentOffset[1])); + layoutData.putDouble("visibleWidth", PixelUtil.toDIPFromPixel(clippedLayout.width())); + layoutData.putDouble("visibleHeight", PixelUtil.toDIPFromPixel(clippedLayout.height())); + // TODO: intrinsic content (unclipped size & position of image) + layoutData.putDouble("contentX", PixelUtil.toDIPFromPixel(layout.left - mParentOffset[0])); // TODO + layoutData.putDouble("contentY", PixelUtil.toDIPFromPixel(layout.top - mParentOffset[1])); // TODO + layoutData.putDouble("contentWidth", PixelUtil.toDIPFromPixel(layout.width())); // TODO + layoutData.putDouble("contentHeight", PixelUtil.toDIPFromPixel(layout.height())); // TODO + + WritableMap styleData = Arguments.createMap(); + styleData.putDouble("borderTopLeftRadius", PixelUtil.toDIPFromPixel(style.borderTopLeftRadius)); + styleData.putDouble("borderTopRightRadius", PixelUtil.toDIPFromPixel(style.borderTopRightRadius)); + styleData.putDouble("borderBottomLeftRadius", PixelUtil.toDIPFromPixel(style.borderBottomLeftRadius)); + styleData.putDouble("borderBottomRightRadius", PixelUtil.toDIPFromPixel(style.borderBottomRightRadius)); + + WritableMap eventData = Arguments.createMap(); + eventData.putString("node", name); + eventData.putMap("layout", layoutData); + RNSharedElementDrawable.ViewType viewType = (content != null) ? RNSharedElementDrawable.getViewType(content.view, style) : RNSharedElementDrawable.ViewType.NONE; - eventData.putString("contentType", viewType.getValue()); - eventData.putMap("style", styleData); + eventData.putString("contentType", viewType.getValue()); + eventData.putMap("style", styleData); - reactContext.getJSModule(RCTEventEmitter.class).receiveEvent( + reactContext.getJSModule(RCTEventEmitter.class).receiveEvent( getId(), "onMeasureNode", eventData); - } + } } diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransitionItem.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransitionItem.java index 8bf6d867c571c..24e0970fd56be 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransitionItem.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransitionItem.java @@ -5,177 +5,171 @@ import android.view.ViewParent; import android.view.ViewGroup; import android.graphics.Rect; -import android.graphics.Matrix; class RNSharedElementTransitionItem { - static private String LOG_TAG = "RNSharedElementTransitionItem"; - - private RNSharedElementNodeManager mNodeManager; - private String mName; - private RNSharedElementNode mNode; - private boolean mHidden; - private boolean mNeedsStyle; - private RNSharedElementStyle mStyle; - private boolean mNeedsContent; - private RNSharedElementContent mContent; - private Rect mClippedLayoutCache; - private boolean mHasCalledOnMeasure; - - RNSharedElementTransitionItem(RNSharedElementNodeManager nodeManager, String name) { - mNodeManager = nodeManager; - mNode = null; - mName = name; - mHidden = false; - mNeedsStyle = false; - mStyle = null; - mNeedsContent = false; - mContent = null; - mClippedLayoutCache = null; - mHasCalledOnMeasure = false; - } - - String getName() { - return mName; - } - - void setHidden(boolean hidden) { - if (mHidden == hidden) return; - mHidden = hidden; - if (mNode == null) return; - if (hidden) { - mNode.addHideRef(); - } else { - mNode.releaseHideRef(); - } - } - - boolean getHidden() { - return mHidden; - } - - RNSharedElementNode getNode() { - return mNode; - } - - void setNode(RNSharedElementNode node) { - if (mNode == node) { - if (node != null) mNodeManager.release(node); - return; - } - if (mNode != null) { - if (mHidden) mNode.releaseHideRef(); - mNodeManager.release(mNode); - } - mNode = node; - mNeedsStyle = node != null; - mStyle = null; - mNeedsContent = (node != null); - mContent = null; - if (mNode != null) { - if (mHidden) mNode.addHideRef(); - } - } - - boolean getNeedsStyle() { - return mNeedsStyle; - } - - void setNeedsStyle(boolean needsStyle) { - mNeedsStyle = needsStyle; - } - - void setStyle(RNSharedElementStyle style) { - mStyle = style; - } - - RNSharedElementStyle getStyle() { - return mStyle; - } - - boolean getNeedsContent() { - return mNeedsContent; - } - - void setNeedsContent(boolean needsContent) { - mNeedsContent = needsContent; - } - - void setContent(RNSharedElementContent content) { - mContent = content; - } - - RNSharedElementContent getContent() { - return mContent; - } - - void setHasCalledOnMeasure(boolean hasCalledOnMeasure) { - mHasCalledOnMeasure = hasCalledOnMeasure; - } - - boolean getHasCalledOnMeasure() { - return mHasCalledOnMeasure; - } - - View getView() { - return (mNode != null) ? mNode.getResolvedView() : null; - } - - Rect getClippedLayout() { - if (mClippedLayoutCache != null) return mClippedLayoutCache; - if (mStyle == null) return null; - - View view = getView(); - View ancestorView = mNode.getAncestorView(); - - // Get ancestor transform - float[] f = new float[9]; - mStyle.ancestorTransform.getValues(f); - int ancestorTranslateX = (int) f[Matrix.MTRANS_X]; - int ancestorTranslateY = (int) f[Matrix.MTRANS_Y]; - - // Get visible area (some parts may be clipped in a scrollview or something) - Rect clippedLayout = new Rect(mStyle.layout); - ViewParent parentView = view.getParent(); - int[] location = new int[2]; - Rect bounds = new Rect(); - while (parentView != null) { - if (!(parentView instanceof ViewGroup)) break; - ViewGroup viewGroup = (ViewGroup) parentView; - viewGroup.getLocationOnScreen(location); - location[0] -= ancestorTranslateX; - location[1] -= ancestorTranslateY; - - bounds.left = location[0]; - bounds.top = location[1]; - bounds.right = location[0] + (viewGroup.getWidth()); - bounds.bottom = location[1] + (viewGroup.getHeight()); - - if (!clippedLayout.intersect(bounds)) { - if (clippedLayout.bottom < bounds.top) { - clippedLayout.top = bounds.top; - clippedLayout.bottom = bounds.top; - } - if (clippedLayout.top > bounds.bottom) { - clippedLayout.top = bounds.bottom; - clippedLayout.bottom = bounds.bottom; - } - if (clippedLayout.right < bounds.left) { - clippedLayout.left = bounds.left; - clippedLayout.right = bounds.left; - } - if (clippedLayout.left > bounds.right) { - clippedLayout.left = bounds.right; - clippedLayout.right = bounds.right; - } - break; - } - if (parentView == ancestorView) { - break; - } - parentView = parentView.getParent(); + static private String LOG_TAG = "RNSharedElementTransitionItem"; + + private RNSharedElementNodeManager mNodeManager; + private String mName; + private RNSharedElementNode mNode; + private boolean mHidden; + private boolean mNeedsStyle; + private RNSharedElementStyle mStyle; + private boolean mNeedsContent; + private RNSharedElementContent mContent; + private Rect mClippedLayoutCache; + private boolean mHasCalledOnMeasure; + + RNSharedElementTransitionItem(RNSharedElementNodeManager nodeManager, String name) { + mNodeManager = nodeManager; + mNode = null; + mName = name; + mHidden = false; + mNeedsStyle = false; + mStyle = null; + mNeedsContent = false; + mContent = null; + mClippedLayoutCache = null; + mHasCalledOnMeasure = false; + } + + String getName() { + return mName; + } + + void setHidden(boolean hidden) { + if (mHidden == hidden) return; + mHidden = hidden; + if (mNode == null) return; + if (hidden) { + mNode.addHideRef(); + } else { + mNode.releaseHideRef(); + } + } + + boolean getHidden() { + return mHidden; + } + + RNSharedElementNode getNode() { + return mNode; + } + + void setNode(RNSharedElementNode node) { + if (mNode == node) { + if (node != null) mNodeManager.release(node); + return; + } + if (mNode != null) { + if (mHidden) mNode.releaseHideRef(); + mNodeManager.release(mNode); + } + mNode = node; + mNeedsStyle = node != null; + mStyle = null; + mNeedsContent = (node != null); + mContent = null; + if (mNode != null) { + if (mHidden) mNode.addHideRef(); + } + } + + boolean getNeedsStyle() { + return mNeedsStyle; + } + + void setNeedsStyle(boolean needsStyle) { + mNeedsStyle = needsStyle; + } + + void setStyle(RNSharedElementStyle style) { + mStyle = style; + } + + RNSharedElementStyle getStyle() { + return mStyle; + } + + boolean getNeedsContent() { + return mNeedsContent; + } + + void setNeedsContent(boolean needsContent) { + mNeedsContent = needsContent; + } + + void setContent(RNSharedElementContent content) { + mContent = content; + } + + RNSharedElementContent getContent() { + return mContent; + } + + void setHasCalledOnMeasure(boolean hasCalledOnMeasure) { + mHasCalledOnMeasure = hasCalledOnMeasure; + } + + boolean getHasCalledOnMeasure() { + return mHasCalledOnMeasure; + } + + View getView() { + return (mNode != null) ? mNode.getResolvedView() : null; + } + + Rect getClippedLayout() { + if (mClippedLayoutCache != null) return mClippedLayoutCache; + if (mStyle == null) return null; + + View view = getView(); + View ancestorView = mNode.getAncestorView(); + + // Get visible area (some parts may be clipped in a scrollview or something) + Rect clippedLayout = new Rect(mStyle.layout); + ViewParent parentView = view.getParent(); + int[] location = new int[2]; + Rect bounds = new Rect(); + while (parentView != null) { + if (!(parentView instanceof ViewGroup)) break; + ViewGroup viewGroup = (ViewGroup) parentView; + viewGroup.getLocationOnScreen(location); + + bounds.left = location[0]; + bounds.top = location[1]; + bounds.right = location[0] + (viewGroup.getWidth()); + bounds.bottom = location[1] + (viewGroup.getHeight()); + + if (viewGroup.getClipChildren()) { + if (!clippedLayout.intersect(bounds)) { + if (clippedLayout.bottom < bounds.top) { + clippedLayout.top = bounds.top; + clippedLayout.bottom = bounds.top; + } + if (clippedLayout.top > bounds.bottom) { + clippedLayout.top = bounds.bottom; + clippedLayout.bottom = bounds.bottom; + } + if (clippedLayout.right < bounds.left) { + clippedLayout.left = bounds.left; + clippedLayout.right = bounds.left; + } + if (clippedLayout.left > bounds.right) { + clippedLayout.left = bounds.right; + clippedLayout.right = bounds.right; + } + break; } - - mClippedLayoutCache = clippedLayout; - return clippedLayout; - } -} \ No newline at end of file + } + if (parentView == ancestorView) { + break; + } + view = (View) parentView; + parentView = parentView.getParent(); + } + + mClippedLayoutCache = clippedLayout; + return clippedLayout; + } +} diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransitionManager.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransitionManager.java index 4943e00108e3b..6d1a5f20fdb14 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransitionManager.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTransitionManager.java @@ -13,82 +13,82 @@ import com.facebook.react.bridge.ReactApplicationContext; public class RNSharedElementTransitionManager extends SimpleViewManager { - public static final String REACT_CLASS = "RNSharedElementTransition"; + public static final String REACT_CLASS = "RNSharedElementTransition"; - public RNSharedElementTransitionManager(ReactApplicationContext reactContext) { - super(); - } + public RNSharedElementTransitionManager(ReactApplicationContext reactContext) { + super(); + } - @Override - public String getName() { - return REACT_CLASS; - } + @Override + public String getName() { + return REACT_CLASS; + } - @Override - public Map getExportedCustomBubblingEventTypeConstants() { - return MapBuilder.builder() + @Override + public Map getExportedCustomBubblingEventTypeConstants() { + return MapBuilder.builder() .put( - "onMeasureNode", - MapBuilder.of( - "phasedRegistrationNames", - MapBuilder.of("bubbled", "onMeasureNode"))) - .build(); - } - - @Override - public RNSharedElementTransition createViewInstance(ThemedReactContext reactContext) { - RNSharedElementModule module = (RNSharedElementModule) reactContext.getNativeModule(RNSharedElementModule.class); - return new RNSharedElementTransition(reactContext, module.getNodeManager()); - } - - @Override - public void onDropViewInstance(RNSharedElementTransition view) { - super.onDropViewInstance(view); - view.releaseData(); - } - - @ReactProp(name = "nodePosition") - public void setNodePosition(final RNSharedElementTransition view, final float nodePosition) { - view.setNodePosition(nodePosition); - } - - @ReactProp(name = "animation") - public void setAnimation(final RNSharedElementTransition view, final int animation) { - view.setAnimation(RNSharedElementAnimation.values()[animation]); - } - - @ReactProp(name = "resize") - public void setResize(final RNSharedElementTransition view, final int resize) { - view.setResize(RNSharedElementResize.values()[resize]); - } - - @ReactProp(name = "align") - public void setAlign(final RNSharedElementTransition view, final int align) { - view.setAlign(RNSharedElementAlign.values()[align]); - } - - private void setViewItem(final RNSharedElementTransition view, RNSharedElementTransition.Item item, final ReadableMap map) { - if (map == null) return; - if (!map.hasKey("node") || !map.hasKey("ancestor")) return; - final ReadableMap nodeMap = map.getMap("node"); - final ReadableMap ancestorMap = map.getMap("ancestor"); - int nodeHandle = nodeMap.getInt("nodeHandle"); - int ancestorHandle = ancestorMap.getInt("nodeHandle"); - boolean isParent = nodeMap.getBoolean("isParent"); - ReadableMap styleConfig = nodeMap.getMap("nodeStyle"); - View nodeView = view.getNodeManager().getNativeViewHierarchyManager().resolveView(nodeHandle); - View ancestorView = view.getNodeManager().getNativeViewHierarchyManager().resolveView(ancestorHandle); - RNSharedElementNode node = view.getNodeManager().acquire(nodeHandle, nodeView, isParent, ancestorView, styleConfig); - view.setItemNode(item, node); - } - - @ReactProp(name = "startNode") - public void setStartNode(final RNSharedElementTransition view, final ReadableMap startNode) { - setViewItem(view, RNSharedElementTransition.Item.START, startNode); - } - - @ReactProp(name = "endNode") - public void setEndNode(final RNSharedElementTransition view, final ReadableMap endNode) { - setViewItem(view, RNSharedElementTransition.Item.END, endNode); - } + "onMeasureNode", + MapBuilder.of( + "phasedRegistrationNames", + MapBuilder.of("bubbled", "onMeasureNode"))) + .build(); + } + + @Override + public RNSharedElementTransition createViewInstance(ThemedReactContext reactContext) { + RNSharedElementModule module = (RNSharedElementModule) reactContext.getNativeModule(RNSharedElementModule.class); + return new RNSharedElementTransition(reactContext, module.getNodeManager()); + } + + @Override + public void onDropViewInstance(RNSharedElementTransition view) { + super.onDropViewInstance(view); + view.releaseData(); + } + + @ReactProp(name = "nodePosition") + public void setNodePosition(final RNSharedElementTransition view, final float nodePosition) { + view.setNodePosition(nodePosition); + } + + @ReactProp(name = "animation") + public void setAnimation(final RNSharedElementTransition view, final int animation) { + view.setAnimation(RNSharedElementAnimation.values()[animation]); + } + + @ReactProp(name = "resize") + public void setResize(final RNSharedElementTransition view, final int resize) { + view.setResize(RNSharedElementResize.values()[resize]); + } + + @ReactProp(name = "align") + public void setAlign(final RNSharedElementTransition view, final int align) { + view.setAlign(RNSharedElementAlign.values()[align]); + } + + private void setViewItem(final RNSharedElementTransition view, RNSharedElementTransition.Item item, final ReadableMap map) { + if (map == null) return; + if (!map.hasKey("node") || !map.hasKey("ancestor")) return; + final ReadableMap nodeMap = map.getMap("node"); + final ReadableMap ancestorMap = map.getMap("ancestor"); + int nodeHandle = nodeMap.getInt("nodeHandle"); + int ancestorHandle = ancestorMap.getInt("nodeHandle"); + boolean isParent = nodeMap.getBoolean("isParent"); + ReadableMap styleConfig = nodeMap.getMap("nodeStyle"); + View nodeView = view.getNodeManager().getNativeViewHierarchyManager().resolveView(nodeHandle); + View ancestorView = view.getNodeManager().getNativeViewHierarchyManager().resolveView(ancestorHandle); + RNSharedElementNode node = view.getNodeManager().acquire(nodeHandle, nodeView, isParent, ancestorView, styleConfig); + view.setItemNode(item, node); + } + + @ReactProp(name = "startNode") + public void setStartNode(final RNSharedElementTransition view, final ReadableMap startNode) { + setViewItem(view, RNSharedElementTransition.Item.START, startNode); + } + + @ReactProp(name = "endNode") + public void setEndNode(final RNSharedElementTransition view, final ReadableMap endNode) { + setViewItem(view, RNSharedElementTransition.Item.END, endNode); + } } \ No newline at end of file diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTypes.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTypes.java index aaeae9a86d285..df09d073de7ae 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTypes.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementTypes.java @@ -1,40 +1,58 @@ package versioned.host.exp.exponent.modules.api.components.sharedelement; enum RNSharedElementAnimation { - MOVE(0), - FADE(1), - FADE_IN(2), - FADE_OUT(3); - - private final int value; - RNSharedElementAnimation(final int newValue) {value = newValue;} - public int getValue() { return value; } + MOVE(0), + FADE(1), + FADE_IN(2), + FADE_OUT(3); + + private final int value; + + RNSharedElementAnimation(final int newValue) { + value = newValue; + } + + public int getValue() { + return value; + } } enum RNSharedElementResize { - AUTO(0), - STRETCH(1), - CLIP(2), - NONE(3); - - private final int value; - RNSharedElementResize(final int newValue) {value = newValue;} - public int getValue() { return value; } + AUTO(0), + STRETCH(1), + CLIP(2), + NONE(3); + + private final int value; + + RNSharedElementResize(final int newValue) { + value = newValue; + } + + public int getValue() { + return value; + } } enum RNSharedElementAlign { - AUTO(0), - LEFT_TOP(1), - LEFT_CENTER(2), - LEFT_BOTTOM(3), - RIGHT_TOP(4), - RIGHT_CENTER(5), - RIGHT_BOTTOM(6), - CENTER_TOP(7), - CENTER_CENTER(8), - CENTER_BOTTOM(9); - - private final int value; - RNSharedElementAlign(final int newValue) {value = newValue;} - public int getValue() { return value; } + AUTO(0), + LEFT_TOP(1), + LEFT_CENTER(2), + LEFT_BOTTOM(3), + RIGHT_TOP(4), + RIGHT_CENTER(5), + RIGHT_BOTTOM(6), + CENTER_TOP(7), + CENTER_CENTER(8), + CENTER_BOTTOM(9); + + private final int value; + + RNSharedElementAlign(final int newValue) { + value = newValue; + } + + public int getValue() { + return value; + } } \ No newline at end of file diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementView.java index 8726654e79be5..cb54a8adf235f 100644 --- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementView.java +++ b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/sharedelement/RNSharedElementView.java @@ -8,80 +8,80 @@ import com.facebook.react.uimanager.ThemedReactContext; class RNSharedElementView extends View { - static private String LOG_TAG = "RNSharedElementView"; - - private RNSharedElementDrawable mDrawable; - private RNSharedElementDrawable.ViewType mViewType; - - RNSharedElementView(ThemedReactContext context) { - super(context); - mViewType = RNSharedElementDrawable.ViewType.NONE; - mDrawable = new RNSharedElementDrawable(); - setBackground(mDrawable); - } - - @Override - public boolean hasOverlappingRendering() { - return mViewType == RNSharedElementDrawable.ViewType.GENERIC; - } - - void reset() { - setAlpha(0.0f); + static private String LOG_TAG = "RNSharedElementView"; + + private RNSharedElementDrawable mDrawable; + private RNSharedElementDrawable.ViewType mViewType; + + RNSharedElementView(ThemedReactContext context) { + super(context); + mViewType = RNSharedElementDrawable.ViewType.NONE; + mDrawable = new RNSharedElementDrawable(); + setBackground(mDrawable); + } + + @Override + public boolean hasOverlappingRendering() { + return mViewType == RNSharedElementDrawable.ViewType.GENERIC; + } + + void reset() { + setAlpha(0.0f); + } + + void updateViewAndDrawable( + RectF layout, + RectF parentLayout, + Rect originalLayout, + Rect originalFrame, + RNSharedElementContent content, + RNSharedElementStyle style, + float alpha, + RNSharedElementResize resize, + RNSharedElementAlign align, + float position) { + + // Update drawable + RNSharedElementDrawable.ViewType viewType = mDrawable.update(content, style, position); + boolean useGPUScaling = (resize != RNSharedElementResize.CLIP) && + ((viewType == RNSharedElementDrawable.ViewType.GENERIC) || + (viewType == RNSharedElementDrawable.ViewType.PLAIN)); + + // Update layer type + if (mViewType != viewType) { + mViewType = viewType; + setLayerType(useGPUScaling ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null); } - void updateViewAndDrawable( - RectF layout, - RectF parentLayout, - Rect originalLayout, - Rect originalFrame, - RNSharedElementContent content, - RNSharedElementStyle style, - float alpha, - RNSharedElementResize resize, - RNSharedElementAlign align, - float position) { - - // Update drawable - RNSharedElementDrawable.ViewType viewType = mDrawable.update(content, style, position); - boolean useGPUScaling = (resize != RNSharedElementResize.CLIP) && - ((viewType == RNSharedElementDrawable.ViewType.GENERIC) || - (viewType == RNSharedElementDrawable.ViewType.PLAIN)); - - // Update layer type - if (mViewType != viewType) { - mViewType = viewType; - setLayerType(useGPUScaling ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null); + // Update view size/position/scale + float width = layout.width(); + float height = layout.height(); + if (useGPUScaling) { + int originalWidth = originalFrame.width(); + int originalHeight = originalFrame.height(); + + // Update view + layout(0, 0, originalWidth, originalHeight); + setTranslationX(layout.left - parentLayout.left); + setTranslationY(layout.top - parentLayout.top); + + // Update scale + float scaleX = width / (float) originalWidth; + float scaleY = height / (float) originalHeight; + if (!Float.isInfinite(scaleX) && !Float.isNaN(scaleX) && !Float.isInfinite(scaleY) && !Float.isNaN(scaleY)) { + + // Determine si + switch (resize) { + case AUTO: + case STRETCH: + break; + case CLIP: + case NONE: + scaleX = (float) originalWidth / (float) originalLayout.width(); + scaleY = (float) originalHeight / (float) originalLayout.height(); + break; } - // Update view size/position/scale - float width = layout.width(); - float height = layout.height(); - if (useGPUScaling) { - int originalWidth = originalFrame.width(); - int originalHeight = originalFrame.height(); - - // Update view - layout(0, 0, originalWidth, originalHeight); - setTranslationX(layout.left - parentLayout.left); - setTranslationY(layout.top - parentLayout.top); - - // Update scale - float scaleX = width / (float) originalWidth; - float scaleY = height / (float) originalHeight; - if (!Float.isInfinite(scaleX) && !Float.isNaN(scaleX) && !Float.isInfinite(scaleY) && !Float.isNaN(scaleY)) { - - // Determine si - switch (resize) { - case AUTO: - case STRETCH: - break; - case CLIP: - case NONE: - scaleX = (float) originalWidth / (float) originalLayout.width(); - scaleY = (float) originalHeight / (float) originalLayout.height(); - break; - } - /*switch (align) { case LEFT_TOP: @@ -104,23 +104,23 @@ void updateViewAndDrawable( break; }*/ - setScaleX(scaleX); - setScaleY(scaleY); - } - setPivotX(0); - setPivotY(0); - } else { - - // Update view - layout(0, 0, (int)Math.ceil(width), (int)Math.ceil(height)); - setTranslationX(layout.left - parentLayout.left); - setTranslationY(layout.top - parentLayout.top); - } + setScaleX(scaleX); + setScaleY(scaleY); + } + setPivotX(0); + setPivotY(0); + } else { + + // Update view + layout(0, 0, (int) Math.ceil(width), (int) Math.ceil(height)); + setTranslationX(layout.left - parentLayout.left); + setTranslationY(layout.top - parentLayout.top); + } - // Update view opacity and elevation - setAlpha(alpha); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { - setElevation(style.elevation); - } + // Update view opacity and elevation + setAlpha(alpha); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + setElevation(style.elevation); } + } } diff --git a/apps/native-component-list/package.json b/apps/native-component-list/package.json index 6c0d7482af556..faa8aa6c55e4f 100644 --- a/apps/native-component-list/package.json +++ b/apps/native-component-list/package.json @@ -92,7 +92,7 @@ "react-native-safe-area-context": "0.7.3", "react-native-safe-area-view": "^0.14.8", "react-native-screens": "~2.2.0", - "react-native-shared-element": "~0.5.6", + "react-native-shared-element": "0.7.0", "react-native-svg": "11.0.1", "react-native-view-shot": "^3.1.2", "react-native-web": "^0.11.0", diff --git a/ios/Exponent.xcodeproj/project.pbxproj b/ios/Exponent.xcodeproj/project.pbxproj index be5cf5afd8dec..8154bb9853160 100644 --- a/ios/Exponent.xcodeproj/project.pbxproj +++ b/ios/Exponent.xcodeproj/project.pbxproj @@ -339,6 +339,7 @@ F21159F62C244F3BA8CDE98B /* RCTConvert+UIPageViewControllerTransitionStyle.m in Sources */ = {isa = PBXBuildFile; fileRef = 4670FD34142C4706BEA4FD1E /* RCTConvert+UIPageViewControllerTransitionStyle.m */; settings = {COMPILER_FLAGS = "-w"; }; }; F77DDB931E04AC1100624CA2 /* SafariServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F77DDB921E04AC1100624CA2 /* SafariServices.framework */; }; FA526593B78C87A7E1C34561 /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D8BAB8D6102FB06BE5BE10B /* libPods-Tests.a */; }; + F581B81CB601426CB5658AEE /* RNSharedElementCornerRadii.m in Sources */ = {isa = PBXBuildFile; fileRef = 780F0BF062134A048B33344C /* RNSharedElementCornerRadii.m */; settings = {COMPILER_FLAGS = "-w"; }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1037,6 +1038,8 @@ F1D6AF6524472BA900AC1C74 /* EXDevMenuDelegateProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EXDevMenuDelegateProtocol.h; sourceTree = ""; }; F77DDB921E04AC1100624CA2 /* SafariServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SafariServices.framework; path = System/Library/Frameworks/SafariServices.framework; sourceTree = SDKROOT; }; FA872B0AA66044B4B8C574F0 /* RNSScreenStack.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; path = RNSScreenStack.h; sourceTree = ""; }; + 57C7EC4AED92477D8C6743E0 /* RNSharedElementCornerRadii.h */ = {isa = PBXFileReference; name = "RNSharedElementCornerRadii.h"; path = "RNSharedElementCornerRadii.h"; sourceTree = ""; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; }; + 780F0BF062134A048B33344C /* RNSharedElementCornerRadii.m */ = {isa = PBXFileReference; name = "RNSharedElementCornerRadii.m"; path = "RNSharedElementCornerRadii.m"; sourceTree = ""; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1221,6 +1224,8 @@ 143A7129A6DA415F8B51B71E /* RNSharedElementTransitionManager.h */, 1E9033E268A74098A882CCEB /* RNSharedElementTransitionManager.m */, CC72501D815B491FBC42AACA /* RNSharedElementTypes.h */, + 57C7EC4AED92477D8C6743E0 /* RNSharedElementCornerRadii.h */, + 780F0BF062134A048B33344C /* RNSharedElementCornerRadii.m */, ); path = SharedElement; sourceTree = ""; @@ -2948,6 +2953,7 @@ 66827DD222C54196B1FDA739 /* RNSScreenStackHeaderConfig.m in Sources */, B75F881E8F9641B785F76CB0 /* RNSVGForeignObject.m in Sources */, C1A9EE2CB1DF4BD8936C3C7F /* RNSVGForeignObjectManager.m in Sources */, + F581B81CB601426CB5658AEE /* RNSharedElementCornerRadii.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementContent.m b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementContent.m index 242ea5faa47ec..68ac5f0861b7c 100644 --- a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementContent.m +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementContent.m @@ -7,98 +7,98 @@ @implementation RNSharedElementContent { - // nop + // nop } - (instancetype)initWithData:(id) data type:(RNSharedElementContentType)type insets:(UIEdgeInsets)insets { - _data = data; - _type = type; - _insets = insets; - return self; + _data = data; + _type = type; + _insets = insets; + return self; } + (BOOL) isKindOfImageView:(UIView*) view { - return ( - [view isKindOfClass:[UIImageView class]] || - [NSStringFromClass(view.class) isEqualToString:@"RCTImageView"] - ); + return ( + [view isKindOfClass:[UIImageView class]] || + [NSStringFromClass(view.class) isEqualToString:@"RCTImageView"] + ); } + (UIImageView*) imageViewFromView:(UIView*) view { - if ([view isKindOfClass:[UIImageView class]]) { - return (UIImageView*) view; - } else if ([NSStringFromClass(view.class) isEqualToString:@"RCTImageView"]) { - // As of react-native 0.60, RCTImageView is no longer inherited from - // UIImageView, but has a UIImageView as child. That will cause this code-path - // to be executed, where the first child view is returned. - return (UIImageView*) view.subviews.firstObject; - } else { - // Error - return nil; - } + if ([view isKindOfClass:[UIImageView class]]) { + return (UIImageView*) view; + } else if ([NSStringFromClass(view.class) isEqualToString:@"RCTImageView"]) { + // As of react-native 0.60, RCTImageView is no longer inherited from + // UIImageView, but has a UIImageView as child. That will cause this code-path + // to be executed, where the first child view is returned. + return (UIImageView*) view.subviews.firstObject; + } else { + // Error + return nil; + } } - (NSString*) typeName { - switch(_type) { - case RNSharedElementContentTypeNone: return @"none"; - case RNSharedElementContentTypeRawImage: return @"image"; - case RNSharedElementContentTypeSnapshotView: return @"snapshotView"; - case RNSharedElementContentTypeSnapshotImage: return @"snapshotImage"; - default: return @"unknown"; - } + switch(_type) { + case RNSharedElementContentTypeNone: return @"none"; + case RNSharedElementContentTypeRawImage: return @"image"; + case RNSharedElementContentTypeSnapshotView: return @"snapshotView"; + case RNSharedElementContentTypeSnapshotImage: return @"snapshotImage"; + default: return @"unknown"; + } } + (CGSize) sizeForRect:(CGRect)layout content:(RNSharedElementContent*)content { - if (content == nil || content.data == nil) return layout.size; - if (content.type != RNSharedElementContentTypeRawImage) return layout.size; - CGSize size = layout.size; - return [content.data isKindOfClass:[UIImage class]] ? ((UIImage*)content.data).size : size; + if (content == nil || content.data == nil) return layout.size; + if (content.type != RNSharedElementContentTypeRawImage) return layout.size; + CGSize size = layout.size; + return [content.data isKindOfClass:[UIImage class]] ? ((UIImage*)content.data).size : size; } + (CGRect) layoutForRect:(CGRect)layout content:(RNSharedElementContent*) content contentMode:(UIViewContentMode) contentMode reverse:(BOOL)reverse { - if (content == nil || content.data == nil) return layout; - if (content.type != RNSharedElementContentTypeRawImage) return layout; - CGSize size = layout.size; - size.width -= (content.insets.left + content.insets.right); - size.height -= (content.insets.top + content.insets.bottom); - CGSize contentSize = [RNSharedElementContent sizeForRect:layout content:content]; - CGFloat contentAspectRatio = (contentSize.width / contentSize.height); - BOOL lo = (size.width / size.height) < contentAspectRatio; - BOOL aspectRatioCriteria = reverse ? !lo : lo; - switch (contentMode) { - case UIViewContentModeScaleToFill: // stretch - break; - case UIViewContentModeScaleAspectFit: // contain - if (aspectRatioCriteria) { - size.height = size.width / contentAspectRatio; - } else { - size.width = size.height * contentAspectRatio; - } - break; - case UIViewContentModeScaleAspectFill: // cover - if (aspectRatioCriteria) { - size.width = size.height * contentAspectRatio; - } else { - size.height = size.width / contentAspectRatio; - } - break; - case UIViewContentModeCenter: // center - size = contentSize; - break; - default: - break; - } - CGRect contentLayout = layout; - contentLayout.origin.x += (contentLayout.size.width - size.width) / 2; - contentLayout.origin.y += (contentLayout.size.height - size.height) / 2; - contentLayout.size = size; - return contentLayout; + if (content == nil || content.data == nil) return layout; + if (content.type != RNSharedElementContentTypeRawImage) return layout; + CGSize size = layout.size; + size.width -= (content.insets.left + content.insets.right); + size.height -= (content.insets.top + content.insets.bottom); + CGSize contentSize = [RNSharedElementContent sizeForRect:layout content:content]; + CGFloat contentAspectRatio = (contentSize.width / contentSize.height); + BOOL lo = (size.width / size.height) < contentAspectRatio; + BOOL aspectRatioCriteria = reverse ? !lo : lo; + switch (contentMode) { + case UIViewContentModeScaleToFill: // stretch + break; + case UIViewContentModeScaleAspectFit: // contain + if (aspectRatioCriteria) { + size.height = size.width / contentAspectRatio; + } else { + size.width = size.height * contentAspectRatio; + } + break; + case UIViewContentModeScaleAspectFill: // cover + if (aspectRatioCriteria) { + size.width = size.height * contentAspectRatio; + } else { + size.height = size.width / contentAspectRatio; + } + break; + case UIViewContentModeCenter: // center + size = contentSize; + break; + default: + break; + } + CGRect contentLayout = layout; + contentLayout.origin.x += (contentLayout.size.width - size.width) / 2; + contentLayout.origin.y += (contentLayout.size.height - size.height) / 2; + contentLayout.size = size; + return contentLayout; } @end diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementCornerRadii.h b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementCornerRadii.h new file mode 100644 index 0000000000000..29fd8841cbc91 --- /dev/null +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementCornerRadii.h @@ -0,0 +1,34 @@ +// +// RNSharedElementCornerRadii_h +// react-native-shared-element +// + +#import +#import + +typedef NS_ENUM(NSInteger, RNSharedElementCorner) { + RNSharedElementCornerAll = 0, + RNSharedElementCornerTopLeft = 1, + RNSharedElementCornerTopRight = 2, + RNSharedElementCornerBottomLeft = 3, + RNSharedElementCornerBottomRight = 4, + RNSharedElementCornerTopStart = 5, + RNSharedElementCornerTopEnd = 6, + RNSharedElementCornerBottomStart = 7, + RNSharedElementCornerBottomEnd = 8 +}; + +@interface RNSharedElementCornerRadii : NSObject + +@property (nonatomic, assign) UIUserInterfaceLayoutDirection layoutDirection; + +- (instancetype)init; + +- (CGFloat)radiusForCorner:(RNSharedElementCorner)corner; +- (BOOL)setRadius:(CGFloat)radius corner:(RNSharedElementCorner)corner; + +- (void)updateClipMaskForLayer:(CALayer *)layer bounds:(CGRect)bounds; +- (void)updateShadowPathForLayer:(CALayer *)layer bounds:(CGRect)bounds; +- (RCTCornerRadii)radiiForBounds:(CGRect)bounds; + +@end diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementCornerRadii.m b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementCornerRadii.m new file mode 100644 index 0000000000000..5060067f4a8e1 --- /dev/null +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementCornerRadii.m @@ -0,0 +1,162 @@ +// +// RNSharedElementCornerRadii_m +// react-native-shared-element +// + +#import "RNSharedElementCornerRadii.h" +#import +#import + +static CGFloat RNSharedElementDefaultIfNegativeTo(CGFloat defaultValue, CGFloat x) +{ + return x >= 0 ? x : defaultValue; +}; + +#define RADII_COUNT 9 + +@implementation RNSharedElementCornerRadii { + CGFloat _radii[RADII_COUNT]; + BOOL _invalidated; + CGRect _cachedBounds; + RCTCornerRadii _cachedRadii; +} + +- (instancetype)init +{ + if (self = [super init]) { + _invalidated = YES; + _layoutDirection = UIUserInterfaceLayoutDirectionLeftToRight; + for (int i = 0; i < RADII_COUNT; i++) { + _radii[i] = -1; + } + } + return self; +} + + +#pragma mark Properties + +- (void)setLayoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection +{ + if (_layoutDirection != layoutDirection) { + _layoutDirection = layoutDirection; + _invalidated = YES; + } +} + + +#pragma mark Methods + +- (CGFloat)radiusForCorner:(RNSharedElementCorner)corner +{ + return _radii[corner]; +} + +- (BOOL)setRadius:(CGFloat)radius corner:(RNSharedElementCorner)corner +{ + if (_radii[corner] != radius) { + _radii[corner] = radius; + _invalidated = YES; + return YES; + } + return NO; +} + +- (void)updateClipMaskForLayer:(CALayer *)layer bounds:(CGRect)bounds +{ + RCTCornerRadii radii = [self radiiForBounds:bounds]; + + CALayer *mask = nil; + CGFloat cornerRadius = 0; + + if (RCTCornerRadiiAreEqual(radii)) { + cornerRadius = radii.topLeft; + } else { + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + RCTCornerInsets cornerInsets = RCTGetCornerInsets(radii, UIEdgeInsetsZero); + CGPathRef path = RCTPathCreateWithRoundedRect(bounds, cornerInsets, NULL); + shapeLayer.path = path; + CGPathRelease(path); + mask = shapeLayer; + } + + layer.cornerRadius = cornerRadius; + layer.mask = mask; +} + +- (void)updateShadowPathForLayer:(CALayer *)layer bounds:(CGRect)bounds +{ + RCTCornerRadii radii = [self radiiForBounds:bounds]; + + BOOL hasShadow = layer.shadowOpacity * CGColorGetAlpha(layer.shadowColor) > 0; + if (!hasShadow) { + layer.shadowPath = nil; + return; + } + + RCTCornerInsets cornerInsets = RCTGetCornerInsets(radii, UIEdgeInsetsZero); + CGPathRef path = RCTPathCreateWithRoundedRect(bounds, cornerInsets, NULL); + layer.shadowPath = path; + CGPathRelease(path); +} + +- (RCTCornerRadii)radiiForBounds:(CGRect)bounds; +{ + if (!_invalidated && CGRectEqualToRect(_cachedBounds, bounds)) { + return _cachedRadii; + } + + const BOOL isRTL = _layoutDirection == UIUserInterfaceLayoutDirectionRightToLeft; + const CGFloat radius = MAX(0, _radii[RNSharedElementCornerAll]); + RCTCornerRadii result; + + if ([[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) { + const CGFloat topStartRadius = RNSharedElementDefaultIfNegativeTo(_radii[RNSharedElementCornerTopLeft], _radii[RNSharedElementCornerTopStart]); + const CGFloat topEndRadius = RNSharedElementDefaultIfNegativeTo(_radii[RNSharedElementCornerTopRight], _radii[RNSharedElementCornerTopEnd]); + const CGFloat bottomStartRadius = RNSharedElementDefaultIfNegativeTo(_radii[RNSharedElementCornerBottomLeft], _radii[RNSharedElementCornerBottomStart]); + const CGFloat bottomEndRadius = RNSharedElementDefaultIfNegativeTo(_radii[RNSharedElementCornerBottomRight], _radii[RNSharedElementCornerBottomEnd]); + + const CGFloat directionAwareTopLeftRadius = isRTL ? topEndRadius : topStartRadius; + const CGFloat directionAwareTopRightRadius = isRTL ? topStartRadius : topEndRadius; + const CGFloat directionAwareBottomLeftRadius = isRTL ? bottomEndRadius : bottomStartRadius; + const CGFloat directionAwareBottomRightRadius = isRTL ? bottomStartRadius : bottomEndRadius; + + result.topLeft = RNSharedElementDefaultIfNegativeTo(radius, directionAwareTopLeftRadius); + result.topRight = RNSharedElementDefaultIfNegativeTo(radius, directionAwareTopRightRadius); + result.bottomLeft = RNSharedElementDefaultIfNegativeTo(radius, directionAwareBottomLeftRadius); + result.bottomRight = RNSharedElementDefaultIfNegativeTo(radius, directionAwareBottomRightRadius); + } else { + const CGFloat directionAwareTopLeftRadius = isRTL ? _radii[RNSharedElementCornerTopEnd] : _radii[RNSharedElementCornerTopStart]; + const CGFloat directionAwareTopRightRadius = isRTL ? _radii[RNSharedElementCornerTopStart] : _radii[RNSharedElementCornerTopEnd]; + const CGFloat directionAwareBottomLeftRadius = isRTL ? _radii[RNSharedElementCornerBottomEnd] : _radii[RNSharedElementCornerBottomStart]; + const CGFloat directionAwareBottomRightRadius = isRTL ? _radii[RNSharedElementCornerBottomStart] : _radii[RNSharedElementCornerBottomEnd]; + + result.topLeft = + RNSharedElementDefaultIfNegativeTo(radius, RNSharedElementDefaultIfNegativeTo(_radii[RNSharedElementCornerTopLeft], directionAwareTopLeftRadius)); + result.topRight = + RNSharedElementDefaultIfNegativeTo(radius, RNSharedElementDefaultIfNegativeTo(_radii[RNSharedElementCornerTopRight], directionAwareTopRightRadius)); + result.bottomLeft = + RNSharedElementDefaultIfNegativeTo(radius, RNSharedElementDefaultIfNegativeTo(_radii[RNSharedElementCornerBottomLeft], directionAwareBottomLeftRadius)); + result.bottomRight = RNSharedElementDefaultIfNegativeTo( + radius, RNSharedElementDefaultIfNegativeTo(_radii[RNSharedElementCornerBottomRight], directionAwareBottomRightRadius)); + } + + // Get scale factors required to prevent radii from overlapping + const CGFloat topScaleFactor = RCTZeroIfNaN(MIN(1, bounds.size.width / (result.topLeft + result.topRight))); + const CGFloat bottomScaleFactor = RCTZeroIfNaN(MIN(1, bounds.size.width / (result.bottomLeft + result.bottomRight))); + const CGFloat rightScaleFactor = RCTZeroIfNaN(MIN(1, bounds.size.height / (result.topRight + result.bottomRight))); + const CGFloat leftScaleFactor = RCTZeroIfNaN(MIN(1, bounds.size.height / (result.topLeft + result.bottomLeft))); + + result.topLeft *= MIN(topScaleFactor, leftScaleFactor); + result.topRight *= MIN(topScaleFactor, rightScaleFactor); + result.bottomLeft *= MIN(bottomScaleFactor, leftScaleFactor); + result.bottomRight *= MIN(bottomScaleFactor, rightScaleFactor); + + _cachedBounds = bounds; + _cachedRadii = result; + _invalidated = NO; + + return result; +} + +@end diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementNode.m b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementNode.m index e4890fd7f7993..214b1eb4d0f2e 100644 --- a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementNode.m +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementNode.m @@ -17,38 +17,38 @@ - (instancetype)initWithView:(UIView*) view hasChildImageView:(BOOL)hasChildImag @implementation RNSharedElementNodeResolvedSource - (instancetype)initWithView:(UIView*) view hasChildImageView:(BOOL)hasChildImageView { - _view = view; - _hasChildImageView = hasChildImageView; - return self; + _view = view; + _hasChildImageView = hasChildImageView; + return self; } + (instancetype)sourceWithView:(UIView*) view { - return [[RNSharedElementNodeResolvedSource alloc]initWithView:view hasChildImageView:NO]; + return [[RNSharedElementNodeResolvedSource alloc]initWithView:view hasChildImageView:NO]; } + (instancetype)sourceWithView:(UIView*) view hasChildImageView:(BOOL)hasChildImageView { - return [[RNSharedElementNodeResolvedSource alloc]initWithView:view hasChildImageView:hasChildImageView]; + return [[RNSharedElementNodeResolvedSource alloc]initWithView:view hasChildImageView:hasChildImageView]; } - (UIView*) contentView { - return (_hasChildImageView && _view.subviews.count) ? _view.subviews[0] : _view; + return (_hasChildImageView && _view.subviews.count) ? _view.subviews[0] : _view; } @end @implementation RNSharedElementNode { - long _refCount; - long _hideRefCount; - - NSMutableArray* _contentRequests; - RNSharedElementContent* _contentCache; - CFTimeInterval _contentCacheTimeInterval; - - NSMutableArray* _styleRequests; - RNSharedElementStyle* _styleCache; - CFTimeInterval _styleCacheTimeInterval; - - CADisplayLink* _displayLink; - - __weak UIView* _sourceView; - RNSharedElementNodeResolvedSource* _resolvedSource; + long _refCount; + long _hideRefCount; + + NSMutableArray* _contentRequests; + RNSharedElementContent* _contentCache; + CFTimeInterval _contentCacheTimeInterval; + + NSMutableArray* _styleRequests; + RNSharedElementStyle* _styleCache; + CFTimeInterval _styleCacheTimeInterval; + + CADisplayLink* _displayLink; + + __weak UIView* _sourceView; + RNSharedElementNodeResolvedSource* _resolvedSource; } @synthesize reactTag = _reactTag; @@ -57,342 +57,342 @@ @implementation RNSharedElementNode + (void) setImageResolvers:(NSArray*) imageResolvers { - _imageResolvers = imageResolvers; + _imageResolvers = imageResolvers; } - (instancetype)init:(NSNumber *)reactTag view:(UIView*) view isParent:(BOOL)isParent { - _reactTag = reactTag; - _sourceView = view; - _isParent = isParent; - _refCount = 1; - _hideRefCount = 0; - _contentRequests = nil; - _contentCache = nil; - _contentCacheTimeInterval = 0.0; - _styleRequests = nil; - _styleCache = nil; - _styleCacheTimeInterval = 0.0; - _displayLink = nil; - _resolvedSource = [RNSharedElementNodeResolvedSource sourceWithView:nil]; - [self updateResolvedSource:YES]; - if (_isParent) [self addStyleObservers:_sourceView]; - return self; + _reactTag = reactTag; + _sourceView = view; + _isParent = isParent; + _refCount = 1; + _hideRefCount = 0; + _contentRequests = nil; + _contentCache = nil; + _contentCacheTimeInterval = 0.0; + _styleRequests = nil; + _styleCache = nil; + _styleCacheTimeInterval = 0.0; + _displayLink = nil; + _resolvedSource = [RNSharedElementNodeResolvedSource sourceWithView:nil]; + [self updateResolvedSource:YES]; + if (_isParent) [self addStyleObservers:_sourceView]; + return self; } - (UIView*) view { - return _resolvedSource ? _resolvedSource.view : nil; + return _resolvedSource ? _resolvedSource.view : nil; } - (void) updateResolvedSource:(BOOL)noReset { - RNSharedElementNodeResolvedSource* resolvedSource = noReset - ? [RNSharedElementNode resolveSource:_isParent ? _sourceView.subviews.firstObject : _sourceView] - : [RNSharedElementNodeResolvedSource sourceWithView:nil]; - - if ((_resolvedSource.view == resolvedSource.view) && (_resolvedSource.contentView == resolvedSource.contentView)) return; - - // Remove old observers - if (_resolvedSource.view != nil && _resolvedSource.view != resolvedSource.view) { - if (_hideRefCount) _resolvedSource.view.hidden = NO; - [self removeStyleObservers: _resolvedSource.view]; - } - if (_resolvedSource.contentView != nil && _resolvedSource.contentView != resolvedSource.contentView) { - [self removeContentObservers: _resolvedSource.contentView]; - } - - // Add new observers - if (resolvedSource.view != nil && _resolvedSource.view != resolvedSource.view) { - if (_hideRefCount) resolvedSource.view.hidden = YES; - [self addStyleObservers:resolvedSource.view]; - } - if (resolvedSource.contentView != nil && _resolvedSource.contentView != resolvedSource.contentView) { - [self addContentObservers:resolvedSource.contentView]; - } - - // Update resolved source - _resolvedSource = resolvedSource; + RNSharedElementNodeResolvedSource* resolvedSource = noReset + ? [RNSharedElementNode resolveSource:_isParent ? _sourceView.subviews.firstObject : _sourceView] + : [RNSharedElementNodeResolvedSource sourceWithView:nil]; + + if ((_resolvedSource.view == resolvedSource.view) && (_resolvedSource.contentView == resolvedSource.contentView)) return; + + // Remove old observers + if (_resolvedSource.view != nil && _resolvedSource.view != resolvedSource.view) { + if (_hideRefCount) _resolvedSource.view.hidden = NO; + [self removeStyleObservers: _resolvedSource.view]; + } + if (_resolvedSource.contentView != nil && _resolvedSource.contentView != resolvedSource.contentView) { + [self removeContentObservers: _resolvedSource.contentView]; + } + + // Add new observers + if (resolvedSource.view != nil && _resolvedSource.view != resolvedSource.view) { + if (_hideRefCount) resolvedSource.view.hidden = YES; + [self addStyleObservers:resolvedSource.view]; + } + if (resolvedSource.contentView != nil && _resolvedSource.contentView != resolvedSource.contentView) { + [self addContentObservers:resolvedSource.contentView]; + } + + // Update resolved source + _resolvedSource = resolvedSource; } + (RNSharedElementNodeResolvedSource*) resolveSource:(UIView*) view { - if (view == nil || _imageResolvers == nil) return [RNSharedElementNodeResolvedSource sourceWithView:view]; - - // If the view is an ImageView, then use that. - if ([RNSharedElementContent isKindOfImageView:view]) { - return [RNSharedElementNodeResolvedSource sourceWithView:view]; - } - - // In case the view contains a single UIImageView child - // which is also the same size as the parent, then - // use child image-view. This fixes . - UIView* subview = view; - for (int i = 0; i < 2; i++) { - if (subview.subviews.count != 1) break; - subview = subview.subviews.firstObject; - if ([RNSharedElementContent isKindOfImageView:subview]) { - CGRect bounds = view.bounds; - if ([view isKindOfClass:[RCTView class]]) { - RCTView* rctView = (RCTView*) view; - CGFloat borderWidth = rctView.borderWidth; - if (borderWidth > 0.0f) { - bounds.origin.x += borderWidth; - bounds.origin.y += borderWidth; - bounds.size.width -= (borderWidth * 2.0f); - bounds.size.height -= (borderWidth * 2.0f); - } - } - if (CGRectEqualToRect(subview.frame, bounds)) { - //NSLog(@"RESOLVED IMAGE VIEW, frame: %@, bounds: %@", NSStringFromCGRect(subview.frame), NSStringFromCGRect(bounds)); - return [RNSharedElementNodeResolvedSource sourceWithView:view hasChildImageView:YES]; - } + if (view == nil || _imageResolvers == nil) return [RNSharedElementNodeResolvedSource sourceWithView:view]; + + // If the view is an ImageView, then use that. + if ([RNSharedElementContent isKindOfImageView:view]) { + return [RNSharedElementNodeResolvedSource sourceWithView:view]; + } + + // In case the view contains a single UIImageView child + // which is also the same size as the parent, then + // use child image-view. This fixes . + UIView* subview = view; + for (int i = 0; i < 2; i++) { + if (subview.subviews.count != 1) break; + subview = subview.subviews.firstObject; + if ([RNSharedElementContent isKindOfImageView:subview]) { + CGRect bounds = view.bounds; + if ([view isKindOfClass:[RCTView class]]) { + RCTView* rctView = (RCTView*) view; + CGFloat borderWidth = rctView.borderWidth; + if (borderWidth > 0.0f) { + bounds.origin.x += borderWidth; + bounds.origin.y += borderWidth; + bounds.size.width -= (borderWidth * 2.0f); + bounds.size.height -= (borderWidth * 2.0f); } + } + if (CGRectEqualToRect(subview.frame, bounds)) { + //NSLog(@"RESOLVED IMAGE VIEW, frame: %@, bounds: %@", NSStringFromCGRect(subview.frame), NSStringFromCGRect(bounds)); + return [RNSharedElementNodeResolvedSource sourceWithView:view hasChildImageView:YES]; + } } - - // Resolve the underlying ImageViews of well known - // react-native libs (e.g. react-native-fast-image) - for (NSArray* imageResolver in _imageResolvers) { - NSArray* subviews = @[view]; - UIView* foundImageView = nil; - for (NSString* name in imageResolver) { - foundImageView = nil; - for (UIView* subview in subviews) { - if ([name isEqualToString:NSStringFromClass(subview.class)]) { - foundImageView = subview; - subviews = subview.subviews; - break; - } - } - } - if (foundImageView != nil) { - return [RNSharedElementNodeResolvedSource sourceWithView:foundImageView]; + } + + // Resolve the underlying ImageViews of well known + // react-native libs (e.g. react-native-fast-image) + for (NSArray* imageResolver in _imageResolvers) { + NSArray* subviews = @[view]; + UIView* foundImageView = nil; + for (NSString* name in imageResolver) { + foundImageView = nil; + for (UIView* subview in subviews) { + if ([name isEqualToString:NSStringFromClass(subview.class)]) { + foundImageView = subview; + subviews = subview.subviews; + break; } + } } - return [RNSharedElementNodeResolvedSource sourceWithView:view]; + if (foundImageView != nil) { + return [RNSharedElementNodeResolvedSource sourceWithView:foundImageView]; + } + } + return [RNSharedElementNodeResolvedSource sourceWithView:view]; } - (void) addStyleObservers:(UIView*)view { - [view addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; - [view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; + [view addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; + [view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; } - (void) removeStyleObservers:(UIView*)view { - [view removeObserver:self forKeyPath:@"bounds"]; - [view removeObserver:self forKeyPath:@"frame"]; + [view removeObserver:self forKeyPath:@"bounds"]; + [view removeObserver:self forKeyPath:@"frame"]; } - (void) addContentObservers:(UIView*)view { - if ([RNSharedElementContent isKindOfImageView:view]) { - UIImageView* imageView = [RNSharedElementContent imageViewFromView:view]; - [imageView addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; - } + if ([RNSharedElementContent isKindOfImageView:view]) { + UIImageView* imageView = [RNSharedElementContent imageViewFromView:view]; + [imageView addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; + } } - (void) removeContentObservers:(UIView*)view { - if ([RNSharedElementContent isKindOfImageView:view]) { - UIImageView* imageView = [RNSharedElementContent imageViewFromView:view]; - [imageView removeObserver:self forKeyPath:@"image"]; - } + if ([RNSharedElementContent isKindOfImageView:view]) { + UIImageView* imageView = [RNSharedElementContent imageViewFromView:view]; + [imageView removeObserver:self forKeyPath:@"image"]; + } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - //NSLog(@"observeValueForKeyPath: %@, changed: %@", keyPath, change); - if ([keyPath isEqualToString:@"image"]) { - [self updateContent]; - } else { - [self updateStyle]; - } + //NSLog(@"observeValueForKeyPath: %@, changed: %@", keyPath, change); + if ([keyPath isEqualToString:@"image"]) { + [self updateContent]; + } else { + [self updateStyle]; + } } - (void) setRefCount:(long)refCount { - _refCount = refCount; - if (_refCount == 0) { - if (_displayLink != nil) { - [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; - _displayLink = nil; - } - [self updateResolvedSource:NO]; - if (_isParent && (_sourceView != nil)) { - [self removeStyleObservers:_sourceView]; - } + _refCount = refCount; + if (_refCount == 0) { + if (_displayLink != nil) { + [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + _displayLink = nil; + } + [self updateResolvedSource:NO]; + if (_isParent && (_sourceView != nil)) { + [self removeStyleObservers:_sourceView]; } + } } - (long) refCount { - return _refCount; + return _refCount; } - (void) setHideRefCount:(long)refCount { - _hideRefCount = refCount; - if (_hideRefCount == 1) { - if (_resolvedSource.view != nil) _resolvedSource.view.hidden = YES; - } - else if (_hideRefCount == 0) { - if (_resolvedSource.view != nil) _resolvedSource.view.hidden = NO; - } + _hideRefCount = refCount; + if (_hideRefCount == 1) { + if (_resolvedSource.view != nil) _resolvedSource.view.hidden = YES; + } + else if (_hideRefCount == 0) { + if (_resolvedSource.view != nil) _resolvedSource.view.hidden = NO; + } } - (long) hideRefCount { - return _hideRefCount; + return _hideRefCount; } - (void) requestContent:(__weak id ) delegate { - if (_contentCache != nil && ((CACurrentMediaTime() - _contentCacheTimeInterval) <= 0.3)) { - [delegate didLoadContent:_contentCache node:self]; - return; - } - - if (_contentRequests == nil) _contentRequests = [[NSMutableArray alloc]init]; - [_contentRequests addObject:delegate]; - - [self updateContent]; + if (_contentCache != nil && ((CACurrentMediaTime() - _contentCacheTimeInterval) <= 0.3)) { + [delegate didLoadContent:_contentCache node:self]; + return; + } + + if (_contentRequests == nil) _contentRequests = [[NSMutableArray alloc]init]; + [_contentRequests addObject:delegate]; + + [self updateContent]; } - (void) updateContent { - // Update the resolved source - [self updateResolvedSource:YES]; - RNSharedElementNodeResolvedSource* resolvedSource = _resolvedSource; - UIView* view = resolvedSource.view; - UIView* contentView = resolvedSource.contentView; - if (view == nil) return; - if (_contentRequests == nil) return; - - CGRect bounds = view.bounds; - CGRect frame = contentView.frame; - if (!bounds.size.width || !bounds.size.height) { - return; - } - - // Obtain snapshot content - RNSharedElementContent* content; - if ([RNSharedElementContent isKindOfImageView:contentView]) { - UIImageView* imageView = [RNSharedElementContent imageViewFromView:contentView]; - UIImage* image = imageView.image; - UIEdgeInsets imageInsets = UIEdgeInsetsZero; - if (contentView != view) { - imageInsets.left = frame.origin.x; - imageInsets.top = frame.origin.y; - imageInsets.right = bounds.size.width - frame.size.width - frame.origin.x; - imageInsets.bottom = bounds.size.height - frame.size.height - frame.origin.y; - } - content = [[RNSharedElementContent alloc]initWithData:image type:RNSharedElementContentTypeRawImage insets:imageInsets]; - } - else if ([NSStringFromClass(view.class) isEqualToString:@"RCTView"] && !view.subviews.count) { - UIView* dummyView = [[UIView alloc]init]; - content = [[RNSharedElementContent alloc]initWithData:dummyView type:RNSharedElementContentTypeSnapshotView insets:UIEdgeInsetsZero]; - } - else { - UIView* snapshotView = [view snapshotViewAfterScreenUpdates:NO]; - content = [[RNSharedElementContent alloc]initWithData:snapshotView type:RNSharedElementContentTypeSnapshotView insets:UIEdgeInsetsZero]; - } - /*else { - NSLog(@"drawViewHierarchyInRect: bounds: %@", NSStringFromCGRect(bounds)); - UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0.0f); - BOOL res = [view drawViewHierarchyInRect:bounds afterScreenUpdates:NO]; // NEVER USE YES, IT CREATED VISUAL ARTEFACTS ON THE CREEN - UIImage* image = res ? UIGraphicsGetImageFromCurrentImageContext() : nil; - UIGraphicsEndImageContext(); - NSLog(@"drawViewHierarchyInRect: RESULT: %li", res); - content = image; - contentType = RNSharedElementContentTypeSnapshotImage; - }*/ - - // If the content could not be obtained, then try again later - if (content == nil || content.data == nil) { - if (_displayLink == nil) { - _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLinkUpdate:)]; - [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; - } - return; + // Update the resolved source + [self updateResolvedSource:YES]; + RNSharedElementNodeResolvedSource* resolvedSource = _resolvedSource; + UIView* view = resolvedSource.view; + UIView* contentView = resolvedSource.contentView; + if (view == nil) return; + if (_contentRequests == nil) return; + + CGRect bounds = view.bounds; + CGRect frame = contentView.frame; + if (!bounds.size.width || !bounds.size.height) { + return; + } + + // Obtain snapshot content + RNSharedElementContent* content; + if ([RNSharedElementContent isKindOfImageView:contentView]) { + UIImageView* imageView = [RNSharedElementContent imageViewFromView:contentView]; + UIImage* image = imageView.image; + UIEdgeInsets imageInsets = UIEdgeInsetsZero; + if (contentView != view) { + imageInsets.left = frame.origin.x; + imageInsets.top = frame.origin.y; + imageInsets.right = bounds.size.width - frame.size.width - frame.origin.x; + imageInsets.bottom = bounds.size.height - frame.size.height - frame.origin.y; } - //NSLog(@"Content fetched: %@, contentType: %d, size: %@", content, contentType, NSStringFromCGSize(bounds.size)); - - _contentCache = content; - _contentCacheTimeInterval = CACurrentMediaTime(); - - NSArray* delegates = _contentRequests; - _contentRequests = nil; - if (_displayLink != nil) { - [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; - _displayLink = nil; + content = [[RNSharedElementContent alloc]initWithData:image type:RNSharedElementContentTypeRawImage insets:imageInsets]; + } + else if ([NSStringFromClass(view.class) isEqualToString:@"RCTView"] && !view.subviews.count) { + UIView* dummyView = [[UIView alloc]init]; + content = [[RNSharedElementContent alloc]initWithData:dummyView type:RNSharedElementContentTypeSnapshotView insets:UIEdgeInsetsZero]; + } + else { + UIView* snapshotView = [view snapshotViewAfterScreenUpdates:NO]; + content = [[RNSharedElementContent alloc]initWithData:snapshotView type:RNSharedElementContentTypeSnapshotView insets:UIEdgeInsetsZero]; + } + /*else { + NSLog(@"drawViewHierarchyInRect: bounds: %@", NSStringFromCGRect(bounds)); + UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0.0f); + BOOL res = [view drawViewHierarchyInRect:bounds afterScreenUpdates:NO]; // NEVER USE YES, IT CREATED VISUAL ARTEFACTS ON THE CREEN + UIImage* image = res ? UIGraphicsGetImageFromCurrentImageContext() : nil; + UIGraphicsEndImageContext(); + NSLog(@"drawViewHierarchyInRect: RESULT: %li", res); + content = image; + contentType = RNSharedElementContentTypeSnapshotImage; + }*/ + + // If the content could not be obtained, then try again later + if (content == nil || content.data == nil) { + if (_displayLink == nil) { + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLinkUpdate:)]; + [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; } - for (__weak id delegate in delegates) { - if (delegate != nil) { - [delegate didLoadContent:content node:self]; - } + return; + } + //NSLog(@"Content fetched: %@, contentType: %d, size: %@", content, contentType, NSStringFromCGSize(bounds.size)); + + _contentCache = content; + _contentCacheTimeInterval = CACurrentMediaTime(); + + NSArray* delegates = _contentRequests; + _contentRequests = nil; + if (_displayLink != nil) { + [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + _displayLink = nil; + } + for (__weak id delegate in delegates) { + if (delegate != nil) { + [delegate didLoadContent:content node:self]; } + } } - (void)onDisplayLinkUpdate:(CADisplayLink *)sender { - // NSLog(@"onDisplayLinkUpdate"); - [self updateContent]; + // NSLog(@"onDisplayLinkUpdate"); + [self updateContent]; } - (void) requestStyle:(__weak id ) delegate { - if (_styleCache != nil && ((CACurrentMediaTime() - _styleCacheTimeInterval) <= 0.3)) { - [delegate didLoadStyle:_styleCache node:self]; - return; - } - - if (_styleRequests == nil) _styleRequests = [[NSMutableArray alloc]init]; - [_styleRequests addObject:delegate]; - - [self updateStyle]; + if (_styleCache != nil && ((CACurrentMediaTime() - _styleCacheTimeInterval) <= 0.3)) { + [delegate didLoadStyle:_styleCache node:self]; + return; + } + + if (_styleRequests == nil) _styleRequests = [[NSMutableArray alloc]init]; + [_styleRequests addObject:delegate]; + + [self updateStyle]; } - (void) updateStyle { - [self updateResolvedSource:YES]; - RNSharedElementNodeResolvedSource* resolvedSource = _resolvedSource; - UIView* view = resolvedSource.view; - UIView* contentView = resolvedSource.contentView; - if (_styleRequests == nil) return; - if (view == nil) return; - - // Get absolute layout - CGRect layout = [view convertRect:view.bounds toView:nil]; - if (CGRectIsEmpty(layout)) return; - - // Create style - RNSharedElementStyle* style = [[RNSharedElementStyle alloc]initWithView:view]; - style.layout = layout; - if ([RNSharedElementContent isKindOfImageView:contentView]) { - UIImageView* imageView = [RNSharedElementContent imageViewFromView:contentView]; - style.contentMode = imageView.contentMode; - } else { - style.contentMode = view.contentMode; - } - /*if (_isParent) { - NSLog(@"Style fetched: %@, realSize: %@, opacity: %lf, transform: %@, borderWidth: %lf, contentMode: %ld", NSStringFromCGRect(layout), NSStringFromCGSize(style.size), style.opacity, [RNSharedElementStyle stringFromTransform:style.transform], style.borderWidth, style.contentMode); - }*/ - - _styleCache = style; - _styleCacheTimeInterval = CACurrentMediaTime(); - - NSArray* delegates = _styleRequests; - _styleRequests = nil; - for (__weak id delegate in delegates) { - if (delegate != nil) { - [delegate didLoadStyle:style node:self]; - } + [self updateResolvedSource:YES]; + RNSharedElementNodeResolvedSource* resolvedSource = _resolvedSource; + UIView* view = resolvedSource.view; + UIView* contentView = resolvedSource.contentView; + if (_styleRequests == nil) return; + if (view == nil) return; + + // Get absolute layout + CGRect layout = [view convertRect:view.bounds toView:nil]; + if (CGRectIsEmpty(layout)) return; + + // Create style + RNSharedElementStyle* style = [[RNSharedElementStyle alloc]initWithView:view]; + style.layout = layout; + if ([RNSharedElementContent isKindOfImageView:contentView]) { + UIImageView* imageView = [RNSharedElementContent imageViewFromView:contentView]; + style.contentMode = imageView.contentMode; + } else { + style.contentMode = view.contentMode; + } + /*if (_isParent) { + NSLog(@"Style fetched: %@, realSize: %@, opacity: %lf, transform: %@, borderWidth: %lf, contentMode: %ld", NSStringFromCGRect(layout), NSStringFromCGSize(style.size), style.opacity, [RNSharedElementStyle stringFromTransform:style.transform], style.borderWidth, style.contentMode); + }*/ + + _styleCache = style; + _styleCacheTimeInterval = CACurrentMediaTime(); + + NSArray* delegates = _styleRequests; + _styleRequests = nil; + for (__weak id delegate in delegates) { + if (delegate != nil) { + [delegate didLoadStyle:style node:self]; } + } } - (void) cancelRequests:(id ) delegate { - if (_styleRequests != nil) [_styleRequests removeObject:delegate]; - if (_contentRequests != nil) [_contentRequests removeObject:delegate]; + if (_styleRequests != nil) [_styleRequests removeObject:delegate]; + if (_contentRequests != nil) [_contentRequests removeObject:delegate]; } @end diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementNodeManager.m b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementNodeManager.m index e3cc5d39eefb8..61ac1c50166a8 100644 --- a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementNodeManager.m +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementNodeManager.m @@ -26,7 +26,7 @@ - (RNSharedElementNode*) acquire:(NSNumber*) reactTag view:(UIView*)view isParen node.refCount = node.refCount + 1; return node; } - node = [[RNSharedElementNode alloc]init:reactTag view:view isParent:isParent]; + node = [[RNSharedElementNode alloc]init:reactTag view:view isParent:isParent]; [_items setObject:node forKey:reactTag]; return node; } diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementStyle.h b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementStyle.h index cbc86e6ea204e..ce8c44e443bf3 100644 --- a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementStyle.h +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementStyle.h @@ -7,22 +7,23 @@ #define RNSharedElementStyle_h #import +#import "RNSharedElementCornerRadii.h" @interface RNSharedElementStyle : NSObject -@property (nonatomic, assign) UIView* view; +@property (nonatomic, weak) UIView *view; @property (nonatomic, assign) CGRect layout; @property (nonatomic, assign) CGSize size; @property (nonatomic, assign) CATransform3D transform; @property (nonatomic, assign) UIViewContentMode contentMode; @property (nonatomic, assign) CGFloat opacity; -@property (nonatomic, assign) UIColor* backgroundColor; -@property (nonatomic, assign) CGFloat cornerRadius; +@property (nonatomic, strong) UIColor *backgroundColor; +@property (nonatomic, readonly) RNSharedElementCornerRadii *cornerRadii; @property (nonatomic, assign) CGFloat borderWidth; -@property (nonatomic, assign) UIColor* borderColor; +@property (nonatomic, strong) UIColor *borderColor; @property (nonatomic, assign) CGFloat shadowOpacity; @property (nonatomic, assign) CGFloat shadowRadius; @property (nonatomic, assign) CGSize shadowOffset; -@property (nonatomic, assign) UIColor* shadowColor; +@property (nonatomic, strong) UIColor *shadowColor; - (instancetype)init; - (instancetype)initWithView:(UIView*) view; diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementStyle.m b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementStyle.m index c407bab78e60a..bfdf9f3f82ce3 100644 --- a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementStyle.m +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementStyle.m @@ -7,19 +7,18 @@ #import @implementation RNSharedElementStyle -{ - UIColor* _backgroundColor; - UIColor* _borderColor; - UIColor* _shadowColor; -} - (instancetype)init { - return self; + if ((self = [super init])) { + _cornerRadii = [RNSharedElementCornerRadii new]; + } + return self; } - (instancetype)initWithView:(UIView*) view { + if ((self = [super init])) { _view = view; _size = view.bounds.size; _transform = [RNSharedElementStyle getAbsoluteViewTransform:view]; @@ -27,7 +26,6 @@ - (instancetype)initWithView:(UIView*) view // Set base props from style CALayer* layer = view.layer; _opacity = layer.opacity; - _cornerRadius = layer.cornerRadius; _borderWidth = layer.borderWidth; _borderColor = layer.borderColor ? [UIColor colorWithCGColor:layer.borderColor] : [UIColor clearColor]; _backgroundColor = layer.backgroundColor ? [UIColor colorWithCGColor:layer.backgroundColor] : [UIColor clearColor]; @@ -40,112 +38,105 @@ - (instancetype)initWithView:(UIView*) view // when a borderWidth is set on the view. Therefore, as a fail-safe we also try to // get the props from the RCTView directly, when possible. if ([view isKindOfClass:[RCTView class]]) { - RCTView* rctView = (RCTView*) view; - _cornerRadius = rctView.borderRadius; - _borderColor = rctView.borderColor ? [UIColor colorWithCGColor:rctView.borderColor] : [UIColor clearColor]; - _borderWidth = rctView.borderWidth >= 0.0f ? rctView.borderWidth : 0.0f; - _backgroundColor = rctView.backgroundColor ? rctView.backgroundColor : [UIColor clearColor]; + RCTView* rctView = (RCTView*) view; + _cornerRadii = [RNSharedElementStyle cornerRadiiFromRCTView: rctView]; + _borderColor = rctView.borderColor ? [UIColor colorWithCGColor:rctView.borderColor] : [UIColor clearColor]; + _borderWidth = rctView.borderWidth >= 0.0f ? rctView.borderWidth : 0.0f; + _backgroundColor = rctView.backgroundColor ? rctView.backgroundColor : [UIColor clearColor]; + } else { + _cornerRadii = [RNSharedElementCornerRadii new]; + [_cornerRadii setRadius:layer.cornerRadius corner:RNSharedElementCornerAll]; } - - return self; -} - -- (void) setBackgroundColor:(UIColor*)backgroundColor { - _backgroundColor = backgroundColor; -} -- (UIColor*) backgroundColor -{ - return _backgroundColor; -} - -- (void) setBorderColor:(UIColor*)borderColor { - _borderColor = borderColor; -} -- (UIColor*) borderColor -{ - return _borderColor; -} - -- (void) setShadowColor:(UIColor*)shadowColor { - _shadowColor = shadowColor; -} - -- (UIColor*)shadowColor -{ - return _shadowColor; + } + + return self; } + (NSString*) stringFromTransform:(CATransform3D) transform { + BOOL isAffine = CATransform3DIsAffine(transform); + if (isAffine) { + CGAffineTransform affine = CATransform3DGetAffineTransform(transform); + return [NSString stringWithFormat:@"tx=%f, ty=%f, sx=%f, sy=%f, ro=%f", + affine.tx, affine.ty, affine.a, affine.d, atan2f(affine.b, affine.a) * (180 / M_PI)]; + } else { return [NSString stringWithFormat:@"x=%f, y=%f, z=%f", transform.m41, transform.m42, transform.m43]; + } } + (CATransform3D) getAbsoluteViewTransform:(UIView*) view { - CATransform3D transform = view.layer.transform; + CATransform3D transform = view.layer.transform; + view = view.superview; + while (view != nil) { + transform = CATransform3DConcat(transform, view.layer.transform); view = view.superview; - while (view != nil) { - CATransform3D t2 = view.layer.transform; - // Other transform props are not needed for now, maybe support them later - /*transform.m11 *= t2.m11; - transform.m12 *= t2.m12; - transform.m13 *= t2.m13; - transform.m14 *= t2.m14; - transform.m21 *= t2.m21; - transform.m22 *= t2.m22; - transform.m23 *= t2.m23; - transform.m24 *= t2.m24; - transform.m31 *= t2.m31; - transform.m32 *= t2.m32; - transform.m33 *= t2.m33; - transform.m34 *= t2.m34;*/ - transform.m41 += t2.m41; // translateX - transform.m42 += t2.m42; // translateY - transform.m43 += t2.m43; // translateZ - //transform.m44 *= t2.m44; - view = view.superview; - } - return transform; + } + return transform; } + (UIColor*) getInterpolatedColor:(UIColor*)color1 color2:(UIColor*)color2 position:(CGFloat)position { - CGFloat red1, green1, blue1, alpha1; - CGFloat red2, green2, blue2, alpha2; - [color1 getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1]; - [color2 getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2]; - CGFloat alpha = alpha1 + ((alpha2 - alpha1) * position); - CGFloat red = red1 + ((red2 - red1) * position); - CGFloat green = green1 + ((green2 - green1) * position); - CGFloat blue = blue1 + ((blue2 - blue1) * position); - if ((alpha1 == 0.0f) && (alpha2 != 0.0f)) { - red = red2; - green = green2; - blue = blue2; - } else if ((alpha2 == 0.0f) && (alpha1 != 0.0f)) { - red = red1; - green = green1; - blue = blue1; - } - return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; + CGFloat red1, green1, blue1, alpha1; + CGFloat red2, green2, blue2, alpha2; + [color1 getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1]; + [color2 getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2]; + CGFloat alpha = alpha1 + ((alpha2 - alpha1) * position); + CGFloat red = red1 + ((red2 - red1) * position); + CGFloat green = green1 + ((green2 - green1) * position); + CGFloat blue = blue1 + ((blue2 - blue1) * position); + if ((alpha1 == 0.0f) && (alpha2 != 0.0f)) { + red = red2; + green = green2; + blue = blue2; + } else if ((alpha2 == 0.0f) && (alpha1 != 0.0f)) { + red = red1; + green = green1; + blue = blue1; + } + return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; } + (RNSharedElementStyle*) getInterpolatedStyle:(RNSharedElementStyle*)style1 style2:(RNSharedElementStyle*)style2 position:(CGFloat) position { - RNSharedElementStyle* style = [[RNSharedElementStyle alloc]init]; - style.opacity = style1.opacity + ((style2.opacity - style1.opacity) * position); - style.cornerRadius = style1.cornerRadius + ((style2.cornerRadius - style1.cornerRadius) * position); - style.borderWidth = style1.borderWidth + ((style2.borderWidth - style1.borderWidth) * position); - style.borderColor = [RNSharedElementStyle getInterpolatedColor:style1.borderColor color2:style2.borderColor position:position]; - style.backgroundColor = [RNSharedElementStyle getInterpolatedColor:style1.backgroundColor color2:style2.backgroundColor position:position]; - style.shadowOpacity = style1.shadowOpacity + ((style2.shadowOpacity - style1.shadowOpacity) * position); - style.shadowRadius = style1.shadowRadius + ((style2.shadowRadius - style1.shadowRadius) * position); - style.shadowOffset = CGSizeMake( - style1.shadowOffset.width + ((style2.shadowOffset.width - style1.shadowOffset.width) * position), - style1.shadowOffset.height + ((style2.shadowOffset.height - style1.shadowOffset.height) * position) - ); - style.shadowColor = [RNSharedElementStyle getInterpolatedColor:style1.shadowColor color2:style2.shadowColor position:position]; - return style; + RNSharedElementStyle* style = [[RNSharedElementStyle alloc]init]; + style.opacity = style1.opacity + ((style2.opacity - style1.opacity) * position); + + CGRect radiiRect = CGRectMake(0, 0, 1000000, 1000000); + RCTCornerRadii radii1 = [style1.cornerRadii radiiForBounds:radiiRect]; + RCTCornerRadii radii2 = [style2.cornerRadii radiiForBounds:radiiRect]; + [style.cornerRadii setRadius:radii1.topLeft + ((radii2.topLeft - radii1.topLeft) * position) corner:RNSharedElementCornerTopLeft]; + [style.cornerRadii setRadius:radii1.topRight + ((radii2.topRight - radii1.topRight) * position) corner:RNSharedElementCornerTopRight]; + [style.cornerRadii setRadius:radii1.bottomLeft + ((radii2.bottomLeft - radii1.bottomLeft) * position) corner:RNSharedElementCornerBottomLeft]; + [style.cornerRadii setRadius:radii1.bottomRight + ((radii2.bottomRight - radii1.bottomRight) * position) corner:RNSharedElementCornerBottomRight]; + + style.borderWidth = style1.borderWidth + ((style2.borderWidth - style1.borderWidth) * position); + style.borderColor = [RNSharedElementStyle getInterpolatedColor:style1.borderColor color2:style2.borderColor position:position]; + style.backgroundColor = [RNSharedElementStyle getInterpolatedColor:style1.backgroundColor color2:style2.backgroundColor position:position]; + style.shadowOpacity = style1.shadowOpacity + ((style2.shadowOpacity - style1.shadowOpacity) * position); + style.shadowRadius = style1.shadowRadius + ((style2.shadowRadius - style1.shadowRadius) * position); + style.shadowOffset = CGSizeMake( + style1.shadowOffset.width + ((style2.shadowOffset.width - style1.shadowOffset.width) * position), + style1.shadowOffset.height + ((style2.shadowOffset.height - style1.shadowOffset.height) * position) + ); + style.shadowColor = [RNSharedElementStyle getInterpolatedColor:style1.shadowColor color2:style2.shadowColor position:position]; + return style; +} + ++ (RNSharedElementCornerRadii *)cornerRadiiFromRCTView:(RCTView *)rctView +{ + RNSharedElementCornerRadii *cornerRadii = [RNSharedElementCornerRadii new]; + [cornerRadii setRadius:[rctView borderRadius] corner:RNSharedElementCornerAll]; + [cornerRadii setRadius:[rctView borderTopLeftRadius] corner:RNSharedElementCornerTopLeft]; + [cornerRadii setRadius:[rctView borderTopRightRadius] corner:RNSharedElementCornerTopRight]; + [cornerRadii setRadius:[rctView borderTopStartRadius] corner:RNSharedElementCornerTopStart]; + [cornerRadii setRadius:[rctView borderTopEndRadius] corner:RNSharedElementCornerTopEnd]; + [cornerRadii setRadius:[rctView borderBottomLeftRadius] corner:RNSharedElementCornerBottomLeft]; + [cornerRadii setRadius:[rctView borderBottomRightRadius] corner:RNSharedElementCornerBottomRight]; + [cornerRadii setRadius:[rctView borderBottomStartRadius] corner:RNSharedElementCornerBottomStart]; + [cornerRadii setRadius:[rctView borderBottomEndRadius] corner:RNSharedElementCornerBottomEnd]; + [cornerRadii setLayoutDirection:[rctView reactLayoutDirection]]; + return cornerRadii; } @end diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransition.m b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransition.m index 86b753218d121..5c014c3ee1b61 100644 --- a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransition.m +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransition.m @@ -24,593 +24,626 @@ @implementation RNSharedElementTransition { - NSArray* _items; - UIView* _outerStyleView; - UIView* _innerClipView; - UIImageView* _primaryImageView; - UIImageView* _secondaryImageView; - BOOL _reactFrameSet; - BOOL _initialLayoutPassCompleted; + NSArray* _items; + UIView* _outerStyleView; + UIView* _innerClipView; + UIImageView* _primaryImageView; + UIImageView* _secondaryImageView; + BOOL _reactFrameSet; + BOOL _initialLayoutPassCompleted; + int _initialVisibleAncestorIndex; } - (instancetype)initWithNodeManager:(RNSharedElementNodeManager*)nodeManager { - if ((self = [super init])) { - _items = @[ - [[RNSharedElementTransitionItem alloc]initWithNodeManager:nodeManager name:@"startAncestor" isAncestor:YES], - [[RNSharedElementTransitionItem alloc]initWithNodeManager:nodeManager name:@"endAncestor" isAncestor:YES], - [[RNSharedElementTransitionItem alloc]initWithNodeManager:nodeManager name:@"startNode" isAncestor:NO], - [[RNSharedElementTransitionItem alloc]initWithNodeManager:nodeManager name:@"endNode" isAncestor:NO] - ]; - _nodePosition = 0.0f; - _animation = RNSharedElementAnimationMove; - _resize = RNSharedElementResizeStretch; - _align = RNSharedElementAlignCenterCenter; - _reactFrameSet = NO; - _initialLayoutPassCompleted = NO; - self.userInteractionEnabled = NO; - - _outerStyleView = [[UIImageView alloc]init]; - _outerStyleView.userInteractionEnabled = NO; - _outerStyleView.frame = self.bounds; - [self addSubview:_outerStyleView]; - - _innerClipView = [[UIImageView alloc]init]; - _innerClipView.userInteractionEnabled = NO; - _innerClipView.frame = self.bounds; - _innerClipView.layer.masksToBounds = YES; - [_outerStyleView addSubview:_innerClipView]; - - _primaryImageView = [self createImageView]; - _secondaryImageView = [self createImageView]; - } + if ((self = [super init])) { + _items = @[ + [[RNSharedElementTransitionItem alloc]initWithNodeManager:nodeManager name:@"startAncestor" isAncestor:YES], + [[RNSharedElementTransitionItem alloc]initWithNodeManager:nodeManager name:@"endAncestor" isAncestor:YES], + [[RNSharedElementTransitionItem alloc]initWithNodeManager:nodeManager name:@"startNode" isAncestor:NO], + [[RNSharedElementTransitionItem alloc]initWithNodeManager:nodeManager name:@"endNode" isAncestor:NO] + ]; + _nodePosition = 0.0f; + _animation = RNSharedElementAnimationMove; + _resize = RNSharedElementResizeStretch; + _align = RNSharedElementAlignCenterCenter; + _reactFrameSet = NO; + _initialLayoutPassCompleted = NO; + _initialVisibleAncestorIndex = -1; + self.userInteractionEnabled = NO; + + _outerStyleView = [[UIImageView alloc]init]; + _outerStyleView.userInteractionEnabled = NO; + _outerStyleView.frame = self.bounds; + [self addSubview:_outerStyleView]; + + _innerClipView = [[UIImageView alloc]init]; + _innerClipView.userInteractionEnabled = NO; + _innerClipView.frame = self.bounds; + _innerClipView.layer.masksToBounds = YES; + [_outerStyleView addSubview:_innerClipView]; - return self; + _primaryImageView = [self createImageView]; + _secondaryImageView = [self createImageView]; + } + + return self; } - (void)removeFromSuperview { - [super removeFromSuperview]; - - for (RNSharedElementTransitionItem* item in _items) { - if (item.node != nil) [item.node cancelRequests:self]; - } + [super removeFromSuperview]; + + for (RNSharedElementTransitionItem* item in _items) { + if (item.node != nil) [item.node cancelRequests:self]; + } } - (void)dealloc { - for (RNSharedElementTransitionItem* item in _items) { - item.node = nil; - } + for (RNSharedElementTransitionItem* item in _items) { + item.node = nil; + } } - (UIImageView*) createImageView { - UIImageView* imageView = [[UIImageView alloc]init]; - imageView.contentMode = UIViewContentModeScaleToFill; - imageView.userInteractionEnabled = NO; - imageView.frame = self.bounds; - return imageView; + UIImageView* imageView = [[UIImageView alloc]init]; + imageView.contentMode = UIViewContentModeScaleToFill; + imageView.userInteractionEnabled = NO; + imageView.frame = self.bounds; + return imageView; } - (RNSharedElementTransitionItem*) findItemForNode:(RNSharedElementNode*) node { - for (RNSharedElementTransitionItem* item in _items) { - if (item.node == node) { - return item; - } + for (RNSharedElementTransitionItem* item in _items) { + if (item.node == node) { + return item; } - return nil; + } + return nil; } - (void)setStartNode:(RNSharedElementNode *)startNode { - ((RNSharedElementTransitionItem*)[_items objectAtIndex:ITEM_START]).node = startNode; + ((RNSharedElementTransitionItem*)[_items objectAtIndex:ITEM_START]).node = startNode; } - (void)setEndNode:(RNSharedElementNode *)endNode { - ((RNSharedElementTransitionItem*)[_items objectAtIndex:ITEM_END]).node = endNode; + ((RNSharedElementTransitionItem*)[_items objectAtIndex:ITEM_END]).node = endNode; } - (void)setStartAncestor:(RNSharedElementNode *)startNodeAncestor { - ((RNSharedElementTransitionItem*)[_items objectAtIndex:ITEM_START_ANCESTOR]).node = startNodeAncestor; + ((RNSharedElementTransitionItem*)[_items objectAtIndex:ITEM_START_ANCESTOR]).node = startNodeAncestor; } - (void)setEndAncestor:(RNSharedElementNode *)endNodeAncestor { - ((RNSharedElementTransitionItem*)[_items objectAtIndex:ITEM_END_ANCESTOR]).node = endNodeAncestor; + ((RNSharedElementTransitionItem*)[_items objectAtIndex:ITEM_END_ANCESTOR]).node = endNodeAncestor; } - (void)setNodePosition:(CGFloat)nodePosition { - if (_nodePosition != nodePosition) { - _nodePosition = nodePosition; - [self updateStyle]; - } + if (_nodePosition != nodePosition) { + _nodePosition = nodePosition; + [self updateStyle]; + } } - (void) setAnimation:(RNSharedElementAnimation)animation { - if (_animation != animation) { - _animation = animation; - [self updateStyle]; - } + if (_animation != animation) { + _animation = animation; + [self updateStyle]; + } } - (void) setResize:(RNSharedElementResize)resize { - if (_resize != resize) { - _resize = resize; - [self updateStyle]; - } + if (_resize != resize) { + _resize = resize; + [self updateStyle]; + } } - (void) setAlign:(RNSharedElementAlign)align { - if (_align != align) { - _align = align; - [self updateStyle]; - } + if (_align != align) { + _align = align; + [self updateStyle]; + } } - (void)updateNodeVisibility { - for (RNSharedElementTransitionItem* item in _items) { - BOOL hidden = _initialLayoutPassCompleted && item.style != nil && item.content != nil; - if (hidden && (_animation == RNSharedElementAnimationFadeIn) && [item.name isEqualToString:@"startNode"]) hidden = NO; - if (hidden && (_animation == RNSharedElementAnimationFadeOut) && [item.name isEqualToString:@"endNode"]) hidden = NO; - item.hidden = hidden; - } + for (RNSharedElementTransitionItem* item in _items) { + BOOL hidden = _initialLayoutPassCompleted && item.style != nil && item.content != nil; + if (hidden && (_animation == RNSharedElementAnimationFadeIn) && [item.name isEqualToString:@"startNode"]) hidden = NO; + if (hidden && (_animation == RNSharedElementAnimationFadeOut) && [item.name isEqualToString:@"endNode"]) hidden = NO; + item.hidden = hidden; + } } - (void) didSetProps:(NSArray *)changedProps { - for (RNSharedElementTransitionItem* item in _items) { - if (_initialLayoutPassCompleted && item.needsLayout) { - item.needsLayout = NO; - [item.node requestStyle:self]; - } + for (RNSharedElementTransitionItem* item in _items) { + if (_initialLayoutPassCompleted && item.needsLayout) { + item.needsLayout = NO; + [item.node requestStyle:self]; } - [self updateNodeVisibility]; + } + [self updateNodeVisibility]; } - (void)updateViewWithImage:(UIImageView*)view image:(UIImage *)image { - if (!image) { - view.image = nil; - return; - } - - // Apply trilinear filtering to smooth out mis-sized images - view.layer.minificationFilter = kCAFilterTrilinear; - view.layer.magnificationFilter = kCAFilterTrilinear; - - // NSLog(@"updateWithImage: %@", NSStringFromCGRect(self.frame)); - view.image = image; + if (!image) { + view.image = nil; + return; + } + + // Apply trilinear filtering to smooth out mis-sized images + view.layer.minificationFilter = kCAFilterTrilinear; + view.layer.magnificationFilter = kCAFilterTrilinear; + + // NSLog(@"updateWithImage: %@", NSStringFromCGRect(self.frame)); + view.image = image; } - (void) didLoadContent:(RNSharedElementContent*)content node:(id)node { - // NSLog(@"didLoadContent: %@", content); - RNSharedElementTransitionItem* item = [self findItemForNode:node]; - if (item == nil) return; - item.content = content; - if ((content.type == RNSharedElementContentTypeSnapshotImage) || (content.type == RNSharedElementContentTypeRawImage)) { - UIImage* image = (UIImage*) content.data; - if (_animation == RNSharedElementAnimationMove) { - if (_primaryImageView.image == nil) { - [self updateViewWithImage:_primaryImageView image:image]; - } else if ((image.size.width * image.size.height) > (_primaryImageView.image.size.width * _primaryImageView.image.size.height)) { - [self updateViewWithImage:_primaryImageView image:image]; - } - } else { - if (item == _items[ITEM_START]) { - [self updateViewWithImage:_primaryImageView image:image]; - } else { - [self updateViewWithImage:_secondaryImageView image:image]; - } - } + // NSLog(@"didLoadContent: %@", content); + RNSharedElementTransitionItem* item = [self findItemForNode:node]; + if (item == nil) return; + item.content = content; + if ((content.type == RNSharedElementContentTypeSnapshotImage) || (content.type == RNSharedElementContentTypeRawImage)) { + UIImage* image = (UIImage*) content.data; + if (_animation == RNSharedElementAnimationMove) { + if (_primaryImageView.image == nil) { + [self updateViewWithImage:_primaryImageView image:image]; + } else if ((image.size.width * image.size.height) > (_primaryImageView.image.size.width * _primaryImageView.image.size.height)) { + [self updateViewWithImage:_primaryImageView image:image]; + } + } else { + if (item == _items[ITEM_START]) { + [self updateViewWithImage:_primaryImageView image:image]; + } else { + [self updateViewWithImage:_secondaryImageView image:image]; + } } - [self updateStyle]; - [self updateNodeVisibility]; + } + [self updateStyle]; + [self updateNodeVisibility]; } - (void) didLoadStyle:(RNSharedElementStyle *)style node:(RNSharedElementNode*)node { - // NSLog(@"didLoadStyle: %@", NSStringFromCGRect(style.layout)); - RNSharedElementTransitionItem* item = [self findItemForNode:node]; - if (item == nil) return; - item.style = style; - [self updateStyle]; - [self updateNodeVisibility]; + // NSLog(@"didLoadStyle: %@", NSStringFromCGRect(style.layout)); + RNSharedElementTransitionItem* item = [self findItemForNode:node]; + if (item == nil) return; + item.style = style; + [self updateStyle]; + [self updateNodeVisibility]; } -- (CGRect)normalizeLayout:(CGRect)layout ancestor:(RNSharedElementTransitionItem*)ancestor +- (CGRect)normalizeLayout:(CGRect)layout + compensateForTransforms:(BOOL)compensateForTransforms + ancestor:(RNSharedElementTransitionItem*)ancestor + otherAncestor:(RNSharedElementTransitionItem*)otherAncestor + { + // Compensate for any transforms that have been applied to the scene by the + // navigator. For instance, a navigator may translate the scene to the right, + // outside of the screen, in order to show it using a slide animation. + // In such a case, remove that transform in order to obtain the "real" + // size and position on the screen. + if (compensateForTransforms && (ancestor.style != nil)) { RNSharedElementStyle* style = ancestor.style; - if (style == nil) return [self.superview convertRect:layout fromView:nil]; - - // Determine origin relative to the left-top of the ancestor - layout.origin.x -= style.transform.m41; - layout.origin.y -= style.transform.m42; - - // Undo any scaling in case the screen is scaled - if (!CGSizeEqualToSize(style.layout.size, style.size)) { - CGFloat scaleX = style.size.width / style.layout.size.width; - CGFloat scaleY = style.size.height / style.layout.size.height; - layout.origin.x *= scaleX; - layout.origin.y *= scaleY; - layout.size.width *= scaleX; - layout.size.height *= scaleY; + RNSharedElementStyle* otherStyle = otherAncestor ? otherAncestor.style : nil; + CATransform3D transform = otherStyle ? CATransform3DConcat(style.transform, CATransform3DInvert(otherStyle.transform)) : style.transform; + if (CATransform3DIsAffine(transform)) { + CGAffineTransform affineTransform = CATransform3DGetAffineTransform(CATransform3DInvert(transform)); + layout = CGRectApplyAffineTransform(layout, affineTransform); + } else { + // Fallback + layout.origin.x -= transform.m41; + layout.origin.y -= transform.m42; } - - return [self.superview convertRect:layout fromView:nil]; + } + + // Convert to render overlay coordinates + return [self.superview convertRect:layout fromView:nil]; } - (CGRect) getInterpolatedLayout:(CGRect)layout1 layout2:(CGRect)layout2 position:(CGFloat) position { - return CGRectMake( - layout1.origin.x + ((layout2.origin.x - layout1.origin.x) * position), - layout1.origin.y + ((layout2.origin.y - layout1.origin.y) * position), - layout1.size.width + ((layout2.size.width - layout1.size.width) * position), - layout1.size.height + ((layout2.size.height - layout1.size.height) * position) - ); + return CGRectMake( + layout1.origin.x + ((layout2.origin.x - layout1.origin.x) * position), + layout1.origin.y + ((layout2.origin.y - layout1.origin.y) * position), + layout1.size.width + ((layout2.size.width - layout1.size.width) * position), + layout1.size.height + ((layout2.size.height - layout1.size.height) * position) + ); } - (UIEdgeInsets) getClipInsets:(CGRect)layout visibleLayout:(CGRect)visibleLayout { - return UIEdgeInsetsMake( - visibleLayout.origin.y - layout.origin.y, - visibleLayout.origin.x - layout.origin.x, - (layout.origin.y + layout.size.height) - (visibleLayout.origin.y + visibleLayout.size.height), - (layout.origin.x + layout.size.width) - (visibleLayout.origin.x + visibleLayout.size.width) - ); + return UIEdgeInsetsMake( + visibleLayout.origin.y - layout.origin.y, + visibleLayout.origin.x - layout.origin.x, + (layout.origin.y + layout.size.height) - (visibleLayout.origin.y + visibleLayout.size.height), + (layout.origin.x + layout.size.width) - (visibleLayout.origin.x + visibleLayout.size.width) + ); } - (UIEdgeInsets) getInterpolatedClipInsets:(CGRect)interpolatedLayout startClipInsets:(UIEdgeInsets)startClipInsets startVisibleLayout:(CGRect)startVisibleLayout endClipInsets:(UIEdgeInsets)endClipInsets endVisibleLayout:(CGRect)endVisibleLayout { - UIEdgeInsets clipInsets = UIEdgeInsetsZero; - - // Top - if (!endClipInsets.top && startClipInsets.top && startVisibleLayout.origin.y <= endVisibleLayout.origin.y) { - clipInsets.top = MAX(0.0f, startVisibleLayout.origin.y - interpolatedLayout.origin.y); - } else if (!startClipInsets.top && endClipInsets.top && endVisibleLayout.origin.y <= startVisibleLayout.origin.y) { - clipInsets.top = MAX(0.0f, endVisibleLayout.origin.y - interpolatedLayout.origin.y); - } else { - clipInsets.top = startClipInsets.top + ((endClipInsets.top - startClipInsets.top) * _nodePosition); - } - - // Bottom - if (!endClipInsets.bottom && startClipInsets.bottom && (startVisibleLayout.origin.y + startVisibleLayout.size.height) >= (endVisibleLayout.origin.y + endVisibleLayout.size.height)) { - clipInsets.bottom = MAX(0.0f, (interpolatedLayout.origin.y + interpolatedLayout.size.height) - (startVisibleLayout.origin.y + startVisibleLayout.size.height)); - } else if (!startClipInsets.bottom && endClipInsets.bottom && (endVisibleLayout.origin.y + endVisibleLayout.size.height) >= (startVisibleLayout.origin.y + startVisibleLayout.size.height)) { - clipInsets.bottom = MAX(0.0f, (interpolatedLayout.origin.y + interpolatedLayout.size.height) - (endVisibleLayout.origin.y + endVisibleLayout.size.height)); - } else { - clipInsets.bottom = startClipInsets.bottom + ((endClipInsets.bottom - startClipInsets.bottom) * _nodePosition); - } - - // Left - if (!endClipInsets.left && startClipInsets.left && startVisibleLayout.origin.x <= endVisibleLayout.origin.x) { - clipInsets.left = MAX(0.0f, startVisibleLayout.origin.x - interpolatedLayout.origin.x); - } else if (!startClipInsets.left && endClipInsets.left && endVisibleLayout.origin.x <= startVisibleLayout.origin.x) { - clipInsets.left = MAX(0.0f, endVisibleLayout.origin.x - interpolatedLayout.origin.x); - } else { - clipInsets.left = startClipInsets.left + ((endClipInsets.left - startClipInsets.left) * _nodePosition); - } - - // Right - if (!endClipInsets.right && startClipInsets.right && (startVisibleLayout.origin.x + startVisibleLayout.size.width) >= (endVisibleLayout.origin.x + endVisibleLayout.size.width)) { - clipInsets.right = MAX(0.0f, (interpolatedLayout.origin.x + interpolatedLayout.size.width) - (startVisibleLayout.origin.x + startVisibleLayout.size.width)); - } else if (!startClipInsets.right && endClipInsets.right && (endVisibleLayout.origin.x + endVisibleLayout.size.width) >= (startVisibleLayout.origin.x + startVisibleLayout.size.width)) { - clipInsets.right = MAX(0.0f, (interpolatedLayout.origin.x + interpolatedLayout.size.width) - (endVisibleLayout.origin.x + endVisibleLayout.size.width)); - } else { - clipInsets.right = startClipInsets.right + ((endClipInsets.right - startClipInsets.right) * _nodePosition); - } - - return clipInsets; + UIEdgeInsets clipInsets = UIEdgeInsetsZero; + + // Top + if (!endClipInsets.top && startClipInsets.top && startVisibleLayout.origin.y <= endVisibleLayout.origin.y) { + clipInsets.top = MAX(0.0f, startVisibleLayout.origin.y - interpolatedLayout.origin.y); + } else if (!startClipInsets.top && endClipInsets.top && endVisibleLayout.origin.y <= startVisibleLayout.origin.y) { + clipInsets.top = MAX(0.0f, endVisibleLayout.origin.y - interpolatedLayout.origin.y); + } else { + clipInsets.top = startClipInsets.top + ((endClipInsets.top - startClipInsets.top) * _nodePosition); + } + + // Bottom + if (!endClipInsets.bottom && startClipInsets.bottom && (startVisibleLayout.origin.y + startVisibleLayout.size.height) >= (endVisibleLayout.origin.y + endVisibleLayout.size.height)) { + clipInsets.bottom = MAX(0.0f, (interpolatedLayout.origin.y + interpolatedLayout.size.height) - (startVisibleLayout.origin.y + startVisibleLayout.size.height)); + } else if (!startClipInsets.bottom && endClipInsets.bottom && (endVisibleLayout.origin.y + endVisibleLayout.size.height) >= (startVisibleLayout.origin.y + startVisibleLayout.size.height)) { + clipInsets.bottom = MAX(0.0f, (interpolatedLayout.origin.y + interpolatedLayout.size.height) - (endVisibleLayout.origin.y + endVisibleLayout.size.height)); + } else { + clipInsets.bottom = startClipInsets.bottom + ((endClipInsets.bottom - startClipInsets.bottom) * _nodePosition); + } + + // Left + if (!endClipInsets.left && startClipInsets.left && startVisibleLayout.origin.x <= endVisibleLayout.origin.x) { + clipInsets.left = MAX(0.0f, startVisibleLayout.origin.x - interpolatedLayout.origin.x); + } else if (!startClipInsets.left && endClipInsets.left && endVisibleLayout.origin.x <= startVisibleLayout.origin.x) { + clipInsets.left = MAX(0.0f, endVisibleLayout.origin.x - interpolatedLayout.origin.x); + } else { + clipInsets.left = startClipInsets.left + ((endClipInsets.left - startClipInsets.left) * _nodePosition); + } + + // Right + if (!endClipInsets.right && startClipInsets.right && (startVisibleLayout.origin.x + startVisibleLayout.size.width) >= (endVisibleLayout.origin.x + endVisibleLayout.size.width)) { + clipInsets.right = MAX(0.0f, (interpolatedLayout.origin.x + interpolatedLayout.size.width) - (startVisibleLayout.origin.x + startVisibleLayout.size.width)); + } else if (!startClipInsets.right && endClipInsets.right && (endVisibleLayout.origin.x + endVisibleLayout.size.width) >= (startVisibleLayout.origin.x + startVisibleLayout.size.width)) { + clipInsets.right = MAX(0.0f, (interpolatedLayout.origin.x + interpolatedLayout.size.width) - (endVisibleLayout.origin.x + endVisibleLayout.size.width)); + } else { + clipInsets.right = startClipInsets.right + ((endClipInsets.right - startClipInsets.right) * _nodePosition); + } + + return clipInsets; } -- (void) applyStyle:(RNSharedElementStyle*)style layer:(CALayer*)layer +- (void) applyStyle:(RNSharedElementStyle*)style view:(UIView*)view { - layer.opacity = style.opacity; - layer.backgroundColor = style.backgroundColor.CGColor; - layer.cornerRadius = style.cornerRadius; - layer.borderWidth = style.borderWidth; - layer.borderColor = style.borderColor.CGColor; - layer.shadowOpacity = style.shadowOpacity; - layer.shadowRadius = style.shadowRadius; - layer.shadowOffset = style.shadowOffset; - layer.shadowColor = style.shadowColor.CGColor; + CALayer *layer = view.layer; + + layer.opacity = style.opacity; + layer.backgroundColor = style.backgroundColor.CGColor; + layer.borderWidth = style.borderWidth; + layer.borderColor = style.borderColor.CGColor; + layer.shadowOpacity = style.shadowOpacity; + layer.shadowRadius = style.shadowRadius; + layer.shadowOffset = style.shadowOffset; + layer.shadowColor = style.shadowColor.CGColor; + [style.cornerRadii updateShadowPathForLayer:layer bounds:view.bounds]; + [style.cornerRadii updateClipMaskForLayer:layer bounds:view.bounds]; } - (void) fireMeasureEvent:(RNSharedElementTransitionItem*) item layout:(CGRect)layout visibleLayout:(CGRect)visibleLayout contentLayout:(CGRect)contentLayout { - if (!self.onMeasureNode) return; - NSDictionary* eventData = @{ - @"node": item.name, - @"layout": @{ - @"x": @(layout.origin.x), - @"y": @(layout.origin.y), - @"width": @(layout.size.width), - @"height": @(layout.size.height), - @"visibleX": @(visibleLayout.origin.x), - @"visibleY": @(visibleLayout.origin.y), - @"visibleWidth": @(visibleLayout.size.width), - @"visibleHeight": @(visibleLayout.size.height), - @"contentX": @(contentLayout.origin.x), - @"contentY": @(contentLayout.origin.y), - @"contentWidth": @(contentLayout.size.width), - @"contentHeight": @(contentLayout.size.height), - }, - @"contentType": item.content ? item.content.typeName : @"none", - @"style": @{ - @"borderRadius": @(item.style.cornerRadius) - } - }; - self.onMeasureNode(eventData); + if (!self.onMeasureNode) return; + RCTCornerRadii cornerRadii = [item.style.cornerRadii radiiForBounds:_outerStyleView.bounds]; + NSDictionary* eventData = @{ + @"node": item.name, + @"layout": @{ + @"x": @(layout.origin.x), + @"y": @(layout.origin.y), + @"width": @(layout.size.width), + @"height": @(layout.size.height), + @"visibleX": @(visibleLayout.origin.x), + @"visibleY": @(visibleLayout.origin.y), + @"visibleWidth": @(visibleLayout.size.width), + @"visibleHeight": @(visibleLayout.size.height), + @"contentX": @(contentLayout.origin.x), + @"contentY": @(contentLayout.origin.y), + @"contentWidth": @(contentLayout.size.width), + @"contentHeight": @(contentLayout.size.height), + }, + @"contentType": item.content ? item.content.typeName : @"none", + @"style": @{ + @"borderTopLeftRadius": @(cornerRadii.topLeft), + @"borderTopRightRadius": @(cornerRadii.topRight), + @"borderBottomLeftRadius": @(cornerRadii.bottomLeft), + @"borderBotomRightRadius": @(cornerRadii.bottomRight) + } + }; + self.onMeasureNode(eventData); } - (void) updateStyle { - if (!_initialLayoutPassCompleted) return; + if (!_initialLayoutPassCompleted) return; + + // Local data + RNSharedElementTransitionItem* startItem = [_items objectAtIndex:ITEM_START]; + RNSharedElementTransitionItem* startAncestor = [_items objectAtIndex:ITEM_START_ANCESTOR]; + RNSharedElementTransitionItem* endItem = [_items objectAtIndex:ITEM_END]; + RNSharedElementTransitionItem* endAncestor = [_items objectAtIndex:ITEM_END_ANCESTOR]; + RNSharedElementStyle* startStyle = startItem.style; + RNSharedElementStyle* endStyle = endItem.style; + + // Determine starting scene that is currently visible to the user + if (_initialVisibleAncestorIndex < 0) { + RNSharedElementStyle* startAncenstorLayout = startAncestor.style; + RNSharedElementStyle* endAncestorStyle = endAncestor.style; + if (startAncenstorLayout && !endAncestorStyle) { + _initialVisibleAncestorIndex = 0; + } else if (!startAncenstorLayout && endAncestorStyle) { + _initialVisibleAncestorIndex = 1; + } else if (startAncenstorLayout && endAncestorStyle){ + CGRect startAncestorVisible = CGRectIntersection(self.superview.bounds, [self.superview convertRect:startAncenstorLayout.layout fromView:nil]); + CGRect endAncestorVisible = CGRectIntersection(self.superview.bounds, [self.superview convertRect:endAncestorStyle.layout fromView:nil]); + _initialVisibleAncestorIndex = ((endAncestorVisible.size.width * endAncestorVisible.size.height) > (startAncestorVisible.size.width * startAncestorVisible.size.height)) ? 1 : 0; + } + } + + // Get start layout + BOOL startCompensate = _initialVisibleAncestorIndex == 1; + CGRect startLayout = startStyle ? [self normalizeLayout:startStyle.layout compensateForTransforms:startCompensate ancestor:startAncestor otherAncestor:endAncestor] : CGRectZero; + CGRect startVisibleLayout = startStyle ? [self normalizeLayout:[startItem visibleLayoutForAncestor:startAncestor] compensateForTransforms:startCompensate ancestor:startAncestor otherAncestor:endAncestor] : CGRectZero; + CGRect startContentLayout = startStyle ? [self normalizeLayout:[startItem contentLayoutForContent:startItem.content] compensateForTransforms:startCompensate ancestor:startAncestor otherAncestor:endAncestor] : CGRectZero; + UIEdgeInsets startClipInsets = [self getClipInsets:startLayout visibleLayout:startVisibleLayout]; + + // Get end layout + BOOL endCompensate = _initialVisibleAncestorIndex == 0; + CGRect endLayout = endStyle ? [self normalizeLayout:endStyle.layout compensateForTransforms:endCompensate ancestor:endAncestor otherAncestor:startAncestor] : CGRectZero; + CGRect endVisibleLayout = endStyle ? [self normalizeLayout:[endItem visibleLayoutForAncestor:endAncestor] compensateForTransforms:endCompensate ancestor:endAncestor otherAncestor:startAncestor] : CGRectZero; + CGRect endContentLayout = endStyle ? [self normalizeLayout:[endItem contentLayoutForContent:(endItem.content ? endItem.content : startItem.content)] compensateForTransforms:endCompensate ancestor:endAncestor otherAncestor:startAncestor] : CGRectZero; + UIEdgeInsets endClipInsets = [self getClipInsets:endLayout visibleLayout:endVisibleLayout]; + + // Get interpolated style & layout + RNSharedElementStyle* interpolatedStyle; + CGRect interpolatedLayout; + CGRect interpolatedContentLayout; + UIEdgeInsets interpolatedClipInsets; + if (!startStyle && !endStyle) return; + if (startStyle && endStyle) { + interpolatedStyle = [RNSharedElementStyle getInterpolatedStyle:startStyle style2:endStyle position:_nodePosition]; + interpolatedLayout = [self getInterpolatedLayout:startLayout layout2:endLayout position:_nodePosition]; + interpolatedClipInsets = [self getInterpolatedClipInsets:interpolatedLayout startClipInsets:startClipInsets startVisibleLayout:startVisibleLayout endClipInsets:endClipInsets endVisibleLayout:endVisibleLayout]; + interpolatedContentLayout = [self getInterpolatedLayout:startContentLayout layout2:endContentLayout position:_nodePosition]; + } else if (startStyle) { + interpolatedStyle = startStyle; + interpolatedLayout = startLayout; + interpolatedClipInsets = startClipInsets; + interpolatedContentLayout = startContentLayout; + } else { + interpolatedStyle = endStyle; + interpolatedLayout = endLayout; + interpolatedClipInsets = endClipInsets; + interpolatedContentLayout = endContentLayout; + } + + // Update frame + CGRect parentBounds = self.superview.bounds; + [super reactSetFrame:parentBounds]; + + // Update clipping mask (handles scrollview/parent clipping) + // This kind of clipping is performed at the top level. + CGFloat clipLeft = interpolatedClipInsets.left != 0.0f ? interpolatedClipInsets.left + interpolatedLayout.origin.x : 0.0f; + CGFloat clipTop = interpolatedClipInsets.top != 0.0f ? interpolatedClipInsets.top + interpolatedLayout.origin.y : 0.0f; + CGFloat clipBottom = interpolatedClipInsets.bottom != 0.0f ? parentBounds.size.height - (interpolatedLayout.origin.y + interpolatedLayout.size.height) + interpolatedClipInsets.bottom : 0.0f; + CGFloat clipRight = interpolatedClipInsets.right != 0.0f ? parentBounds.size.width - (interpolatedLayout.origin.x + interpolatedLayout.size.width) + interpolatedClipInsets.right : 0.0f; + CGRect clipFrame = CGRectMake( + clipLeft, + clipTop, + parentBounds.size.width - clipLeft - clipRight, + parentBounds.size.height - clipTop - clipBottom); + CALayer *maskLayer = [[CALayer alloc] init]; + maskLayer.backgroundColor = [UIColor whiteColor].CGColor; + maskLayer.frame = clipFrame; + self.layer.mask = maskLayer; + + // Update outer style view. This view has all styles such as border-color, + // background color, and shadow. Because of the shadow, the view itsself + // does not mask its bounds, otherwise the shadow isn't visible. + _outerStyleView.frame = interpolatedLayout; + [self applyStyle:interpolatedStyle view:_outerStyleView]; + + // Update inner clip view. This view holds the image/content views + // inside and clips their content. + CGRect innerClipFrame = interpolatedLayout; + innerClipFrame.origin.x = 0; + innerClipFrame.origin.y = 0; + _innerClipView.frame = innerClipFrame; + [interpolatedStyle.cornerRadii updateClipMaskForLayer:_innerClipView.layer bounds:_innerClipView.bounds]; + _innerClipView.layer.masksToBounds = _resize != RNSharedElementResizeNone; + + // Update content + UIView* contentView1 = (startItem.content && startItem.content.type == RNSharedElementContentTypeSnapshotView) ? startItem.content.data : _primaryImageView; + if (contentView1.superview != _innerClipView) [_innerClipView addSubview:contentView1]; + if (_animation == RNSharedElementAnimationMove) { - // Local data - RNSharedElementTransitionItem* startItem = [_items objectAtIndex:ITEM_START]; - RNSharedElementTransitionItem* startAncestor = [_items objectAtIndex:ITEM_START_ANCESTOR]; - RNSharedElementTransitionItem* endItem = [_items objectAtIndex:ITEM_END]; - RNSharedElementTransitionItem* endAncestor = [_items objectAtIndex:ITEM_END_ANCESTOR]; + // In case of move, we correctly calculate the content-frame + // and interpolate between the start- and end-state, assuming + // that the start- and end-content (image) has the same aspect-ratio + CGRect contentFrame = interpolatedContentLayout; + contentFrame.origin.x -= interpolatedLayout.origin.x; + contentFrame.origin.y -= interpolatedLayout.origin.y; + contentView1.frame = contentFrame; + } + else { + // Update content-view 2 + UIView* contentView2 = (endItem.content && endItem.content.type == RNSharedElementContentTypeSnapshotView) ? endItem.content.data : _secondaryImageView; + if (contentView2.superview != _innerClipView) [_innerClipView addSubview:contentView2]; - // Get start layout - RNSharedElementStyle* startStyle = startItem.style; - CGRect startLayout = startStyle ? [self normalizeLayout:startStyle.layout ancestor:startAncestor] : CGRectZero; - CGRect startVisibleLayout = startStyle ? [self normalizeLayout:[startItem visibleLayoutForAncestor:startAncestor] ancestor:startAncestor] : CGRectZero; - CGRect startContentLayout = startStyle ? [self normalizeLayout:[startItem contentLayoutForContent:startItem.content] ancestor:startAncestor] : CGRectZero; - UIEdgeInsets startClipInsets = [self getClipInsets:startLayout visibleLayout:startVisibleLayout]; + // In all other cases, animate and interpolate both the start- and + // end views to look like each other + CGRect startContentLayout2 = startStyle ? [RNSharedElementContent layoutForRect:endStyle ? endContentLayout : startContentLayout content:startItem.content contentMode:startStyle.contentMode reverse:YES] : CGRectZero; + CGRect endContentLayout1 = endStyle ? [RNSharedElementContent layoutForRect:startStyle ? startContentLayout : endContentLayout content:endItem.content contentMode:endStyle.contentMode reverse:YES] : CGRectZero; - // Get end layout - RNSharedElementStyle* endStyle = endItem.style; - CGRect endLayout = endStyle ? [self normalizeLayout:endStyle.layout ancestor:endAncestor] : CGRectZero; - CGRect endVisibleLayout = endStyle ? [self normalizeLayout:[endItem visibleLayoutForAncestor:endAncestor] ancestor:endAncestor] : CGRectZero; - CGRect endContentLayout = endStyle ? [self normalizeLayout:[endItem contentLayoutForContent:(endItem.content ? endItem.content : startItem.content)] ancestor:endAncestor] : CGRectZero; - UIEdgeInsets endClipInsets = [self getClipInsets:endLayout visibleLayout:endVisibleLayout]; + // Calculate interpolated layout + CGRect startInterpolatedContentLayout = [self getInterpolatedLayout:startContentLayout layout2:startContentLayout2 position:_nodePosition]; + CGRect endInterpolatedContentLayout = [self getInterpolatedLayout:endContentLayout1 layout2:endContentLayout position:_nodePosition]; - // Get interpolated style & layout - RNSharedElementStyle* interpolatedStyle; - CGRect interpolatedLayout; - CGRect interpolatedContentLayout; - UIEdgeInsets interpolatedClipInsets; - if (!startStyle && !endStyle) return; - if (startStyle && endStyle) { - interpolatedStyle = [RNSharedElementStyle getInterpolatedStyle:startStyle style2:endStyle position:_nodePosition]; - interpolatedLayout = [self getInterpolatedLayout:startLayout layout2:endLayout position:_nodePosition]; - interpolatedClipInsets = [self getInterpolatedClipInsets:interpolatedLayout startClipInsets:startClipInsets startVisibleLayout:startVisibleLayout endClipInsets:endClipInsets endVisibleLayout:endVisibleLayout]; - interpolatedContentLayout = [self getInterpolatedLayout:startContentLayout layout2:endContentLayout position:_nodePosition]; - } else if (startStyle) { - interpolatedStyle = startStyle; - interpolatedLayout = startLayout; - interpolatedClipInsets = startClipInsets; - interpolatedContentLayout = startContentLayout; - } else { - interpolatedStyle = endStyle; - interpolatedLayout = endLayout; - interpolatedClipInsets = endClipInsets; - interpolatedContentLayout = endContentLayout; + // Calculate new size + switch (_resize) { + case RNSharedElementResizeAuto: + // Nothing to do + break; + case RNSharedElementResizeStretch: + // TODO + break; + case RNSharedElementResizeClip: + case RNSharedElementResizeNone: + startInterpolatedContentLayout.size = startContentLayout.size; + endInterpolatedContentLayout.size = endContentLayout.size; + break; } - // Update frame - CGRect parentBounds = self.superview.bounds; - [super reactSetFrame:parentBounds]; - - // Update clipping mask (handles scrollview/parent clipping) - // This kind of clipping is performed at the top level. - CGFloat clipLeft = interpolatedClipInsets.left != 0.0f ? interpolatedClipInsets.left + interpolatedLayout.origin.x : 0.0f; - CGFloat clipTop = interpolatedClipInsets.top != 0.0f ? interpolatedClipInsets.top + interpolatedLayout.origin.y : 0.0f; - CGFloat clipBottom = interpolatedClipInsets.bottom != 0.0f ? parentBounds.size.height - (interpolatedLayout.origin.y + interpolatedLayout.size.height) + interpolatedClipInsets.bottom : 0.0f; - CGFloat clipRight = interpolatedClipInsets.right != 0.0f ? parentBounds.size.width - (interpolatedLayout.origin.x + interpolatedLayout.size.width) + interpolatedClipInsets.right : 0.0f; - CGRect clipFrame = CGRectMake( - clipLeft, - clipTop, - parentBounds.size.width - clipLeft - clipRight, - parentBounds.size.height - clipTop - clipBottom); - CALayer *maskLayer = [[CALayer alloc] init]; - maskLayer.backgroundColor = [UIColor whiteColor].CGColor; - maskLayer.frame = clipFrame; - self.layer.mask = maskLayer; - - // Update outer style view. This view has all styles such as border-color, - // background color, and shadow. Because of the shadow, the view itsself - // does not mask its bounds, otherwise the shadow isn't visible. - _outerStyleView.frame = interpolatedLayout; - [self applyStyle:interpolatedStyle layer:_outerStyleView.layer]; + // Calculate new origin + switch (_align) { + case RNSharedElementAlignLeftTop: + startInterpolatedContentLayout.origin.x = 0; + startInterpolatedContentLayout.origin.y = 0; + endInterpolatedContentLayout.origin.x = 0; + endInterpolatedContentLayout.origin.y = 0; + break; + case RNSharedElementAlignLeftCenter: + startInterpolatedContentLayout.origin.x = 0; + startInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - startInterpolatedContentLayout.size.height) / 2; + endInterpolatedContentLayout.origin.x = 0; + endInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - endInterpolatedContentLayout.size.height) / 2; + break; + case RNSharedElementAlignLeftBottom: + startInterpolatedContentLayout.origin.x = 0; + startInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - startInterpolatedContentLayout.size.height; + endInterpolatedContentLayout.origin.x = 0; + endInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - endInterpolatedContentLayout.size.height; + break; + case RNSharedElementAlignRightTop: + startInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - startInterpolatedContentLayout.size.width; + startInterpolatedContentLayout.origin.y = 0; + endInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - endInterpolatedContentLayout.size.width; + endInterpolatedContentLayout.origin.y = 0; + break; + case RNSharedElementAlignRightCenter: + startInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - startInterpolatedContentLayout.size.width; + startInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - startInterpolatedContentLayout.size.height) / 2; + endInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - endInterpolatedContentLayout.size.width; + endInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - endInterpolatedContentLayout.size.height) / 2; + break; + case RNSharedElementAlignRightBottom: + startInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - startInterpolatedContentLayout.size.width; + startInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - startInterpolatedContentLayout.size.height; + endInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - endInterpolatedContentLayout.size.width; + endInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - endInterpolatedContentLayout.size.height; + break; + case RNSharedElementAlignCenterTop: + startInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - startInterpolatedContentLayout.size.width) / 2; + startInterpolatedContentLayout.origin.y = 0; + endInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - endInterpolatedContentLayout.size.width) / 2; + endInterpolatedContentLayout.origin.y = 0; + break; + case RNSharedElementAlignAuto: + case RNSharedElementAlignCenterCenter: + startInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - startInterpolatedContentLayout.size.width) / 2; + startInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - startInterpolatedContentLayout.size.height) / 2; + endInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - endInterpolatedContentLayout.size.width) / 2; + endInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - endInterpolatedContentLayout.size.height) / 2; + break; + case RNSharedElementAlignCenterBottom: + startInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - startInterpolatedContentLayout.size.width) / 2; + startInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - startInterpolatedContentLayout.size.height; + endInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - endInterpolatedContentLayout.size.width) / 2; + endInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - endInterpolatedContentLayout.size.height; + break; + } - // Update inner clip view. This view holds the image/content views - // inside and clips their content. - CGRect innerClipFrame = interpolatedLayout; - innerClipFrame.origin.x = 0; - innerClipFrame.origin.y = 0; - _innerClipView.layer.cornerRadius = interpolatedStyle.cornerRadius; - _innerClipView.frame = innerClipFrame; - _innerClipView.layer.masksToBounds = _resize != RNSharedElementResizeNone; + // Update start node + contentView1.frame = startInterpolatedContentLayout; - // Update content - UIView* contentView1 = (startItem.content && startItem.content.type == RNSharedElementContentTypeSnapshotView) ? startItem.content.data : _primaryImageView; - if (contentView1.superview != _innerClipView) [_innerClipView addSubview:contentView1]; - if (_animation == RNSharedElementAnimationMove) { - - // In case of move, we correctly calculate the content-frame - // and interpolate between the start- and end-state, assuming - // that the start- and end-content (image) has the same aspect-ratio - CGRect contentFrame = interpolatedContentLayout; - contentFrame.origin.x -= interpolatedLayout.origin.x; - contentFrame.origin.y -= interpolatedLayout.origin.y; - contentView1.frame = contentFrame; - } - else { - // Update content-view 2 - UIView* contentView2 = (endItem.content && endItem.content.type == RNSharedElementContentTypeSnapshotView) ? endItem.content.data : _secondaryImageView; - if (contentView2.superview != _innerClipView) [_innerClipView addSubview:contentView2]; - - // In all other cases, animate and interpolate both the start- and - // end views to look like each other - CGRect startContentLayout2 = startStyle ? [RNSharedElementContent layoutForRect:endStyle ? endContentLayout : startContentLayout content:startItem.content contentMode:startStyle.contentMode reverse:YES] : CGRectZero; - CGRect endContentLayout1 = endStyle ? [RNSharedElementContent layoutForRect:startStyle ? startContentLayout : endContentLayout content:endItem.content contentMode:endStyle.contentMode reverse:YES] : CGRectZero; - - // Calculate interpolated layout - CGRect startInterpolatedContentLayout = [self getInterpolatedLayout:startContentLayout layout2:startContentLayout2 position:_nodePosition]; - CGRect endInterpolatedContentLayout = [self getInterpolatedLayout:endContentLayout1 layout2:endContentLayout position:_nodePosition]; - - // Calculate new size - switch (_resize) { - case RNSharedElementResizeAuto: - // Nothing to do - break; - case RNSharedElementResizeStretch: - // TODO - break; - case RNSharedElementResizeClip: - case RNSharedElementResizeNone: - startInterpolatedContentLayout.size = startContentLayout.size; - endInterpolatedContentLayout.size = endContentLayout.size; - break; - } - - // Calculate new origin - switch (_align) { - case RNSharedElementAlignLeftTop: - startInterpolatedContentLayout.origin.x = 0; - startInterpolatedContentLayout.origin.y = 0; - endInterpolatedContentLayout.origin.x = 0; - endInterpolatedContentLayout.origin.y = 0; - break; - case RNSharedElementAlignLeftCenter: - startInterpolatedContentLayout.origin.x = 0; - startInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - startInterpolatedContentLayout.size.height) / 2; - endInterpolatedContentLayout.origin.x = 0; - endInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - endInterpolatedContentLayout.size.height) / 2; - break; - case RNSharedElementAlignLeftBottom: - startInterpolatedContentLayout.origin.x = 0; - startInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - startInterpolatedContentLayout.size.height; - endInterpolatedContentLayout.origin.x = 0; - endInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - endInterpolatedContentLayout.size.height; - break; - case RNSharedElementAlignRightTop: - startInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - startInterpolatedContentLayout.size.width; - startInterpolatedContentLayout.origin.y = 0; - endInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - endInterpolatedContentLayout.size.width; - endInterpolatedContentLayout.origin.y = 0; - break; - case RNSharedElementAlignRightCenter: - startInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - startInterpolatedContentLayout.size.width; - startInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - startInterpolatedContentLayout.size.height) / 2; - endInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - endInterpolatedContentLayout.size.width; - endInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - endInterpolatedContentLayout.size.height) / 2; - break; - case RNSharedElementAlignRightBottom: - startInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - startInterpolatedContentLayout.size.width; - startInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - startInterpolatedContentLayout.size.height; - endInterpolatedContentLayout.origin.x = interpolatedLayout.size.width - endInterpolatedContentLayout.size.width; - endInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - endInterpolatedContentLayout.size.height; - break; - case RNSharedElementAlignCenterTop: - startInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - startInterpolatedContentLayout.size.width) / 2; - startInterpolatedContentLayout.origin.y = 0; - endInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - endInterpolatedContentLayout.size.width) / 2; - endInterpolatedContentLayout.origin.y = 0; - break; - case RNSharedElementAlignAuto: - case RNSharedElementAlignCenterCenter: - startInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - startInterpolatedContentLayout.size.width) / 2; - startInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - startInterpolatedContentLayout.size.height) / 2; - endInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - endInterpolatedContentLayout.size.width) / 2; - endInterpolatedContentLayout.origin.y = (interpolatedLayout.size.height - endInterpolatedContentLayout.size.height) / 2; - break; - case RNSharedElementAlignCenterBottom: - startInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - startInterpolatedContentLayout.size.width) / 2; - startInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - startInterpolatedContentLayout.size.height; - endInterpolatedContentLayout.origin.x = (interpolatedLayout.size.width - endInterpolatedContentLayout.size.width) / 2; - endInterpolatedContentLayout.origin.y = interpolatedLayout.size.height - endInterpolatedContentLayout.size.height; - break; - } - - // Update start node - contentView1.frame = startInterpolatedContentLayout; - - // Update end node - contentView2.frame = endInterpolatedContentLayout; - - // Fade - if (_animation == RNSharedElementAnimationFadeIn) { - // Fade-in - contentView1.layer.opacity = 0.0f; - contentView2.layer.opacity = MIN(MAX(_nodePosition, 0.0f), 1.0f); - } - else if (_animation == RNSharedElementAnimationFadeOut) { - // Fade-out - contentView1.layer.opacity = 1.0f - MIN(MAX(_nodePosition, 0.0f), 1.0f); - contentView2.layer.opacity = 0.0f; - } - else { - // Cross-fade - contentView1.layer.opacity = 1.0f - MIN(MAX(_nodePosition, 0.0f), 1.0f); - contentView2.layer.opacity = MIN(MAX(_nodePosition, 0.0f), 1.0f); - } - } + // Update end node + contentView2.frame = endInterpolatedContentLayout; - // Fire events - if ((startAncestor.style != nil) && !startAncestor.hasCalledOnMeasure) { - startAncestor.hasCalledOnMeasure = YES; - startItem.hasCalledOnMeasure = NO; - CGRect ancestorLayout = [self.superview convertRect:startAncestor.style.layout fromView:nil]; - [self fireMeasureEvent:startAncestor layout:ancestorLayout visibleLayout:ancestorLayout contentLayout:ancestorLayout]; - } - if ((startItem.style != nil) && !startItem.hasCalledOnMeasure) { - startItem.hasCalledOnMeasure = YES; - [self fireMeasureEvent:startItem layout:startLayout visibleLayout:startVisibleLayout contentLayout:startContentLayout]; + // Fade + if (_animation == RNSharedElementAnimationFadeIn) { + // Fade-in + contentView1.layer.opacity = 0.0f; + contentView2.layer.opacity = MIN(MAX(_nodePosition, 0.0f), 1.0f); } - if ((endAncestor.style != nil) && !endAncestor.hasCalledOnMeasure) { - endAncestor.hasCalledOnMeasure = YES; - endItem.hasCalledOnMeasure = NO; - CGRect ancestorLayout = [self.superview convertRect:endAncestor.style.layout fromView:nil]; - [self fireMeasureEvent:endAncestor layout:ancestorLayout visibleLayout:ancestorLayout contentLayout:ancestorLayout]; + else if (_animation == RNSharedElementAnimationFadeOut) { + // Fade-out + contentView1.layer.opacity = 1.0f - MIN(MAX(_nodePosition, 0.0f), 1.0f); + contentView2.layer.opacity = 0.0f; } - if ((endItem.style != nil) && !endItem.hasCalledOnMeasure) { - endItem.hasCalledOnMeasure = YES; - [self fireMeasureEvent:endItem layout:endLayout visibleLayout:endVisibleLayout contentLayout:endContentLayout]; + else { + // Cross-fade + contentView1.layer.opacity = 1.0f - MIN(MAX(_nodePosition, 0.0f), 1.0f); + contentView2.layer.opacity = MIN(MAX(_nodePosition, 0.0f), 1.0f); } + } + + // Fire events + if ((startAncestor.style != nil) && !startAncestor.hasCalledOnMeasure) { + startAncestor.hasCalledOnMeasure = YES; + startItem.hasCalledOnMeasure = NO; + CGRect ancestorLayout = [self.superview convertRect:startAncestor.style.layout fromView:nil]; + [self fireMeasureEvent:startAncestor layout:ancestorLayout visibleLayout:ancestorLayout contentLayout:ancestorLayout]; + } + if ((startItem.style != nil) && !startItem.hasCalledOnMeasure) { + startItem.hasCalledOnMeasure = YES; + [self fireMeasureEvent:startItem layout:startLayout visibleLayout:startVisibleLayout contentLayout:startContentLayout]; + } + if ((endAncestor.style != nil) && !endAncestor.hasCalledOnMeasure) { + endAncestor.hasCalledOnMeasure = YES; + endItem.hasCalledOnMeasure = NO; + CGRect ancestorLayout = [self.superview convertRect:endAncestor.style.layout fromView:nil]; + [self fireMeasureEvent:endAncestor layout:ancestorLayout visibleLayout:ancestorLayout contentLayout:ancestorLayout]; + } + if ((endItem.style != nil) && !endItem.hasCalledOnMeasure) { + endItem.hasCalledOnMeasure = YES; + [self fireMeasureEvent:endItem layout:endLayout visibleLayout:endVisibleLayout contentLayout:endContentLayout]; + } } - (void) reactSetFrame:(CGRect)frame { - // Only after the frame bounds have been set by the RN layout-system - // we schedule a layout-fetch to run after these updates to ensure - // that Yoga/UIManager has finished the initial layout pass. - if (_reactFrameSet == NO) { - //NSLog(@"reactSetFrame: %@", NSStringFromCGRect(frame)); - _reactFrameSet = YES; - dispatch_async(dispatch_get_main_queue(), ^{ - for (RNSharedElementTransitionItem* item in self->_items) { - if (item.needsLayout) { - item.needsLayout = NO; - [item.node requestStyle:self]; - } - if (item.needsContent) { - item.needsContent = NO; - [item.node requestContent:self]; - } - } - self->_initialLayoutPassCompleted = YES; - [self updateStyle]; - [self updateNodeVisibility]; - }); - } - - // When react attempts to change the frame on this view, - // override that and apply our own measured frame and styles - [self updateStyle]; - [self updateNodeVisibility]; + // Only after the frame bounds have been set by the RN layout-system + // we schedule a layout-fetch to run after these updates to ensure + // that Yoga/UIManager has finished the initial layout pass. + if (_reactFrameSet == NO) { + //NSLog(@"reactSetFrame: %@", NSStringFromCGRect(frame)); + _reactFrameSet = YES; + dispatch_async(dispatch_get_main_queue(), ^{ + for (RNSharedElementTransitionItem* item in self->_items) { + if (item.needsLayout) { + item.needsLayout = NO; + [item.node requestStyle:self]; + } + if (item.needsContent) { + item.needsContent = NO; + [item.node requestContent:self]; + } + } + self->_initialLayoutPassCompleted = YES; + [self updateStyle]; + [self updateNodeVisibility]; + }); + } + + // When react attempts to change the frame on this view, + // override that and apply our own measured frame and styles + [self updateStyle]; + [self updateNodeVisibility]; } @end diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransitionItem.m b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransitionItem.m index d7c81c90b90b8..23d4512f60dd0 100644 --- a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransitionItem.m +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransitionItem.m @@ -14,97 +14,97 @@ #endif @implementation RNSharedElementTransitionItem { - - CGRect _visibleLayoutCache; + + CGRect _visibleLayoutCache; } - (instancetype)initWithNodeManager:(RNSharedElementNodeManager*)nodeManager name:(NSString*)name isAncestor:(BOOL)isAncestor { - _visibleLayoutCache = CGRectNull; - _nodeManager = nodeManager; - _name = name; - _isAncestor = isAncestor; - _node = nil; - _needsLayout = NO; - _needsContent = NO; - _content = nil; - _style = nil; - _hidden = NO; - return self; + _visibleLayoutCache = CGRectNull; + _nodeManager = nodeManager; + _name = name; + _isAncestor = isAncestor; + _node = nil; + _needsLayout = NO; + _needsContent = NO; + _content = nil; + _style = nil; + _hidden = NO; + return self; } - (void) setNode:(RNSharedElementNode *)node { - if (_node == node) { - if (node != nil) [_nodeManager release:node]; - return; - } - if (_node != nil) { - if (_hidden) _node.hideRefCount--; - [_nodeManager release:_node]; - } - _node = node; - _needsLayout = node != nil; - _needsContent = !_isAncestor && (node != nil); - _content = nil; - _style = nil; - _hidden = NO; + if (_node == node) { + if (node != nil) [_nodeManager release:node]; + return; + } + if (_node != nil) { + if (_hidden) _node.hideRefCount--; + [_nodeManager release:_node]; + } + _node = node; + _needsLayout = node != nil; + _needsContent = !_isAncestor && (node != nil); + _content = nil; + _style = nil; + _hidden = NO; } - (void) setHidden:(BOOL)hidden { - if (_hidden == hidden) return; - _hidden = hidden; - if (hidden) { - _node.hideRefCount++; - } else { - _node.hideRefCount--; - } + if (_hidden == hidden) return; + _hidden = hidden; + if (hidden) { + _node.hideRefCount++; + } else { + _node.hideRefCount--; + } } - (CGRect) contentLayoutForContent:(RNSharedElementContent*)content { - if (!content || !_style) return CGRectZero; - return [RNSharedElementContent layoutForRect:_style.layout content:content contentMode:_style.contentMode reverse:NO]; + if (!content || !_style) return CGRectZero; + return [RNSharedElementContent layoutForRect:_style.layout content:content contentMode:_style.contentMode reverse:NO]; } - (CGRect) visibleLayoutForAncestor:(RNSharedElementTransitionItem*) ancestor { - if (!CGRectIsNull(_visibleLayoutCache) || !_style) return _visibleLayoutCache; - if (!ancestor.style) return _style.layout; - - // Get visible area (some parts may be clipped in a scrollview or something) - CGRect visibleLayout = _style.layout; - UIView* superview = _style.view.superview; - while (superview != nil) { - if (superview.layer.masksToBounds || (superview.layer.mask != nil)) { - CGRect superLayout = [superview convertRect:superview.bounds toView:nil]; - CGRect intersectedLayout = CGRectIntersection(visibleLayout, superLayout); - if (isinf(intersectedLayout.origin.x) || isinf(intersectedLayout.origin.y) || CGRectIsEmpty(intersectedLayout)) { - if ((visibleLayout.origin.y + visibleLayout.size.height) < superLayout.origin.y) { - visibleLayout.origin.y = superLayout.origin.y; - visibleLayout.size.height = 0; - } - if (visibleLayout.origin.y > (superLayout.origin.y + superLayout.size.height)) { - visibleLayout.origin.y = superLayout.origin.y + superLayout.size.height; - visibleLayout.size.height = 0; - } - if ((visibleLayout.origin.x + visibleLayout.size.width) < superLayout.origin.x) { - visibleLayout.origin.x = superLayout.origin.x; - visibleLayout.size.width = 0; - } - if (visibleLayout.origin.x > (superLayout.origin.x + superLayout.size.width)) { - visibleLayout.origin.x = superLayout.origin.x + superLayout.size.width; - visibleLayout.size.width = 0; - } - break; - } - visibleLayout = intersectedLayout; + if (!CGRectIsNull(_visibleLayoutCache) || !_style) return _visibleLayoutCache; + if (!ancestor.style) return _style.layout; + + // Get visible area (some parts may be clipped in a scrollview or something) + CGRect visibleLayout = _style.layout; + UIView* superview = _style.view.superview; + while (superview != nil) { + if (superview.layer.masksToBounds || (superview.layer.mask != nil)) { + CGRect superLayout = [superview convertRect:superview.bounds toView:nil]; + CGRect intersectedLayout = CGRectIntersection(visibleLayout, superLayout); + if (isinf(intersectedLayout.origin.x) || isinf(intersectedLayout.origin.y) || CGRectIsEmpty(intersectedLayout)) { + if ((visibleLayout.origin.y + visibleLayout.size.height) < superLayout.origin.y) { + visibleLayout.origin.y = superLayout.origin.y; + visibleLayout.size.height = 0; + } + if (visibleLayout.origin.y > (superLayout.origin.y + superLayout.size.height)) { + visibleLayout.origin.y = superLayout.origin.y + superLayout.size.height; + visibleLayout.size.height = 0; + } + if ((visibleLayout.origin.x + visibleLayout.size.width) < superLayout.origin.x) { + visibleLayout.origin.x = superLayout.origin.x; + visibleLayout.size.width = 0; + } + if (visibleLayout.origin.x > (superLayout.origin.x + superLayout.size.width)) { + visibleLayout.origin.x = superLayout.origin.x + superLayout.size.width; + visibleLayout.size.width = 0; } - if (superview == ancestor.style.view) break; - superview = superview.superview; + break; + } + visibleLayout = intersectedLayout; } - _visibleLayoutCache = visibleLayout; - return visibleLayout; + if (superview == ancestor.style.view) break; + superview = superview.superview; + } + _visibleLayoutCache = visibleLayout; + return visibleLayout; } diff --git a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransitionManager.m b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransitionManager.m index 02771a49440f4..77e590a7159f1 100644 --- a/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransitionManager.m +++ b/ios/Exponent/Versioned/Core/Api/Components/SharedElement/RNSharedElementTransitionManager.m @@ -12,39 +12,39 @@ @implementation RNSharedElementTransitionManager { - RNSharedElementNodeManager* _nodeManager; + RNSharedElementNodeManager* _nodeManager; } RCT_EXPORT_MODULE(RNSharedElementTransition); - (instancetype) init { - if ((self = [super init])) { - _nodeManager = [[RNSharedElementNodeManager alloc]init]; - } - return self; + if ((self = [super init])) { + _nodeManager = [[RNSharedElementNodeManager alloc]init]; + } + return self; } - (UIView *)view { - return [[RNSharedElementTransition alloc] initWithNodeManager:_nodeManager]; + return [[RNSharedElementTransition alloc] initWithNodeManager:_nodeManager]; } - (dispatch_queue_t)methodQueue { - return self.bridge.uiManager.methodQueue; + return self.bridge.uiManager.methodQueue; } - (RNSharedElementNode*) nodeFromJson:(NSDictionary*)json { - if (json == nil) return nil; - NSNumber* nodeHandle = [json valueForKey:@"nodeHandle"]; - NSNumber* isParent =[json valueForKey:@"isParent"]; - if ([nodeHandle isKindOfClass:[NSNumber class]]) { - UIView *sourceView = [self.bridge.uiManager viewForReactTag:nodeHandle]; - return [_nodeManager acquire:nodeHandle view:sourceView isParent:[isParent boolValue]]; - } - return nil; + if (json == nil) return nil; + NSNumber* nodeHandle = [json valueForKey:@"nodeHandle"]; + NSNumber* isParent =[json valueForKey:@"isParent"]; + if ([nodeHandle isKindOfClass:[NSNumber class]]) { + UIView *sourceView = [self.bridge.uiManager viewForReactTag:nodeHandle]; + return [_nodeManager acquire:nodeHandle view:sourceView isParent:[isParent boolValue]]; + } + return nil; } RCT_EXPORT_VIEW_PROPERTY(nodePosition, CGFloat); @@ -53,13 +53,13 @@ - (RNSharedElementNode*) nodeFromJson:(NSDictionary*)json RCT_EXPORT_VIEW_PROPERTY(align, NSInteger); RCT_CUSTOM_VIEW_PROPERTY(startNode, NSObject, RNSharedElementTransition) { - view.startNode = [self nodeFromJson:[json valueForKey:@"node"]]; - view.startAncestor = [self nodeFromJson:[json valueForKey:@"ancestor"]]; + view.startNode = [self nodeFromJson:[json valueForKey:@"node"]]; + view.startAncestor = [self nodeFromJson:[json valueForKey:@"ancestor"]]; } RCT_CUSTOM_VIEW_PROPERTY(endNode, NSObject, RNSharedElementTransition) { - view.endNode = [self nodeFromJson:[json valueForKey:@"node"]]; - view.endAncestor = [self nodeFromJson:[json valueForKey:@"ancestor"]]; + view.endNode = [self nodeFromJson:[json valueForKey:@"node"]]; + view.endAncestor = [self nodeFromJson:[json valueForKey:@"ancestor"]]; } RCT_EXPORT_VIEW_PROPERTY(onMeasureNode, RCTDirectEventBlock); @@ -68,16 +68,16 @@ - (RNSharedElementNode*) nodeFromJson:(NSDictionary*)json resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { - NSArray* imageResolvers = [config valueForKey:@"imageResolvers"]; - if (imageResolvers != nil) { - [RNSharedElementNode setImageResolvers:imageResolvers]; - } - resolve(@(YES)); + NSArray* imageResolvers = [config valueForKey:@"imageResolvers"]; + if (imageResolvers != nil) { + [RNSharedElementNode setImageResolvers:imageResolvers]; + } + resolve(@(YES)); } + (BOOL)requiresMainQueueSetup { - return YES; + return YES; } @end diff --git a/packages/expo/bundledNativeModules.json b/packages/expo/bundledNativeModules.json index a23484763a3c2..35f5323b86f1c 100644 --- a/packages/expo/bundledNativeModules.json +++ b/packages/expo/bundledNativeModules.json @@ -79,7 +79,7 @@ "expo-branch": "~2.1.1", "react-native-appearance": "~0.3.3", "react-native-safe-area-context": "0.7.3", - "react-native-shared-element": "0.5.6", + "react-native-shared-element": "0.7.0", "expo-application": "~2.1.1", "expo-battery": "~2.1.1", "expo-cellular": "~2.1.1", diff --git a/yarn.lock b/yarn.lock index 3db3f77fb8ea0..5f1bce2c3cb49 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14115,10 +14115,10 @@ react-native-scrollable-mixin@^1.0.0: resolved "https://registry.yarnpkg.com/react-native-scrollable-mixin/-/react-native-scrollable-mixin-1.0.1.tgz#34a32167b64248594154fd0d6a8b03f22740548e" integrity sha1-NKMhZ7ZCSFlBVP0NaosD8idAVI4= -react-native-shared-element@~0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/react-native-shared-element/-/react-native-shared-element-0.5.6.tgz#f0efac19aac50d495cd4b8caedae758cb9752503" - integrity sha512-4e3mFtrmqVHqmRum9xFTpY3JrteEP78oOIMM2I3ygu1sHcemYlyOq1BRoHk4vjHKzIckYuSXt8GAPYv1U5vosg== +react-native-shared-element@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/react-native-shared-element/-/react-native-shared-element-0.7.0.tgz#c5e02eb372f6e38e48eb1030fd59be8f3d8c7a1f" + integrity sha512-TJTGwQceABYete+vH3ahNSgzVzXz7X2SPv3thT91gdcFCrm7ht7IKXBXJiYjOA+4TfdqnGEAWkspCy80oEnOgw== react-native-svg@11.0.1: version "11.0.1"