Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions apps/src/tests/TestZoomTransition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from "react";
import { Button, View } from "react-native";
import Colors from "../shared/styling/Colors";
import { createNativeStackNavigator, NativeStackNavigationProp } from "@react-navigation/native-stack";
import { ParamListBase } from "@react-navigation/routers";
import { NavigationContainer } from "@react-navigation/native";
import { Rectangle } from "../shared/Rectangle";
import { ZoomTransitionSource } from "react-native-screens";

interface StackRouteParams extends ParamListBase {
Home: undefined,
Second: undefined,
}

interface StackRouteProp {
navigation: NativeStackNavigationProp<StackRouteParams>,
}

const Stack = createNativeStackNavigator<StackRouteParams>();

export default function App() {
return (
<AppStack />
)
}

function AppStack() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name='Home' component={Home} />
<Stack.Screen name='Second' component={Second} options={{ headerShown: false }} />
</Stack.Navigator>
</NavigationContainer>
)
}

function Home({ navigation }: StackRouteProp) {
return (
<View style={{ flex: 1, width: '100%', height: '100%', backgroundColor: Colors.YellowLight60 }}>
<Button title="Go Second" onPress={() => navigation.navigate('Second')} />
<View style={{ flexDirection: 'row', backgroundColor: Colors.BlueLight60 }} collapsable={false}>
<ZoomTransitionSource transitionTag="zoom">
<Rectangle width={64} height={64} color={Colors.RedLight60} />
</ZoomTransitionSource>
</View>
</View >
)
}

function Second({ navigation }: StackRouteProp) {
return (
<View style={{ flex: 1, width: '100%', height: '100%', backgroundColor: Colors.PurpleLight60 }}>
<Rectangle width={64} height={128} color={Colors.RedLight60} />
<Button title="Go back" onPress={() => navigation.popTo('Home')} />
</View>
)
}

1 change: 1 addition & 0 deletions apps/src/tests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,4 @@ export { default as TestAnimation } from './TestAnimation';
export { default as TestBottomTabs } from './TestBottomTabs';
export { default as TestScreenStack } from './TestScreenStack';
export { default as TestSplitView } from './TestSplitView';
export { default as TestZoomTransition } from './TestZoomTransition';
4 changes: 4 additions & 0 deletions ios/RNSScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ namespace react = facebook::react;
- (CGFloat)calculateHeaderHeightIsModal:(BOOL)isModal;
#endif

#if RNS_GAMMA_ENABLED
- (void)registerForZoomTransition:(UIView *)zoomSourceView;
#endif

@end

@class RNSScreenStackHeaderConfig;
Expand Down
52 changes: 52 additions & 0 deletions ios/RNSScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,7 @@

@implementation RNSScreen {
__weak id _previousFirstResponder;
__weak UIView *_zoomTransitionSource;
CGRect _lastViewFrame;
RNSScreenView *_initialView;
UIView *_fakeView;
Expand All @@ -1458,11 +1459,62 @@
_shouldNotify = YES;
#ifdef RCT_NEW_ARCH_ENABLED
_initialView = (RNSScreenView *)view;
_zoomTransitionSource = nil;
__weak RNSScreen *weakSelf = self;

UIZoomTransitionOptions *zoomOptions = [UIZoomTransitionOptions new];

Check failure on line 1465 in ios/RNSScreen.mm

View workflow job for this annotation

GitHub Actions / build

use of undeclared identifier 'UIZoomTransitionOptions'

Check failure on line 1465 in ios/RNSScreen.mm

View workflow job for this annotation

GitHub Actions / build

unknown type name 'UIZoomTransitionOptions'
zoomOptions.alignmentRectProvider = ^CGRect(UIZoomTransitionAlignmentRectContext *context) {

Check failure on line 1466 in ios/RNSScreen.mm

View workflow job for this annotation

GitHub Actions / build

expected ')'

Check failure on line 1466 in ios/RNSScreen.mm

View workflow job for this annotation

GitHub Actions / build

type-id cannot have a name
RNSScreen *strongSelf = weakSelf;
if (strongSelf == nil) {
RCTLogError(@"[RNScreens] Nullish self");
return CGRectNull;
}

NSLog(@"AlignmentRectProvider called");

// UIView *navCtrlView = strongSelf.navigationController.view;
// CGRect frame = [strongSelf->_zoomTransitionSource convertRect:strongSelf->_zoomTransitionSource.frame
// toView:navCtrlView];

return context.sourceView.frame;

Check failure on line 1479 in ios/RNSScreen.mm

View workflow job for this annotation

GitHub Actions / build

use of undeclared identifier 'context'
};

self.preferredTransition = [UIViewControllerTransition

Check failure on line 1482 in ios/RNSScreen.mm

View workflow job for this annotation

GitHub Actions / build

use of undeclared identifier 'UIViewControllerTransition'

Check failure on line 1482 in ios/RNSScreen.mm

View workflow job for this annotation

GitHub Actions / build

property 'preferredTransition' not found on object of type 'RNSScreen *'
zoomWithOptions:nil
sourceViewProvider:^UIView *(UIZoomTransitionSourceViewProviderContext *context) {
RNSScreen *strongSelf = weakSelf;
if (strongSelf == nil) {
RCTLogError(@"[RNScreens] Nullish self");
return nil;
}

if (![context.zoomedViewController isKindOfClass:RNSScreen.class]) {
NSLog(@"[RNScreens] Unexpected view controller. Got %@", context.zoomedViewController);
assert(false);
}

NSLog(
@"Source\n%@\nZoomed\n%@\nSource\n%@",
context.sourceViewController,
context.zoomedViewController,
strongSelf->_zoomTransitionSource);

RNSScreen *sourceScreen = static_cast<RNSScreen *>(context.sourceViewController);
return sourceScreen->_zoomTransitionSource;
}];
#endif
}
return self;
}

#if RNS_GAMMA_ENABLED
- (void)registerForZoomTransition:(UIView *)zoomSourceView
{
NSLog(@"Registering view %@ for zoom transition", zoomSourceView);
self->_zoomTransitionSource = zoomSourceView;
}
#endif

// TODO: Find out why this is executed when screen is going out
- (void)viewWillAppear:(BOOL)animated
{
Expand Down
11 changes: 11 additions & 0 deletions ios/gamma/transition/RNSZoomTransitionSourceComponentView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import "RNSReactBaseView.h"

NS_ASSUME_NONNULL_BEGIN

@interface RNSZoomTransitionSourceComponentView : RNSReactBaseView

@property (nonatomic, nonnull, readonly) NSString *transitionTag;

@end

NS_ASSUME_NONNULL_END
88 changes: 88 additions & 0 deletions ios/gamma/transition/RNSZoomTransitionSourceComponentView.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#import "RNSZoomTransitionSourceComponentView.h"

#import <RNScreens/RNSScreen.h>
#import <React/RCTConversions.h>
#import <react/renderer/components/rnscreens/ComponentDescriptors.h>
#import <react/renderer/components/rnscreens/EventEmitters.h>
#import <react/renderer/components/rnscreens/Props.h>
#import <react/renderer/components/rnscreens/RCTComponentViewHelpers.h>

namespace react = facebook::react;

@implementation RNSZoomTransitionSourceComponentView

- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self resetState];
}
return self;
}

- (void)resetState
{
_transitionTag = @"";
}

- (void)didMoveToWindow
{
if (self.window == nil) {
return;
}

RNSScreen *screenController = [self findAncestorScreen];
if (screenController == nil) {
RCTLogError(@"Nullish screen controller");
}

[screenController registerForZoomTransition:self];
}

#pragma mark - RCTViewComponentViewProtocol

- (void)updateProps:(const facebook::react::Props::Shared &)props
oldProps:(const facebook::react::Props::Shared &)oldProps
{
const auto &oldComponentProps = *std::static_pointer_cast<const react::RNSZoomTransitionSourceProps>(_props);

const auto &newComponentProps = *std::static_pointer_cast<const react::RNSZoomTransitionSourceProps>(props);

if (newComponentProps.transitionTag != oldComponentProps.transitionTag) {
_transitionTag = RCTNSStringFromString(newComponentProps.transitionTag);
}

[super updateProps:props oldProps:oldProps];
}

+ (BOOL)shouldBeRecycled
{
return NO;
}

+ (react::ComponentDescriptorProvider)componentDescriptorProvider
{
return react::concreteComponentDescriptorProvider<react::RNSZoomTransitionSourceComponentDescriptor>();
}

#pragma mark - Lookup traversals

- (RNSScreen *_Nullable)findAncestorScreen
{
UIView *screenViewCandidate = self.superview;

while (screenViewCandidate != nil) {
if ([screenViewCandidate isKindOfClass:RNSScreenView.class]) {
return static_cast<RNSScreenView *>(screenViewCandidate).controller;
}
screenViewCandidate = screenViewCandidate.superview;
}

return nil;
}

@end

Class<RCTComponentViewProtocol> RNSZoomTransitionSourceCls(void)
{
return RNSZoomTransitionSourceComponentView.class;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#import <React/RCTViewManager.h>

NS_ASSUME_NONNULL_BEGIN

@interface RNSZoomTransitionSourceComponentViewManager : RCTViewManager

@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#import "RNSZoomTransitionSourceComponentViewManager.h"

@implementation RNSZoomTransitionSourceComponentViewManager

RCT_EXPORT_MODULE(RNSZoomTransitionSourceViewManager)

@end
8 changes: 8 additions & 0 deletions ios/gamma/transition/RNSZoomTransitionSupporting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef RNSZoomTransitionSupporting_h
#define RNSZoomTransitionSupporting_h

@protocol RNSZoomTransitionSupporting <NSObject>

@end

#endif /* RNSZoomTransitionSupporting_h */
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@
"RNSScreenStack": "RNSScreenStackView",
"RNSSearchBar": "RNSSearchBar",
"RNSSplitViewHost": "RNSSplitViewHostComponentView",
"RNSSplitViewScreen": "RNSSplitViewScreenComponentView"
"RNSSplitViewScreen": "RNSSplitViewScreenComponentView",
"RNSZoomTransitionSource": "RNSZoomTransitionSourceComponentView"
},
"components": {
"RNSFullWindowOverlay": {
Expand Down Expand Up @@ -233,6 +234,9 @@
},
"RNSSplitViewScreen": {
"className": "RNSSplitViewScreenComponentView"
},
"RNSZoomTransitionSource": {
"className": "RNSZoomTransitionSourceComponentView"
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/components/ZoomTransitionSource.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import { ViewProps } from 'react-native';
import ZoomTransitionSourceNativeComponent from '../fabric/ZoomTransitionSourceNativeComponent';

export interface ZoomTransitionSourceProps extends ViewProps {
transitionTag: string;
}

function ZoomTransitionSource(props: ZoomTransitionSourceProps) {
return <ZoomTransitionSourceNativeComponent {...props} collapsable={false} />;
}

export default ZoomTransitionSource;
17 changes: 17 additions & 0 deletions src/fabric/ZoomTransitionSourceNativeComponent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';

import type { ViewProps } from 'react-native';
// eslint-disable-next-line @react-native/no-deep-imports
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';

// eslint-disable-next-line @typescript-eslint/ban-types
export type GenericEmptyEvent = Readonly<{}>;

export interface NativeProps extends ViewProps {
transitionTag: string;
}

export default codegenNativeComponent<NativeProps>(
'RNSZoomTransitionSource',
{},
);
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ export {
} from './components/gamma/StackScreen';
export { default as SplitViewHost } from './components/gamma/SplitViewHost';
export { default as SplitViewScreen } from './components/gamma/SplitViewScreen';
export { default as ZoomTransitionSource } from './components/ZoomTransitionSource';
Loading