mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-03-26 09:14:22 +08:00
Trigger transition finish event from native stack (#330)
This change adds new event that gets dispatched from native stack when screen transitioning is finished. The new event is used by react-navigation to update the library internal state that the triggered action has been finished. Previously we were relying solely on onAppear and onDisappear events, however those does not get triggered when new iOS 13 modals are used or when transparent modals are displayed. This is because with transparent modals the view below is still visible. We therefore needed another way of notifying the library that screen transition have finished despite the fact that disappear event couldn't be triggered. As a part of this change I also refactored invalid ref cycle-break code on iOS which was ought to remove reference cycle between view and view controller. This code have been moved to viewWillDisappear callback. Also on Android part small refactoring has been done and we removed the necessity to keep mActiveScreens array which was occasionally getting out of sync with the list of active fragments.
This commit is contained in:
committed by
GitHub
parent
24b70abd64
commit
102880c18b
@@ -11,7 +11,6 @@ import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.facebook.react.bridge.GuardedRunnable;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
@@ -47,7 +46,7 @@ public class Screen extends ViewGroup {
|
||||
}
|
||||
};
|
||||
|
||||
private @Nullable Fragment mFragment;
|
||||
private @Nullable ScreenFragment mFragment;
|
||||
private @Nullable ScreenContainer mContainer;
|
||||
private boolean mActive;
|
||||
private boolean mTransitioning;
|
||||
@@ -55,6 +54,14 @@ public class Screen extends ViewGroup {
|
||||
private StackAnimation mStackAnimation = StackAnimation.DEFAULT;
|
||||
private boolean mGestureEnabled = true;
|
||||
|
||||
@Override
|
||||
protected void onAnimationEnd() {
|
||||
super.onAnimationEnd();
|
||||
if (mFragment != null) {
|
||||
mFragment.onViewAnimationEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public Screen(ReactContext context) {
|
||||
super(context);
|
||||
// we set layout params as WindowManager.LayoutParams to workaround the issue with TextInputs
|
||||
@@ -168,11 +175,11 @@ public class Screen extends ViewGroup {
|
||||
mContainer = container;
|
||||
}
|
||||
|
||||
protected void setFragment(Fragment fragment) {
|
||||
protected void setFragment(ScreenFragment fragment) {
|
||||
mFragment = fragment;
|
||||
}
|
||||
|
||||
protected @Nullable Fragment getFragment() {
|
||||
protected @Nullable ScreenFragment getFragment() {
|
||||
return mFragment;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
@@ -22,15 +23,12 @@ import java.util.Set;
|
||||
public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
|
||||
protected final ArrayList<T> mScreenFragments = new ArrayList<>();
|
||||
private final Set<ScreenFragment> mActiveScreenFragments = new HashSet<>();
|
||||
private final ArrayList<Runnable> mAfterTransitionRunnables = new ArrayList<>(1);
|
||||
|
||||
protected @Nullable FragmentManager mFragmentManager;
|
||||
private @Nullable FragmentTransaction mCurrentTransaction;
|
||||
private @Nullable FragmentTransaction mProcessingTransaction;
|
||||
private boolean mNeedUpdate;
|
||||
private boolean mIsAttached;
|
||||
private boolean mIsTransitioning;
|
||||
private boolean mLayoutEnqueued = false;
|
||||
|
||||
|
||||
@@ -118,36 +116,6 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startViewTransition(View view) {
|
||||
super.startViewTransition(view);
|
||||
mIsTransitioning = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endViewTransition(View view) {
|
||||
super.endViewTransition(view);
|
||||
if (mIsTransitioning) {
|
||||
mIsTransitioning = false;
|
||||
notifyTransitionFinished();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTransitioning() {
|
||||
return mIsTransitioning || mProcessingTransaction != null;
|
||||
}
|
||||
|
||||
public void postAfterTransition(Runnable runnable) {
|
||||
mAfterTransitionRunnables.add(runnable);
|
||||
}
|
||||
|
||||
protected void notifyTransitionFinished() {
|
||||
for (int i = 0, size = mAfterTransitionRunnables.size(); i < size; i++) {
|
||||
mAfterTransitionRunnables.get(i).run();
|
||||
}
|
||||
mAfterTransitionRunnables.clear();
|
||||
}
|
||||
|
||||
protected int getScreenCount() {
|
||||
return mScreenFragments.size();
|
||||
}
|
||||
@@ -219,7 +187,6 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
|
||||
private void attachScreen(ScreenFragment screenFragment) {
|
||||
getOrCreateTransaction().add(getId(), screenFragment);
|
||||
mActiveScreenFragments.add(screenFragment);
|
||||
}
|
||||
|
||||
private void moveToFront(ScreenFragment screenFragment) {
|
||||
@@ -230,7 +197,6 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
|
||||
private void detachScreen(ScreenFragment screenFragment) {
|
||||
getOrCreateTransaction().remove(screenFragment);
|
||||
mActiveScreenFragments.remove(screenFragment);
|
||||
}
|
||||
|
||||
protected boolean isScreenActive(ScreenFragment screenFragment) {
|
||||
@@ -259,7 +225,6 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
mFragmentManager = null;
|
||||
// so we don't add the same screen twice after re-attach
|
||||
removeAllViews();
|
||||
mActiveScreenFragments.clear();
|
||||
// after re-attach we'll update the screen and add views again
|
||||
markUpdated();
|
||||
}
|
||||
@@ -282,11 +247,11 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
|
||||
protected void onUpdate() {
|
||||
// detach screens that are no longer active
|
||||
Set<ScreenFragment> orphaned = new HashSet<>(mActiveScreenFragments);
|
||||
Set<Fragment> orphaned = new HashSet<>(mFragmentManager.getFragments());
|
||||
for (int i = 0, size = mScreenFragments.size(); i < size; i++) {
|
||||
ScreenFragment screenFragment = mScreenFragments.get(i);
|
||||
boolean isActive = isScreenActive(screenFragment);
|
||||
if (!isActive && mActiveScreenFragments.contains(screenFragment)) {
|
||||
if (!isActive && screenFragment.isAdded()) {
|
||||
detachScreen(screenFragment);
|
||||
}
|
||||
orphaned.remove(screenFragment);
|
||||
@@ -312,7 +277,7 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
for (int i = 0, size = mScreenFragments.size(); i < size; i++) {
|
||||
ScreenFragment screenFragment = mScreenFragments.get(i);
|
||||
boolean isActive = isScreenActive(screenFragment);
|
||||
if (isActive && !mActiveScreenFragments.contains(screenFragment)) {
|
||||
if (isActive && !screenFragment.isAdded()) {
|
||||
addedBefore = true;
|
||||
attachScreen(screenFragment);
|
||||
} else if (isActive && addedBefore) {
|
||||
|
||||
@@ -45,19 +45,15 @@ public class ScreenFragment extends Fragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ScreenContainer container = mScreenView.getContainer();
|
||||
if (container.isTransitioning()) {
|
||||
container.postAfterTransition(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dispatchOnAppear();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
dispatchOnAppear();
|
||||
}
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
public void onViewAnimationEnd() {
|
||||
// onViewAnimationEnd is triggered from View#onAnimationEnd method of the fragment's root view.
|
||||
// We override Screen#onAnimationEnd and an appropriate method of the StackFragment's root view
|
||||
// in order to achieve this.
|
||||
dispatchOnAppear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.swmansion.rnscreens;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -18,6 +22,7 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
private final Set<ScreenStackFragment> mDismissed = new HashSet<>();
|
||||
|
||||
private ScreenStackFragment mTopScreen = null;
|
||||
private boolean mRemovalTransitionStarted = false;
|
||||
|
||||
private final FragmentManager.OnBackStackChangedListener mBackStackListener = new FragmentManager.OnBackStackChangedListener() {
|
||||
@Override
|
||||
@@ -90,6 +95,34 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
mFragmentManager.registerFragmentLifecycleCallbacks(mLifecycleCallbacks, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startViewTransition(View view) {
|
||||
super.startViewTransition(view);
|
||||
mRemovalTransitionStarted = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endViewTransition(View view) {
|
||||
super.endViewTransition(view);
|
||||
if (mRemovalTransitionStarted) {
|
||||
mRemovalTransitionStarted = false;
|
||||
dispatchOnFinishTransitioning();
|
||||
}
|
||||
}
|
||||
|
||||
public void onViewAppearTransitionEnd() {
|
||||
if (!mRemovalTransitionStarted) {
|
||||
dispatchOnFinishTransitioning();
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchOnFinishTransitioning() {
|
||||
((ReactContext) getContext())
|
||||
.getNativeModule(UIManagerModule.class)
|
||||
.getEventDispatcher()
|
||||
.dispatchEvent(new StackFinishTransitioningEvent(getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeScreenAt(int index) {
|
||||
Screen toBeRemoved = getScreenAt(index);
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package com.swmansion.rnscreens;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
@@ -18,6 +22,27 @@ import com.google.android.material.appbar.AppBarLayout;
|
||||
|
||||
public class ScreenStackFragment extends ScreenFragment {
|
||||
|
||||
private static class NotifyingCoordinatorLayout extends CoordinatorLayout {
|
||||
|
||||
private final ScreenFragment mFragment;
|
||||
|
||||
public NotifyingCoordinatorLayout(@NonNull Context context, ScreenFragment fragment) {
|
||||
super(context);
|
||||
mFragment = fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAnimation(Animation animation) {
|
||||
super.startAnimation(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAnimationEnd() {
|
||||
super.onAnimationEnd();
|
||||
mFragment.onViewAnimationEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private static final float TOOLBAR_ELEVATION = PixelUtil.toPixelFromDIP(4);
|
||||
|
||||
private AppBarLayout mAppBarLayout;
|
||||
@@ -62,7 +87,7 @@ public class ScreenStackFragment extends ScreenFragment {
|
||||
}
|
||||
|
||||
private CoordinatorLayout configureView() {
|
||||
CoordinatorLayout view = new CoordinatorLayout(getContext());
|
||||
CoordinatorLayout view = new NotifyingCoordinatorLayout(getContext(), this);
|
||||
CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
|
||||
params.setBehavior(new AppBarLayout.ScrollingViewBehavior());
|
||||
@@ -86,6 +111,30 @@ public class ScreenStackFragment extends ScreenFragment {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAnimationEnd() {
|
||||
super.onViewAnimationEnd();
|
||||
notifyViewAppearTransitionEnd();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
|
||||
if (enter && transit == 0) {
|
||||
// this means that the fragment will appear without transition, in this case onViewAnimationEnd
|
||||
// won't be called and we need to notify stack directly from here.
|
||||
notifyViewAppearTransitionEnd();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void notifyViewAppearTransitionEnd() {
|
||||
ViewParent screenStack = getView().getParent();
|
||||
if (screenStack instanceof ScreenStack) {
|
||||
((ScreenStack) screenStack).onViewAppearTransitionEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
|
||||
@@ -69,6 +69,8 @@ public class ScreenViewManager extends ViewGroupManager<Screen> {
|
||||
ScreenDismissedEvent.EVENT_NAME,
|
||||
MapBuilder.of("registrationName", "onDismissed"),
|
||||
ScreenAppearEvent.EVENT_NAME,
|
||||
MapBuilder.of("registrationName", "onAppear"));
|
||||
MapBuilder.of("registrationName", "onAppear"),
|
||||
StackFinishTransitioningEvent.EVENT_NAME,
|
||||
MapBuilder.of("registrationName", "onFinishTransitioning"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.swmansion.rnscreens;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
public class StackFinishTransitioningEvent extends Event<ScreenAppearEvent> {
|
||||
public static final String EVENT_NAME = "topFinishTransitioning";
|
||||
|
||||
public StackFinishTransitioningEvent(int viewId) {
|
||||
super(viewId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return EVENT_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCoalescingKey() {
|
||||
// All events for a given view can be coalesced.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), Arguments.createMap());
|
||||
}
|
||||
}
|
||||
@@ -27,18 +27,36 @@ function renderComponentOrThunk(componentOrThunk, props) {
|
||||
|
||||
class StackView extends React.Component {
|
||||
_removeScene = route => {
|
||||
console.warn('REMOVE SCENE');
|
||||
this.props.navigation.dispatch(StackActions.pop({ key: route.key }));
|
||||
};
|
||||
|
||||
_onSceneFocus = (route, descriptor) => {
|
||||
_onAppear = (route, descriptor) => {
|
||||
descriptor.options &&
|
||||
descriptor.options.onAppear &&
|
||||
descriptor.options.onAppear();
|
||||
this.props.navigation.dispatch(
|
||||
StackActions.completeTransition({ toChildKey: route.key })
|
||||
StackActions.completeTransition({
|
||||
toChildKey: route.key,
|
||||
key: this.props.navigation.state.key,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
_onFinishTransitioning = () => {
|
||||
console.warn('FINISH TRANSITIONING');
|
||||
const { routes } = this.props.navigation.state;
|
||||
let lastRoute = routes && routes.length && routes[routes.length - 1];
|
||||
if (lastRoute) {
|
||||
this.props.navigation.dispatch(
|
||||
StackActions.completeTransition({
|
||||
toChildKey: lastRoute.key,
|
||||
key: this.props.navigation.state.key,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
_renderHeaderConfig = (index, route, descriptor) => {
|
||||
const { navigationConfig } = this.props;
|
||||
const { options } = descriptor;
|
||||
@@ -194,7 +212,7 @@ class StackView extends React.Component {
|
||||
gestureEnabled={
|
||||
options.gestureEnabled === undefined ? true : options.gestureEnabled
|
||||
}
|
||||
onAppear={() => this._onSceneFocus(route, descriptor)}
|
||||
onAppear={() => this._onAppear(route, descriptor)}
|
||||
onDismissed={() => this._removeScene(route)}>
|
||||
{this._renderHeaderConfig(index, route, descriptor)}
|
||||
<SceneView
|
||||
@@ -210,7 +228,9 @@ class StackView extends React.Component {
|
||||
const { navigation, descriptors } = this.props;
|
||||
|
||||
return (
|
||||
<ScreenStack style={styles.scenes}>
|
||||
<ScreenStack
|
||||
style={styles.scenes}
|
||||
onFinishTransitioning={this._onFinishTransitioning}>
|
||||
{navigation.state.routes.map((route, i) =>
|
||||
this._renderScene(i, route, descriptors[route.key])
|
||||
)}
|
||||
|
||||
@@ -191,6 +191,19 @@
|
||||
[_touchHandler reset];
|
||||
}
|
||||
|
||||
- (BOOL)presentationControllerShouldDismiss:(UIPresentationController *)presentationController
|
||||
{
|
||||
return _gestureEnabled;
|
||||
}
|
||||
|
||||
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
|
||||
{
|
||||
if ([_reactSuperview respondsToSelector:@selector(presentationControllerDidDismiss:)]) {
|
||||
[_reactSuperview performSelector:@selector(presentationControllerDidDismiss:)
|
||||
withObject:presentationController];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreen {
|
||||
@@ -247,6 +260,8 @@
|
||||
if (self.parentViewController == nil && self.presentingViewController == nil) {
|
||||
// screen dismissed, send event
|
||||
[((RNSScreenView *)self.view) notifyDismissed];
|
||||
_view = self.view;
|
||||
self.view = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
@interface RNSScreenStackView : UIView <RNSScreenContainerDelegate, RCTInvalidating>
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onFinishTransitioning;
|
||||
|
||||
- (void)markChildUpdated;
|
||||
- (void)didUpdateChildren;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#import <React/RCTRootContentView.h>
|
||||
#import <React/RCTTouchHandler.h>
|
||||
|
||||
@interface RNSScreenStackView () <UINavigationControllerDelegate, UIGestureRecognizerDelegate>
|
||||
@interface RNSScreenStackView () <UINavigationControllerDelegate, UIAdaptivePresentationControllerDelegate, UIGestureRecognizerDelegate>
|
||||
@end
|
||||
|
||||
@interface RNSScreenStackAnimator : NSObject <UIViewControllerAnimatedTransitioning>
|
||||
@@ -22,7 +22,6 @@
|
||||
NSMutableArray<RNSScreenView *> *_reactSubviews;
|
||||
NSMutableSet<RNSScreenView *> *_dismissedScreens;
|
||||
NSMutableArray<UIViewController *> *_presentedModals;
|
||||
__weak UIViewController* recentPopped;
|
||||
__weak RNSScreenStackManager *_manager;
|
||||
}
|
||||
|
||||
@@ -64,15 +63,36 @@
|
||||
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
|
||||
{
|
||||
for (NSUInteger i = _reactSubviews.count; i > 0; i--) {
|
||||
if ([viewController isEqual:[_reactSubviews objectAtIndex:i - 1].controller]) {
|
||||
RNSScreenView *screenView = [_reactSubviews objectAtIndex:i - 1];
|
||||
if ([viewController isEqual:screenView.controller]) {
|
||||
break;
|
||||
} else {
|
||||
[_dismissedScreens addObject:[_reactSubviews objectAtIndex:i - 1]];
|
||||
} else if (screenView.stackPresentation == RNSScreenStackPresentationPush) {
|
||||
[_dismissedScreens addObject:screenView];
|
||||
}
|
||||
}
|
||||
if (recentPopped != nil) {
|
||||
recentPopped.view = nil;
|
||||
recentPopped = nil;
|
||||
if (self.onFinishTransitioning) {
|
||||
self.onFinishTransitioning(nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
|
||||
{
|
||||
// We don't directly set presentation delegate but instead rely on the ScreenView's delegate to
|
||||
// forward certain calls to the container (Stack).
|
||||
UIView *screenView = presentationController.presentedViewController.view;
|
||||
if ([screenView isKindOfClass:[RNSScreenView class]]) {
|
||||
[_dismissedScreens addObject:(RNSScreenView *)screenView];
|
||||
[_presentedModals removeObject:presentationController.presentedViewController];
|
||||
if (self.onFinishTransitioning) {
|
||||
// instead of directly triggering onFinishTransitioning this time we enqueue the event on the
|
||||
// main queue. We do that because onDismiss event is also enqueued and we want for the transition
|
||||
// finish event to arrive later than onDismiss (see RNSScreen#notifyDismiss)
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self.onFinishTransitioning) {
|
||||
self.onFinishTransitioning(nil);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +103,6 @@
|
||||
screen = (RNSScreenView *) toVC.view;
|
||||
} else if (operation == UINavigationControllerOperationPop) {
|
||||
screen = (RNSScreenView *) fromVC.view;
|
||||
recentPopped = fromVC;
|
||||
}
|
||||
if (screen != nil && (screen.stackAnimation == RNSScreenStackAnimationFade || screen.stackAnimation == RNSScreenStackAnimationNone)) {
|
||||
return [[RNSScreenStackAnimator alloc] initWithOperation:operation];
|
||||
@@ -123,11 +142,13 @@
|
||||
RCTLogError(@"ScreenStack only accepts children of type Screen");
|
||||
return;
|
||||
}
|
||||
subview.reactSuperview = self;
|
||||
[_reactSubviews insertObject:subview atIndex:atIndex];
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RNSScreenView *)subview
|
||||
{
|
||||
subview.reactSuperview = nil;
|
||||
[_reactSubviews removeObject:subview];
|
||||
[_dismissedScreens removeObject:subview];
|
||||
}
|
||||
@@ -176,15 +197,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
__weak RNSScreenStackView *weakSelf = self;
|
||||
|
||||
void (^dispatchFinishTransitioning)(void) = ^{
|
||||
if (weakSelf.onFinishTransitioning) {
|
||||
weakSelf.onFinishTransitioning(nil);
|
||||
}
|
||||
};
|
||||
|
||||
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:(i == controllers.count - 1)
|
||||
completion:nil];
|
||||
animated:animate
|
||||
completion:animate ? dispatchFinishTransitioning : nil];
|
||||
previous = next;
|
||||
}
|
||||
if (changeRootIndex >= controllers.count) {
|
||||
dispatchFinishTransitioning();
|
||||
}
|
||||
};
|
||||
|
||||
if (changeRootController.presentedViewController) {
|
||||
@@ -299,8 +332,7 @@
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(transitioning, NSInteger)
|
||||
RCT_EXPORT_VIEW_PROPERTY(progress, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onFinishTransitioning, RCTDirectEventBlock);
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user