mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-01-30 17:28:20 +08:00
Compare commits
11 Commits
2.0.0-beta
...
2.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26d8dc21d2 | ||
|
|
3193e7da8f | ||
|
|
cce8373a20 | ||
|
|
823d11e691 | ||
|
|
1aeba7faa1 | ||
|
|
e6ed4176cd | ||
|
|
4f792b4281 | ||
|
|
d35c523c37 | ||
|
|
67806cbbb5 | ||
|
|
6212847218 | ||
|
|
a87faf443f |
@@ -49,8 +49,9 @@ repositories {
|
||||
dependencies {
|
||||
implementation 'com.facebook.react:react-native:+'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'androidx.fragment:fragment:1.2.1'
|
||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
}
|
||||
|
||||
def configureReactNativePom(def pom) {
|
||||
|
||||
@@ -220,13 +220,6 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mIsAttached = false;
|
||||
|
||||
// fragment manager is destroyed so we can't do anything with it anymore
|
||||
mFragmentManager = null;
|
||||
// so we don't add the same screen twice after re-attach
|
||||
removeAllViews();
|
||||
// after re-attach we'll update the screen and add views again
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
@@ -14,6 +15,21 @@ import com.facebook.react.uimanager.UIManagerModule;
|
||||
|
||||
public class ScreenFragment extends Fragment {
|
||||
|
||||
protected static View recycleView(View view) {
|
||||
// screen fragments reuse view instances instead of creating new ones. In order to reuse a given
|
||||
// view it needs to be detached from the view hierarchy to allow the fragment to attach it back.
|
||||
ViewParent parent = view.getParent();
|
||||
if (parent != null) {
|
||||
((ViewGroup) parent).endViewTransition(view);
|
||||
((ViewGroup) parent).removeView(view);
|
||||
}
|
||||
// view detached from fragment manager get their visibility changed to GONE after their state is
|
||||
// dumped. Since we don't restore the state but want to reuse the view we need to change visibility
|
||||
// back to VISIBLE in order for the fragment manager to animate in the view.
|
||||
view.setVisibility(View.VISIBLE);
|
||||
return view;
|
||||
}
|
||||
|
||||
protected Screen mScreenView;
|
||||
|
||||
public ScreenFragment() {
|
||||
@@ -30,7 +46,7 @@ public class ScreenFragment extends Fragment {
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return mScreenView;
|
||||
return recycleView(mScreenView);
|
||||
}
|
||||
|
||||
public Screen getScreen() {
|
||||
@@ -56,6 +72,12 @@ public class ScreenFragment extends Fragment {
|
||||
dispatchOnAppear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
recycleView(getView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
@@ -31,11 +31,6 @@ public class ScreenStackFragment extends ScreenFragment {
|
||||
mFragment = fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAnimation(Animation animation) {
|
||||
super.startAnimation(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAnimationEnd() {
|
||||
super.onAnimationEnd();
|
||||
@@ -143,7 +138,7 @@ public class ScreenStackFragment extends ScreenFragment {
|
||||
mScreenRootView = configureView();
|
||||
}
|
||||
|
||||
return mScreenRootView;
|
||||
return recycleView(mScreenRootView);
|
||||
}
|
||||
|
||||
public boolean isDismissable() {
|
||||
|
||||
@@ -132,6 +132,11 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
return;
|
||||
}
|
||||
|
||||
AppCompatActivity activity = (AppCompatActivity) getScreenFragment().getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsHidden) {
|
||||
if (mToolbar.getParent() != null) {
|
||||
getScreenFragment().removeToolbar();
|
||||
@@ -143,7 +148,6 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
getScreenFragment().setToolbar(mToolbar);
|
||||
}
|
||||
|
||||
AppCompatActivity activity = (AppCompatActivity) getScreenFragment().getActivity();
|
||||
activity.setSupportActionBar(mToolbar);
|
||||
ActionBar actionBar = activity.getSupportActionBar();
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public class ScreenViewManager extends ViewGroupManager<Screen> {
|
||||
public void setStackPresentation(Screen view, String presentation) {
|
||||
if ("push".equals(presentation)) {
|
||||
view.setStackPresentation(Screen.StackPresentation.PUSH);
|
||||
} else if ("modal".equals(presentation) || "containedModal".equals(presentation)) {
|
||||
} else if ("modal".equals(presentation) || "containedModal".equals(presentation) || "fullScreenModal".equals(presentation) || "formSheet".equals(presentation)) {
|
||||
// at the moment Android implementation does not handle contained vs regular modals
|
||||
view.setStackPresentation(Screen.StackPresentation.MODAL);
|
||||
} else if ("transparentModal".equals(presentation) || "containedTransparentModal".equals((presentation))) {
|
||||
|
||||
@@ -27,7 +27,6 @@ function renderComponentOrThunk(componentOrThunk, props) {
|
||||
|
||||
class StackView extends React.Component {
|
||||
_removeScene = route => {
|
||||
console.warn('REMOVE SCENE');
|
||||
this.props.navigation.dispatch(StackActions.pop({ key: route.key }));
|
||||
};
|
||||
|
||||
@@ -44,7 +43,6 @@ class StackView extends React.Component {
|
||||
};
|
||||
|
||||
_onFinishTransitioning = () => {
|
||||
console.warn('FINISH TRANSITIONING');
|
||||
const { routes } = this.props.navigation.state;
|
||||
let lastRoute = routes && routes.length && routes[routes.length - 1];
|
||||
if (lastRoute) {
|
||||
@@ -206,7 +204,7 @@ class StackView extends React.Component {
|
||||
return (
|
||||
<Screen
|
||||
key={`screen_${route.key}`}
|
||||
style={options.cardStyle}
|
||||
style={[StyleSheet.absoluteFill, options.cardStyle]}
|
||||
stackAnimation={stackAnimation}
|
||||
stackPresentation={stackPresentation}
|
||||
gestureEnabled={
|
||||
|
||||
@@ -10,7 +10,9 @@ typedef NS_ENUM(NSInteger, RNSScreenStackPresentation) {
|
||||
RNSScreenStackPresentationModal,
|
||||
RNSScreenStackPresentationTransparentModal,
|
||||
RNSScreenStackPresentationContainedModal,
|
||||
RNSScreenStackPresentationContainedTransparentModal
|
||||
RNSScreenStackPresentationContainedTransparentModal,
|
||||
RNSScreenStackPresentationFullScreenModal,
|
||||
RNSScreenStackPresentationFormSheet
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSInteger, RNSScreenStackAnimation) {
|
||||
|
||||
@@ -34,11 +34,16 @@
|
||||
|
||||
- (void)reactSetFrame:(CGRect)frame
|
||||
{
|
||||
if (_active) {
|
||||
if (![self.reactViewController.parentViewController
|
||||
isKindOfClass:[UINavigationController class]]) {
|
||||
[super reactSetFrame:frame];
|
||||
}
|
||||
// ignore setFrame call from react, the frame of this view
|
||||
// is controlled by the UIViewController it is contained in
|
||||
// when screen is mounted under UINavigationController it's size is controller
|
||||
// by the navigation controller itself. That is, it is set to fill space of
|
||||
// the controller. In that case we ignore react layout system from managing
|
||||
// the screen dimentions and we wait for the screen VC to update and then we
|
||||
// pass the dimentions to ui view manager to take into account when laying out
|
||||
// subviews
|
||||
}
|
||||
|
||||
- (void)updateBounds
|
||||
@@ -75,6 +80,12 @@
|
||||
_controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
#endif
|
||||
break;
|
||||
case RNSScreenStackPresentationFullScreenModal:
|
||||
_controller.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
break;
|
||||
case RNSScreenStackPresentationFormSheet:
|
||||
_controller.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
break;
|
||||
case RNSScreenStackPresentationTransparentModal:
|
||||
_controller.modalPresentationStyle = UIModalPresentationOverFullScreen;
|
||||
break;
|
||||
@@ -310,6 +321,8 @@ RCT_EXPORT_VIEW_PROPERTY(onDismissed, RCTDirectEventBlock);
|
||||
RCT_ENUM_CONVERTER(RNSScreenStackPresentation, (@{
|
||||
@"push": @(RNSScreenStackPresentationPush),
|
||||
@"modal": @(RNSScreenStackPresentationModal),
|
||||
@"fullScreenModal": @(RNSScreenStackPresentationFullScreenModal),
|
||||
@"formSheet": @(RNSScreenStackPresentationFormSheet),
|
||||
@"containedModal": @(RNSScreenStackPresentationContainedModal),
|
||||
@"transparentModal": @(RNSScreenStackPresentationTransparentModal),
|
||||
@"containedTransparentModal": @(RNSScreenStackPresentationContainedTransparentModal)
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
#import <React/RCTTouchHandler.h>
|
||||
|
||||
@interface RNSScreenStackView () <UINavigationControllerDelegate, UIAdaptivePresentationControllerDelegate, UIGestureRecognizerDelegate>
|
||||
|
||||
@property (nonatomic) NSMutableArray<UIViewController *> *presentedModals;
|
||||
@property (nonatomic) BOOL updatingModals;
|
||||
@property (nonatomic) BOOL scheduleModalsUpdate;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenStackAnimator : NSObject <UIViewControllerAnimatedTransitioning>
|
||||
@@ -21,7 +26,6 @@
|
||||
UINavigationController *_controller;
|
||||
NSMutableArray<RNSScreenView *> *_reactSubviews;
|
||||
NSMutableSet<RNSScreenView *> *_dismissedScreens;
|
||||
NSMutableArray<UIViewController *> *_presentedModals;
|
||||
__weak RNSScreenStackManager *_manager;
|
||||
}
|
||||
|
||||
@@ -160,10 +164,20 @@
|
||||
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
// do nothing
|
||||
[self updateContainer];
|
||||
}
|
||||
|
||||
- (void)didMoveToWindow
|
||||
{
|
||||
[super didMoveToWindow];
|
||||
if (self.window) {
|
||||
// when stack is added to a window we try to update push and modal view controllers. It is
|
||||
// because modal operations are blocked by UIKit when parent VC is not mounted, so we need
|
||||
// to redo them when the stack is attached.
|
||||
[self updateContainer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setModalViewControllers:(NSArray<UIViewController *> *)controllers
|
||||
{
|
||||
// when there is no change we return immediately. This check is important because sometime we may
|
||||
@@ -197,26 +211,57 @@
|
||||
}
|
||||
}
|
||||
|
||||
// prevent re-entry
|
||||
if (_updatingModals) {
|
||||
_scheduleModalsUpdate = YES;
|
||||
return;
|
||||
}
|
||||
_updatingModals = YES;
|
||||
|
||||
__weak RNSScreenStackView *weakSelf = self;
|
||||
|
||||
void (^dispatchFinishTransitioning)(void) = ^{
|
||||
void (^afterTransitions)(void) = ^{
|
||||
if (weakSelf.onFinishTransitioning) {
|
||||
weakSelf.onFinishTransitioning(nil);
|
||||
}
|
||||
weakSelf.updatingModals = NO;
|
||||
if (weakSelf.scheduleModalsUpdate) {
|
||||
// if modals update was requested during setModalViewControllers we set scheduleModalsUpdate
|
||||
// flag in order to perform updates at a later point. Here we are done with all modals
|
||||
// transitions and check this flag again. If it was set, we reset the flag and execute updates.
|
||||
weakSelf.scheduleModalsUpdate = NO;
|
||||
[weakSelf updateContainer];
|
||||
}
|
||||
};
|
||||
|
||||
void (^finish)(void) = ^{
|
||||
UIViewController *previous = changeRootController;
|
||||
for (NSUInteger i = changeRootIndex; i < controllers.count; i++) {
|
||||
UIViewController *next = controllers[i];
|
||||
BOOL animate = (i == controllers.count - 1);
|
||||
[previous presentViewController:next
|
||||
animated:animate
|
||||
completion:animate ? dispatchFinishTransitioning : nil];
|
||||
previous = next;
|
||||
NSUInteger oldCount = weakSelf.presentedModals.count;
|
||||
if (changeRootIndex < oldCount) {
|
||||
[weakSelf.presentedModals
|
||||
removeObjectsInRange:NSMakeRange(changeRootIndex, oldCount - changeRootIndex)];
|
||||
}
|
||||
if (changeRootIndex >= controllers.count) {
|
||||
dispatchFinishTransitioning();
|
||||
if (changeRootController.view.window == nil || changeRootIndex >= controllers.count) {
|
||||
// if change controller view is not attached, presenting modals will silently fail on iOS.
|
||||
// In such a case we trigger controllers update from didMoveToWindow.
|
||||
// We also don't run any present transitions if changeRootIndex is greater or equal to the size
|
||||
// of new controllers array. This means that no new controllers should be presented.
|
||||
afterTransitions();
|
||||
return;
|
||||
} else {
|
||||
UIViewController *previous = changeRootController;
|
||||
for (NSUInteger i = changeRootIndex; i < controllers.count; i++) {
|
||||
UIViewController *next = controllers[i];
|
||||
BOOL lastModal = (i == controllers.count - 1);
|
||||
[previous presentViewController:next
|
||||
animated:lastModal
|
||||
completion:^{
|
||||
[weakSelf.presentedModals addObject:next];
|
||||
if (lastModal) {
|
||||
afterTransitions();
|
||||
};
|
||||
}];
|
||||
previous = next;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -227,7 +272,6 @@
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
[_presentedModals setArray:controllers];
|
||||
}
|
||||
|
||||
- (void)setPushViewControllers:(NSArray<UIViewController *> *)controllers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-screens",
|
||||
"version": "2.0.0-beta.3",
|
||||
"version": "2.0.0-beta.7",
|
||||
"description": "First incomplete navigation solution for your react-native app.",
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
|
||||
2
src/screens.d.ts
vendored
2
src/screens.d.ts
vendored
@@ -15,7 +15,7 @@ declare module 'react-native-screens' {
|
||||
export function enableScreens(shouldEnableScreens?: boolean): void;
|
||||
export function screensEnabled(): boolean;
|
||||
|
||||
export type StackPresentationTypes = 'push' | 'modal' | 'transparentModal';
|
||||
export type StackPresentationTypes = 'push' | 'modal' | 'transparentModal' | 'fullScreenModal' | 'formSheet';
|
||||
export type StackAnimationTypes = 'default' | 'fade' | 'flip' | 'none';
|
||||
|
||||
export interface ScreenProps extends ViewProps {
|
||||
|
||||
Reference in New Issue
Block a user