mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-03-27 01:34:25 +08:00
Compare commits
27 Commits
2.0.0-alph
...
2.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
124e8acb2d | ||
|
|
2c5f95cea6 | ||
|
|
0a2336d005 | ||
|
|
58d1791d4a | ||
|
|
3d56c5d4e2 | ||
|
|
47658d4d7d | ||
|
|
258ae419de | ||
|
|
62123f16f9 | ||
|
|
a94424192b | ||
|
|
752d6c0f04 | ||
|
|
a017713efc | ||
|
|
26384b625e | ||
|
|
5d5e8bfca6 | ||
|
|
2536837795 | ||
|
|
20650a8ede | ||
|
|
ee0dbfe8ae | ||
|
|
23cbc009d4 | ||
|
|
f21a093918 | ||
|
|
1d4712acbd | ||
|
|
09c71a45a2 | ||
|
|
79e664f11d | ||
|
|
b622abc935 | ||
|
|
7d4bbb8f88 | ||
|
|
7c304a007f | ||
|
|
adf3333462 | ||
|
|
d4636d3130 | ||
|
|
4749405d64 |
@@ -140,6 +140,7 @@ A callback that gets called when the current screen is dismissed by hardware bac
|
||||
Allows for the customization of how the given screen should appear/dissapear when pushed or popped at the top of the stack. The followin values are currently supported:
|
||||
- `"default"` – uses a platform default animation
|
||||
- `"fade"` – fades screen in or out
|
||||
- `"flip"` – flips the screen, requires `stackPresentation: "modal"` (iOS only)
|
||||
- `"none"` – the screen appears/dissapears without an animation
|
||||
|
||||
#### `stackPresentation`
|
||||
|
||||
@@ -91,12 +91,12 @@ afterEvaluate { project ->
|
||||
}
|
||||
|
||||
task androidJavadocJar(type: Jar, dependsOn: androidJavadoc) {
|
||||
archiveClassifier.set('javadoc')
|
||||
classifier = 'javadoc'
|
||||
from androidJavadoc.destinationDir
|
||||
}
|
||||
|
||||
task androidSourcesJar(type: Jar) {
|
||||
archiveClassifier.set('sources')
|
||||
classifier = 'sources'
|
||||
from android.sourceSets.main.java.srcDirs
|
||||
include '**/*.java'
|
||||
}
|
||||
|
||||
@@ -17,5 +17,6 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.useDeprecatedNdk=true
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.swmansion.rnscreens;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
@@ -13,7 +12,6 @@ import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.facebook.react.bridge.GuardedRunnable;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.uimanager.MeasureSpecAssertions;
|
||||
import com.facebook.react.uimanager.PointerEvents;
|
||||
import com.facebook.react.uimanager.ReactPointerEventsView;
|
||||
import com.facebook.react.uimanager.UIManagerModule;
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.view.ViewParent;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.facebook.react.ReactRootView;
|
||||
@@ -25,21 +26,45 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
private @Nullable FragmentTransaction mCurrentTransaction;
|
||||
private boolean mNeedUpdate;
|
||||
private boolean mIsAttached;
|
||||
private boolean mLayoutEnqueued = false;
|
||||
|
||||
private ChoreographerCompat.FrameCallback mFrameCallback = new ChoreographerCompat.FrameCallback() {
|
||||
private final ChoreographerCompat.FrameCallback mFrameCallback = new ChoreographerCompat.FrameCallback() {
|
||||
@Override
|
||||
public void doFrame(long frameTimeNanos) {
|
||||
updateIfNeeded();
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mLayoutRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mLayoutEnqueued = false;
|
||||
measure(
|
||||
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
|
||||
layout(getLeft(), getTop(), getRight(), getBottom());
|
||||
}
|
||||
};
|
||||
|
||||
public ScreenContainer(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
|
||||
// no-op
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
for (int i = 0, size = getChildCount(); i < size; i++) {
|
||||
getChildAt(i).layout(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestLayout() {
|
||||
super.requestLayout();
|
||||
|
||||
if (!mLayoutEnqueued) {
|
||||
mLayoutEnqueued = true;
|
||||
post(mLayoutRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
protected void markUpdated() {
|
||||
@@ -109,9 +134,13 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
return (FragmentActivity) context;
|
||||
}
|
||||
|
||||
protected FragmentManager getFragmentManager() {
|
||||
return findRootFragmentActivity().getSupportFragmentManager();
|
||||
}
|
||||
|
||||
protected FragmentTransaction getOrCreateTransaction() {
|
||||
if (mCurrentTransaction == null) {
|
||||
mCurrentTransaction = findRootFragmentActivity().getSupportFragmentManager().beginTransaction();
|
||||
mCurrentTransaction = getFragmentManager().beginTransaction();
|
||||
mCurrentTransaction.setReorderingAllowed(true);
|
||||
}
|
||||
return mCurrentTransaction;
|
||||
@@ -157,6 +186,14 @@ public class ScreenContainer<T extends ScreenFragment> extends ViewGroup {
|
||||
mIsAttached = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
for (int i = 0, size = getChildCount(); i < size; i++) {
|
||||
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateIfNeeded() {
|
||||
if (!mNeedUpdate || !mIsAttached) {
|
||||
return;
|
||||
|
||||
@@ -43,4 +43,9 @@ public class ScreenContainerViewManager extends ViewGroupManager<ScreenContainer
|
||||
public View getChildAt(ScreenContainer parent, int index) {
|
||||
return parent.getScreenAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsCustomLayoutForChildren() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.swmansion.rnscreens;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -10,11 +11,24 @@ import java.util.Set;
|
||||
|
||||
public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
|
||||
private static final String BACK_STACK_TAG = "RN_SCREEN_LAST";
|
||||
|
||||
private final ArrayList<ScreenStackFragment> mStack = new ArrayList<>();
|
||||
private final Set<ScreenStackFragment> mDismissed = new HashSet<>();
|
||||
|
||||
private ScreenStackFragment mTopScreen = null;
|
||||
private boolean mLayoutEnqueued = false;
|
||||
|
||||
private final FragmentManager.OnBackStackChangedListener mBackStackListener = new FragmentManager.OnBackStackChangedListener() {
|
||||
@Override
|
||||
public void onBackStackChanged() {
|
||||
if (getFragmentManager().getBackStackEntryCount() == 0) {
|
||||
// when back stack entry count hits 0 it means the user's navigated back using hw back
|
||||
// button. As the "fake" transaction we installed on the back stack does nothing we need
|
||||
// to handle back navigation on our own.
|
||||
dismiss(mTopScreen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public ScreenStack(Context context) {
|
||||
super(context);
|
||||
@@ -25,6 +39,10 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
onUpdate();
|
||||
}
|
||||
|
||||
public Screen getTopScreen() {
|
||||
return mTopScreen.getScreen();
|
||||
}
|
||||
|
||||
public Screen getRootScreen() {
|
||||
for (int i = 0, size = getScreenCount(); i < size; i++) {
|
||||
Screen screen = getScreenAt(i);
|
||||
@@ -41,40 +59,17 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
for (int i = 0, size = getChildCount(); i < size; i++) {
|
||||
getChildAt(i).layout(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
getFragmentManager().removeOnBackStackChangedListener(mBackStackListener);
|
||||
getFragmentManager().popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
for (int i = 0, size = getChildCount(); i < size; i++) {
|
||||
getChildAt(i).measure(
|
||||
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
|
||||
}
|
||||
}
|
||||
|
||||
private final Runnable mLayoutRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mLayoutEnqueued = false;
|
||||
measure(
|
||||
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
|
||||
layout(getLeft(), getTop(), getRight(), getBottom());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void requestLayout() {
|
||||
super.requestLayout();
|
||||
|
||||
if (!mLayoutEnqueued) {
|
||||
mLayoutEnqueued = true;
|
||||
post(mLayoutRunnable);
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (mTopScreen != null) {
|
||||
setupBackHandlerIfNeeded(mTopScreen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,5 +164,52 @@ public class ScreenStack extends ScreenContainer<ScreenStackFragment> {
|
||||
mStack.addAll(mScreenFragments);
|
||||
|
||||
tryCommitTransaction();
|
||||
|
||||
setupBackHandlerIfNeeded(mTopScreen);
|
||||
|
||||
for (ScreenStackFragment screen : mStack) {
|
||||
screen.onStackUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The below method sets up fragment manager's back stack in a way that it'd trigger our back
|
||||
* stack change listener when hw back button is clicked.
|
||||
*
|
||||
* Because back stack by default rolls back the transaction the stack entry is associated with we
|
||||
* generate a "fake" transaction that hides and shows the top fragment. As a result when back
|
||||
* stack entry is rolled back nothing happens and we are free to handle back navigation on our
|
||||
* own in `mBackStackListener`.
|
||||
*
|
||||
* We pop that "fake" transaction each time we update stack and we add a new one in case the top
|
||||
* screen is allowed to be dismised using hw back button. This way in the listener we can tell
|
||||
* if back button was pressed based on the count of the items on back stack. We expect 0 items
|
||||
* in case hw back is pressed becakse we try to keep the number of items at 1 by always resetting
|
||||
* and adding new items. In case we don't add a new item to back stack we remove listener so that
|
||||
* it does not get triggered.
|
||||
*
|
||||
* It is important that we don't install back handler when stack contains a single screen as in
|
||||
* that case we want the parent navigator or activity handler to take over.
|
||||
*/
|
||||
private void setupBackHandlerIfNeeded(ScreenStackFragment topScreen) {
|
||||
getFragmentManager().removeOnBackStackChangedListener(mBackStackListener);
|
||||
getFragmentManager().popBackStack(BACK_STACK_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
|
||||
ScreenStackFragment firstScreen = null;
|
||||
for (int i = 0, size = mStack.size(); i < size; i++) {
|
||||
ScreenStackFragment screen = mStack.get(i);
|
||||
if (!mDismissed.contains(screen)) {
|
||||
firstScreen = screen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (topScreen != firstScreen && topScreen.isDismissable()) {
|
||||
getFragmentManager()
|
||||
.beginTransaction()
|
||||
.hide(topScreen)
|
||||
.show(topScreen)
|
||||
.addToBackStack(BACK_STACK_TAG)
|
||||
.commitAllowingStateLoss();
|
||||
getFragmentManager().addOnBackStackChangedListener(mBackStackListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,13 @@ public class ScreenStackFragment extends ScreenFragment {
|
||||
}
|
||||
}
|
||||
|
||||
public void onStackUpdate() {
|
||||
View child = mScreenView.getChildAt(0);
|
||||
if (child instanceof ScreenStackHeaderConfig) {
|
||||
((ScreenStackHeaderConfig) child).onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@@ -79,4 +86,12 @@ public class ScreenStackFragment extends ScreenFragment {
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public boolean isDismissable() {
|
||||
View child = mScreenView.getChildAt(0);
|
||||
if (child instanceof ScreenStackHeaderConfig) {
|
||||
return ((ScreenStackHeaderConfig) child).isDismissable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.swmansion.rnscreens;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.TypedValue;
|
||||
@@ -11,7 +10,6 @@ import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
@@ -29,17 +27,14 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
private int mTitleFontSize;
|
||||
private int mBackgroundColor;
|
||||
private boolean mIsHidden;
|
||||
private boolean mGestureEnabled = true;
|
||||
private boolean mIsBackButtonHidden;
|
||||
private boolean mIsShadowHidden;
|
||||
private int mTintColor;
|
||||
private final Toolbar mToolbar;
|
||||
|
||||
private OnBackPressedCallback mBackCallback = new OnBackPressedCallback(false) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
getScreenStack().dismiss(getScreenFragment());
|
||||
}
|
||||
};
|
||||
private boolean mIsAttachedToWindow = false;
|
||||
|
||||
private OnClickListener mBackClickListener = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
@@ -68,7 +63,14 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
update();
|
||||
mIsAttachedToWindow = true;
|
||||
onUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mIsAttachedToWindow = false;
|
||||
}
|
||||
|
||||
private Screen getScreen() {
|
||||
@@ -101,14 +103,20 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void installBackCallback() {
|
||||
mBackCallback.remove();
|
||||
Fragment fragment = getScreenFragment();
|
||||
fragment.requireActivity().getOnBackPressedDispatcher().addCallback(fragment, mBackCallback);
|
||||
public boolean isDismissable() {
|
||||
return mGestureEnabled;
|
||||
}
|
||||
|
||||
private void update() {
|
||||
public void onUpdate() {
|
||||
Screen parent = (Screen) getParent();
|
||||
final ScreenStack stack = getScreenStack();
|
||||
boolean isRoot = stack == null ? true : stack.getRootScreen() == parent;
|
||||
boolean isTop = stack == null ? true : stack.getTopScreen() == parent;
|
||||
|
||||
if (!mIsAttachedToWindow || !isTop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsHidden) {
|
||||
if (mToolbar.getParent() != null) {
|
||||
getScreenFragment().removeToolbar();
|
||||
@@ -125,13 +133,7 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
ActionBar actionBar = activity.getSupportActionBar();
|
||||
|
||||
// hide back button
|
||||
final ScreenStack stack = getScreenStack();
|
||||
boolean isRoot = stack == null ? true : stack.getRootScreen() == parent;
|
||||
actionBar.setDisplayHomeAsUpEnabled(isRoot ? false : !mIsBackButtonHidden);
|
||||
if (!isRoot) {
|
||||
installBackCallback();
|
||||
}
|
||||
mBackCallback.setEnabled(!isRoot);
|
||||
|
||||
// when setSupportActionBar is called a toolbar wrapper gets initialized that overwrites
|
||||
// navigation click listener. The default behavior set in the wrapper is to call into
|
||||
@@ -205,6 +207,12 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeUpdate() {
|
||||
if (getParent() != null) {
|
||||
onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public ScreenStackHeaderSubview getConfigSubview(int index) {
|
||||
return mConfigSubviews[index];
|
||||
}
|
||||
@@ -218,6 +226,7 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
mSubviewsCount--;
|
||||
}
|
||||
mConfigSubviews[index] = null;
|
||||
maybeUpdate();
|
||||
}
|
||||
|
||||
public void addConfigSubview(ScreenStackHeaderSubview child, int index) {
|
||||
@@ -225,6 +234,7 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
mSubviewsCount++;
|
||||
}
|
||||
mConfigSubviews[index] = child;
|
||||
maybeUpdate();
|
||||
}
|
||||
|
||||
private TextView getTitleTextView() {
|
||||
@@ -268,6 +278,10 @@ public class ScreenStackHeaderConfig extends ViewGroup {
|
||||
mIsShadowHidden = hideShadow;
|
||||
}
|
||||
|
||||
public void setGestureEnabled(boolean gestureEnabled) {
|
||||
mGestureEnabled = gestureEnabled;
|
||||
}
|
||||
|
||||
public void setHideBackButton(boolean hideBackButton) {
|
||||
mIsBackButtonHidden = hideBackButton;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,12 @@ public class ScreenStackHeaderConfigViewManager extends ViewGroupManager<ScreenS
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAfterUpdateTransaction(ScreenStackHeaderConfig parent) {
|
||||
super.onAfterUpdateTransaction(parent);
|
||||
parent.onUpdate();
|
||||
}
|
||||
|
||||
@ReactProp(name = "title")
|
||||
public void setTitle(ScreenStackHeaderConfig config, String title) {
|
||||
config.setTitle(title);
|
||||
@@ -82,6 +88,11 @@ public class ScreenStackHeaderConfigViewManager extends ViewGroupManager<ScreenS
|
||||
config.setHideShadow(hideShadow);
|
||||
}
|
||||
|
||||
@ReactProp(name = "gestureEnabled", defaultBoolean = true)
|
||||
public void setGestureEnabled(ScreenStackHeaderConfig config, boolean gestureEnabled) {
|
||||
config.setGestureEnabled(gestureEnabled);
|
||||
}
|
||||
|
||||
@ReactProp(name = "hideBackButton")
|
||||
public void setHideBackButton(ScreenStackHeaderConfig config, boolean hideBackButton) {
|
||||
config.setHideBackButton(hideBackButton);
|
||||
|
||||
@@ -35,9 +35,11 @@ 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)) {
|
||||
} else if ("modal".equals(presentation) || "containedModal".equals(presentation)) {
|
||||
// at the moment Android implementation does not handle contained vs regular modals
|
||||
view.setStackPresentation(Screen.StackPresentation.MODAL);
|
||||
} else if ("transparentModal".equals(presentation)) {
|
||||
} else if ("transparentModal".equals(presentation) || "containedTransparentModal".equals((presentation))) {
|
||||
// at the moment Android implementation does not handle contained vs regular modals
|
||||
view.setStackPresentation(Screen.StackPresentation.TRANSPARENT_MODAL);
|
||||
} else {
|
||||
throw new JSApplicationIllegalArgumentException("Unknown presentation type " + presentation);
|
||||
|
||||
@@ -18,6 +18,13 @@ import {
|
||||
ScreenStackHeaderTitleView,
|
||||
} from 'react-native-screens';
|
||||
|
||||
function renderComponentOrThunk(componentOrThunk, props) {
|
||||
if (typeof componentOrThunk === 'function') {
|
||||
return componentOrThunk(props);
|
||||
}
|
||||
return componentOrThunk;
|
||||
}
|
||||
|
||||
class StackView extends React.Component {
|
||||
_removeScene = route => {
|
||||
const { navigation } = this.props;
|
||||
@@ -46,6 +53,7 @@ class StackView extends React.Component {
|
||||
largeTitle,
|
||||
headerLargeTitleStyle,
|
||||
translucent,
|
||||
hideShadow,
|
||||
} = options;
|
||||
|
||||
const scene = {
|
||||
@@ -72,6 +80,7 @@ class StackView extends React.Component {
|
||||
headerLargeTitleStyle && headerLargeTitleStyle.fontFamily,
|
||||
largeTitleFontSize:
|
||||
headerLargeTitleStyle && headerLargeTitleStyle.fontSize,
|
||||
hideShadow,
|
||||
};
|
||||
|
||||
const hasHeader = headerMode !== 'none' && options.header !== null;
|
||||
@@ -88,7 +97,7 @@ class StackView extends React.Component {
|
||||
if (options.headerLeft !== undefined) {
|
||||
children.push(
|
||||
<ScreenStackHeaderLeftView key="left">
|
||||
{options.headerLeft({ scene })}
|
||||
{renderComponentOrThunk(options.headerLeft, { scene })}
|
||||
</ScreenStackHeaderLeftView>
|
||||
);
|
||||
} else if (options.headerBackImage !== undefined) {
|
||||
@@ -118,17 +127,21 @@ class StackView extends React.Component {
|
||||
}
|
||||
|
||||
if (options.headerTitle) {
|
||||
children.push(
|
||||
<ScreenStackHeaderTitleView key="title">
|
||||
{options.headerTitle({ scene })}
|
||||
</ScreenStackHeaderTitleView>
|
||||
);
|
||||
if (title === undefined && typeof options.headerTitle === 'string') {
|
||||
headerOptions.title = options.headerTitle;
|
||||
} else {
|
||||
children.push(
|
||||
<ScreenStackHeaderTitleView key="title">
|
||||
{renderComponentOrThunk(options.headerTitle, { scene })}
|
||||
</ScreenStackHeaderTitleView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.headerRight) {
|
||||
children.push(
|
||||
<ScreenStackHeaderRightView key="right">
|
||||
{options.headerRight({ scene })}
|
||||
{renderComponentOrThunk(options.headerRight, { scene })}
|
||||
</ScreenStackHeaderRightView>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ typedef NS_ENUM(NSInteger, RNSScreenStackAnimation) {
|
||||
RNSScreenStackAnimationDefault,
|
||||
RNSScreenStackAnimationNone,
|
||||
RNSScreenStackAnimationFade,
|
||||
RNSScreenStackAnimationFlip,
|
||||
};
|
||||
|
||||
@interface RCTConvert (RNSScreen)
|
||||
@@ -26,6 +27,13 @@ typedef NS_ENUM(NSInteger, RNSScreenStackAnimation) {
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreen : UIViewController
|
||||
|
||||
- (instancetype)initWithView:(UIView *)view;
|
||||
- (void)notifyFinishTransitioning;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenManager : RCTViewManager
|
||||
@end
|
||||
|
||||
|
||||
@@ -8,40 +8,6 @@
|
||||
#import <React/RCTShadowView.h>
|
||||
#import <React/RCTTouchHandler.h>
|
||||
|
||||
@interface RNSScreenFrameData : NSObject
|
||||
@property (nonatomic, readonly) CGFloat rightInset;
|
||||
@property (nonatomic, readonly) CGFloat topInset;
|
||||
@property (nonatomic, readonly) CGFloat bottomInset;
|
||||
@property (nonatomic, readonly) CGFloat leftInset;
|
||||
@property (nonatomic, readonly) CGFloat navbarOffset;
|
||||
|
||||
- (instancetype)initWithInsets:(UIEdgeInsets)insets;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RNSScreenFrameData
|
||||
|
||||
- (instancetype)initWithInsets:(UIEdgeInsets)insets andNavbarOffset:(CGFloat)navbarOffset
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_topInset = insets.top;
|
||||
_bottomInset = insets.bottom;
|
||||
_leftInset = insets.left;
|
||||
_rightInset = insets.right;
|
||||
_navbarOffset = navbarOffset;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreen : UIViewController
|
||||
|
||||
- (instancetype)initWithView:(UIView *)view;
|
||||
- (void)notifyFinishTransitioning;
|
||||
|
||||
@end
|
||||
|
||||
@interface RNSScreenView () <UIAdaptivePresentationControllerDelegate>
|
||||
@end
|
||||
|
||||
@@ -129,6 +95,9 @@
|
||||
case RNSScreenStackAnimationFade:
|
||||
_controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
|
||||
break;
|
||||
case RNSScreenStackAnimationFlip:
|
||||
_controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
|
||||
break;
|
||||
case RNSScreenStackAnimationNone:
|
||||
case RNSScreenStackAnimationDefault:
|
||||
// Default
|
||||
@@ -145,6 +114,8 @@
|
||||
{
|
||||
if (![view isKindOfClass:[RNSScreenStackHeaderConfig class]]) {
|
||||
[super addSubview:view];
|
||||
} else {
|
||||
((RNSScreenStackHeaderConfig*) view).screenView = self;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,8 +280,10 @@ RCT_ENUM_CONVERTER(RNSScreenStackPresentation, (@{
|
||||
RCT_ENUM_CONVERTER(RNSScreenStackAnimation, (@{
|
||||
@"default": @(RNSScreenStackAnimationDefault),
|
||||
@"none": @(RNSScreenStackAnimationNone),
|
||||
@"fade": @(RNSScreenStackAnimationFade)
|
||||
@"fade": @(RNSScreenStackAnimationFade),
|
||||
@"flip": @(RNSScreenStackAnimationFlip),
|
||||
}), RNSScreenStackAnimationDefault, integerValue)
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
{
|
||||
subview.reactSuperview = self;
|
||||
[_reactSubviews insertObject:subview atIndex:atIndex];
|
||||
subview.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RNSScreenView *)subview
|
||||
@@ -159,6 +160,10 @@
|
||||
[super layoutSubviews];
|
||||
[self reactAddControllerToClosestParent:_controller];
|
||||
_controller.view.frame = self.bounds;
|
||||
for (RNSScreenView *subview in _reactSubviews) {
|
||||
subview.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
|
||||
[subview setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUIManagerUtils.h>
|
||||
#import <React/RCTShadowView.h>
|
||||
#import <React/RCTRootContentView.h>
|
||||
#import <React/RCTTouchHandler.h>
|
||||
|
||||
@interface RNSScreenStackView () <UINavigationControllerDelegate, UIGestureRecognizerDelegate>
|
||||
@end
|
||||
@@ -35,6 +37,12 @@
|
||||
_needUpdate = NO;
|
||||
[self addSubview:_controller.view];
|
||||
_controller.interactivePopGestureRecognizer.delegate = self;
|
||||
|
||||
// we have to initialize viewControllers with a non empty array for
|
||||
// largeTitle header to render in the opened state. If it is empty
|
||||
// the header will render in collapsed state which is perhaps a bug
|
||||
// in UIKit but ¯\_(ツ)_/¯
|
||||
[_controller setViewControllers:@[[UIViewController new]]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -58,7 +66,6 @@
|
||||
if ([viewController isEqual:[_reactSubviews objectAtIndex:i - 1].controller]) {
|
||||
break;
|
||||
} else {
|
||||
// TODO: send dismiss event
|
||||
[_dismissedScreens addObject:[_reactSubviews objectAtIndex:i - 1]];
|
||||
}
|
||||
}
|
||||
@@ -72,7 +79,7 @@
|
||||
} else if (operation == UINavigationControllerOperationPop) {
|
||||
screen = (RNSScreenView *) fromVC.view;
|
||||
}
|
||||
if (screen != nil && screen.stackAnimation != RNSScreenStackAnimationDefault) {
|
||||
if (screen != nil && (screen.stackAnimation == RNSScreenStackAnimationFade || screen.stackAnimation == RNSScreenStackAnimationNone)) {
|
||||
return [[RNSScreenStackAnimator alloc] initWithOperation:operation];
|
||||
}
|
||||
return nil;
|
||||
@@ -85,30 +92,13 @@
|
||||
// Without the below code the Touchable will remain active (highlighted) for the duration of back
|
||||
// gesture and onPress may fire when we release the finger.
|
||||
UIView *parent = _controller.view;
|
||||
while (parent != nil && ![parent isKindOfClass:[RCTRootView class]]) parent = parent.superview;
|
||||
RCTRootView *rootView = (RCTRootView *)parent;
|
||||
[rootView cancelTouches];
|
||||
while (parent != nil && ![parent isKindOfClass:[RCTRootContentView class]]) parent = parent.superview;
|
||||
RCTRootContentView *rootView = (RCTRootContentView *)parent;
|
||||
[rootView.touchHandler cancel];
|
||||
|
||||
return _controller.viewControllers.count > 1;
|
||||
}
|
||||
|
||||
- (void)markUpdated
|
||||
{
|
||||
// We want 'updateContainer' to be executed on main thread after all enqueued operations in
|
||||
// uimanager are complete. In order to achieve that we enqueue call on UIManagerQueue from which
|
||||
// we enqueue call on the main queue. This seems to be working ok in all the cases I've tried but
|
||||
// there is a chance it is not the correct way to do that.
|
||||
if (!_needUpdate) {
|
||||
_needUpdate = YES;
|
||||
RCTExecuteOnUIManagerQueue(^{
|
||||
RCTExecuteOnMainQueue(^{
|
||||
_needUpdate = NO;
|
||||
[self updateContainer];
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)markChildUpdated
|
||||
{
|
||||
// do nothing
|
||||
@@ -126,14 +116,12 @@
|
||||
return;
|
||||
}
|
||||
[_reactSubviews insertObject:subview atIndex:atIndex];
|
||||
[self markUpdated];
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RNSScreenView *)subview
|
||||
{
|
||||
[_reactSubviews removeObject:subview];
|
||||
[_dismissedScreens removeObject:subview];
|
||||
[self markUpdated];
|
||||
}
|
||||
|
||||
- (NSArray<UIView *> *)reactSubviews
|
||||
@@ -144,6 +132,7 @@
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
// do nothing
|
||||
[self updateContainer];
|
||||
}
|
||||
|
||||
- (void)setModalViewControllers:(NSArray<UIViewController *> *)controllers
|
||||
@@ -186,11 +175,17 @@
|
||||
UIViewController *top = controllers.lastObject;
|
||||
UIViewController *lastTop = _controller.viewControllers.lastObject;
|
||||
|
||||
BOOL shouldAnimate = ((RNSScreenView *) lastTop.view).stackAnimation != RNSScreenStackAnimationNone;
|
||||
// at the start we set viewControllers to contain a single UIVIewController
|
||||
// instance. This is a workaround for header height adjustment bug (see comment
|
||||
// in the init function). Here, we need to detect if the initial empty
|
||||
// controller is still there
|
||||
BOOL firstTimePush = ![lastTop isKindOfClass:[RNSScreen class]];
|
||||
|
||||
if (_controller.viewControllers.count == 0) {
|
||||
BOOL shouldAnimate = !firstTimePush && ((RNSScreenView *) lastTop.view).stackAnimation != RNSScreenStackAnimationNone;
|
||||
|
||||
if (firstTimePush) {
|
||||
// nothing pushed yet
|
||||
[_controller setViewControllers:@[top] animated:NO];
|
||||
[_controller setViewControllers:controllers animated:NO];
|
||||
} else if (top != lastTop) {
|
||||
if (![controllers containsObject:lastTop]) {
|
||||
// last top controller is no longer on stack
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#import <React/RCTViewManager.h>
|
||||
#import <React/RCTConvert.h>
|
||||
|
||||
#import "RNSScreen.h"
|
||||
|
||||
@interface RNSScreenStackHeaderConfig : UIView
|
||||
|
||||
@property (nonatomic, weak) RNSScreenView *screenView;
|
||||
|
||||
@property (nonatomic, retain) NSString *title;
|
||||
@property (nonatomic, retain) NSString *titleFontFamily;
|
||||
@property (nonatomic, retain) NSNumber *titleFontSize;
|
||||
|
||||
@@ -66,60 +66,89 @@
|
||||
return _reactSubviews;
|
||||
}
|
||||
|
||||
- (UIViewController*)screen
|
||||
- (UIView *)reactSuperview
|
||||
{
|
||||
UIView *superview = self.superview;
|
||||
if ([superview isKindOfClass:[RNSScreenView class]]) {
|
||||
return ((RNSScreenView *)superview).controller;
|
||||
return _screenView;
|
||||
}
|
||||
|
||||
- (void)removeFromSuperview
|
||||
{
|
||||
[super removeFromSuperview];
|
||||
_screenView = nil;
|
||||
}
|
||||
|
||||
- (void)updateViewControllerIfNeeded
|
||||
{
|
||||
UIViewController *vc = _screenView.controller;
|
||||
UINavigationController *nav = (UINavigationController*) vc.parentViewController;
|
||||
if (vc != nil && nav.visibleViewController == vc) {
|
||||
[RNSScreenStackHeaderConfig updateViewController:self.screenView.controller withConfig:self];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)didSetProps:(NSArray<NSString *> *)changedProps
|
||||
{
|
||||
[super didSetProps:changedProps];
|
||||
[self updateViewControllerIfNeeded];
|
||||
}
|
||||
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
[super didUpdateReactSubviews];
|
||||
[self updateViewControllerIfNeeded];
|
||||
}
|
||||
|
||||
+ (void)setAnimatedConfig:(UIViewController *)vc withConfig:(RNSScreenStackHeaderConfig *)config
|
||||
{
|
||||
UINavigationBar *navbar = ((UINavigationController *)vc.parentViewController).navigationBar;
|
||||
BOOL hideShadow = config.hideShadow;
|
||||
[navbar setTintColor:config.color];
|
||||
if (config.backgroundColor && CGColorGetAlpha(config.backgroundColor.CGColor) == 0.) {
|
||||
[navbar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
|
||||
[navbar setBarTintColor:[UIColor clearColor]];
|
||||
hideShadow = YES;
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
// font customized on the navigation item level, so nothing to do here
|
||||
} else {
|
||||
[navbar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
|
||||
[navbar setBarTintColor:config.backgroundColor];
|
||||
}
|
||||
[navbar setTranslucent:config.translucent];
|
||||
[navbar setValue:@(hideShadow ? YES : NO) forKey:@"hidesShadow"];
|
||||
BOOL hideShadow = config.hideShadow;
|
||||
|
||||
if (config.titleFontFamily || config.titleFontSize || config.titleColor) {
|
||||
NSMutableDictionary *attrs = [NSMutableDictionary new];
|
||||
|
||||
if (config.titleColor) {
|
||||
attrs[NSForegroundColorAttributeName] = config.titleColor;
|
||||
}
|
||||
|
||||
CGFloat size = config.titleFontSize ? [config.titleFontSize floatValue] : 17;
|
||||
if (config.titleFontFamily) {
|
||||
attrs[NSFontAttributeName] = [UIFont fontWithName:config.titleFontFamily size:size];
|
||||
if (config.backgroundColor && CGColorGetAlpha(config.backgroundColor.CGColor) == 0.) {
|
||||
[navbar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
|
||||
[navbar setBarTintColor:[UIColor clearColor]];
|
||||
hideShadow = YES;
|
||||
} else {
|
||||
attrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:size];
|
||||
[navbar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
|
||||
[navbar setBarTintColor:config.backgroundColor];
|
||||
}
|
||||
[navbar setTitleTextAttributes:attrs];
|
||||
}
|
||||
|
||||
if (@available(iOS 11.0, *) && config.largeTitle) {
|
||||
if (config.largeTitleFontFamily || config.largeTitleFontSize || config.titleColor) {
|
||||
NSMutableDictionary *largeAttrs = [NSMutableDictionary new];
|
||||
[navbar setTranslucent:config.translucent];
|
||||
[navbar setValue:@(hideShadow ? YES : NO) forKey:@"hidesShadow"];
|
||||
|
||||
if (config.titleFontFamily || config.titleFontSize || config.titleColor) {
|
||||
NSMutableDictionary *attrs = [NSMutableDictionary new];
|
||||
|
||||
if (config.titleColor) {
|
||||
largeAttrs[NSForegroundColorAttributeName] = config.titleColor;
|
||||
attrs[NSForegroundColorAttributeName] = config.titleColor;
|
||||
}
|
||||
CGFloat largeSize = config.largeTitleFontSize ? [config.largeTitleFontSize floatValue] : 34;
|
||||
if (config.largeTitleFontFamily) {
|
||||
largeAttrs[NSFontAttributeName] = [UIFont fontWithName:config.largeTitleFontFamily size:largeSize];
|
||||
|
||||
CGFloat size = config.titleFontSize ? [config.titleFontSize floatValue] : 17;
|
||||
if (config.titleFontFamily) {
|
||||
attrs[NSFontAttributeName] = [UIFont fontWithName:config.titleFontFamily size:size];
|
||||
} else {
|
||||
largeAttrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:largeSize];
|
||||
attrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:size];
|
||||
}
|
||||
[navbar setTitleTextAttributes:attrs];
|
||||
}
|
||||
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (config.largeTitle && (config.largeTitleFontFamily || config.largeTitleFontSize || config.titleColor)) {
|
||||
NSMutableDictionary *largeAttrs = [NSMutableDictionary new];
|
||||
if (config.titleColor) {
|
||||
largeAttrs[NSForegroundColorAttributeName] = config.titleColor;
|
||||
}
|
||||
CGFloat largeSize = config.largeTitleFontSize ? [config.largeTitleFontSize floatValue] : 34;
|
||||
if (config.largeTitleFontFamily) {
|
||||
largeAttrs[NSFontAttributeName] = [UIFont fontWithName:config.largeTitleFontFamily size:largeSize];
|
||||
} else {
|
||||
largeAttrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:largeSize];
|
||||
}
|
||||
[navbar setLargeTitleTextAttributes:largeAttrs];
|
||||
}
|
||||
[navbar setLargeTitleTextAttributes:largeAttrs];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,6 +165,11 @@
|
||||
}
|
||||
|
||||
+ (void)willShowViewController:(UIViewController *)vc withConfig:(RNSScreenStackHeaderConfig *)config
|
||||
{
|
||||
[self updateViewController:vc withConfig:config];
|
||||
}
|
||||
|
||||
+ (void)updateViewController:(UIViewController *)vc withConfig:(RNSScreenStackHeaderConfig *)config
|
||||
{
|
||||
UINavigationItem *navitem = vc.navigationItem;
|
||||
UINavigationController *navctr = (UINavigationController *)vc.parentViewController;
|
||||
@@ -146,10 +180,21 @@
|
||||
BOOL wasHidden = navctr.navigationBarHidden;
|
||||
BOOL shouldHide = config == nil || config.hide;
|
||||
|
||||
if (!shouldHide && !config.translucent) {
|
||||
// when nav bar is not translucent we chage edgesForExtendedLayout to avoid system laying out
|
||||
// the screen underneath navigation controllers
|
||||
vc.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
} else {
|
||||
// system default is UIRectEdgeAll
|
||||
vc.edgesForExtendedLayout = UIRectEdgeAll;
|
||||
}
|
||||
|
||||
[navctr setNavigationBarHidden:shouldHide animated:YES];
|
||||
navctr.interactivePopGestureRecognizer.enabled = config.gestureEnabled;
|
||||
#ifdef __IPHONE_13_0
|
||||
vc.modalInPresentation = !config.gestureEnabled;
|
||||
if (@available(iOS 13.0, *)) {
|
||||
vc.modalInPresentation = !config.gestureEnabled;
|
||||
}
|
||||
#endif
|
||||
if (shouldHide) {
|
||||
return;
|
||||
@@ -183,7 +228,73 @@
|
||||
}
|
||||
navitem.largeTitleDisplayMode = config.largeTitle ? UINavigationItemLargeTitleDisplayModeAlways : UINavigationItemLargeTitleDisplayModeNever;
|
||||
}
|
||||
#ifdef __IPHONE_13_0
|
||||
if (@available(iOS 13.0, *)) {
|
||||
UINavigationBarAppearance *appearance = [UINavigationBarAppearance new];
|
||||
|
||||
if (config.backgroundColor && CGColorGetAlpha(config.backgroundColor.CGColor) == 0.) {
|
||||
// transparent background color
|
||||
[appearance configureWithTransparentBackground];
|
||||
} else {
|
||||
// non-transparent background or default background
|
||||
if (config.translucent) {
|
||||
[appearance configureWithDefaultBackground];
|
||||
} else {
|
||||
[appearance configureWithOpaqueBackground];
|
||||
}
|
||||
|
||||
// set background color if specified
|
||||
if (config.backgroundColor) {
|
||||
appearance.backgroundColor = config.backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.backgroundColor && CGColorGetAlpha(config.backgroundColor.CGColor) == 0.) {
|
||||
appearance.backgroundColor = config.backgroundColor;
|
||||
}
|
||||
|
||||
if (config.hideShadow) {
|
||||
appearance.shadowColor = nil;
|
||||
}
|
||||
|
||||
if (config.titleFontFamily || config.titleFontSize || config.titleColor) {
|
||||
NSMutableDictionary *attrs = [NSMutableDictionary new];
|
||||
|
||||
if (config.titleColor) {
|
||||
attrs[NSForegroundColorAttributeName] = config.titleColor;
|
||||
}
|
||||
|
||||
CGFloat size = config.titleFontSize ? [config.titleFontSize floatValue] : 17;
|
||||
if (config.titleFontFamily) {
|
||||
attrs[NSFontAttributeName] = [UIFont fontWithName:config.titleFontFamily size:size];
|
||||
} else {
|
||||
attrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:size];
|
||||
}
|
||||
appearance.titleTextAttributes = attrs;
|
||||
}
|
||||
|
||||
if (config.largeTitleFontFamily || config.largeTitleFontSize || config.titleColor) {
|
||||
NSMutableDictionary *largeAttrs = [NSMutableDictionary new];
|
||||
|
||||
if (config.titleColor) {
|
||||
largeAttrs[NSForegroundColorAttributeName] = config.titleColor;
|
||||
}
|
||||
|
||||
CGFloat largeSize = config.largeTitleFontSize ? [config.largeTitleFontSize floatValue] : 34;
|
||||
if (config.largeTitleFontFamily) {
|
||||
largeAttrs[NSFontAttributeName] = [UIFont fontWithName:config.largeTitleFontFamily size:largeSize];
|
||||
} else {
|
||||
largeAttrs[NSFontAttributeName] = [UIFont boldSystemFontOfSize:largeSize];
|
||||
}
|
||||
|
||||
appearance.largeTitleTextAttributes = largeAttrs;
|
||||
}
|
||||
|
||||
navitem.standardAppearance = appearance;
|
||||
navitem.compactAppearance = appearance;
|
||||
navitem.scrollEdgeAppearance = appearance;
|
||||
}
|
||||
#endif
|
||||
for (RNSScreenStackHeaderSubview *subview in config.reactSubviews) {
|
||||
switch (subview.type) {
|
||||
case RNSScreenStackHeaderSubviewTypeLeft: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-screens",
|
||||
"version": "2.0.0-alpha.5",
|
||||
"version": "2.0.0-alpha.12",
|
||||
"description": "First incomplete navigation solution for your react-native app.",
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
|
||||
@@ -17,7 +17,7 @@ const getViewManagerConfigCompat = name =>
|
||||
? UIManager.getViewManagerConfig(name)
|
||||
: UIManager[name];
|
||||
|
||||
export function enableScreens(shouldEnableScreens = true) {
|
||||
function enableScreens(shouldEnableScreens = true) {
|
||||
ENABLE_SCREENS = shouldEnableScreens;
|
||||
if (ENABLE_SCREENS && !getViewManagerConfigCompat('RNSScreen')) {
|
||||
console.error(
|
||||
@@ -27,11 +27,12 @@ export function enableScreens(shouldEnableScreens = true) {
|
||||
}
|
||||
|
||||
// we should remove this at some point
|
||||
export function useScreens(shouldUseScreens = true) {
|
||||
function useScreens(shouldUseScreens = true) {
|
||||
console.warn('Method `useScreens` is deprecated, please use `enableScreens`');
|
||||
enableScreens(shouldUseScreens);
|
||||
}
|
||||
|
||||
export function screensEnabled() {
|
||||
function screensEnabled() {
|
||||
return ENABLE_SCREENS;
|
||||
}
|
||||
|
||||
@@ -202,6 +203,7 @@ module.exports = {
|
||||
ScreenStackHeaderTitleView,
|
||||
ScreenStackHeaderCenterView,
|
||||
|
||||
enableScreens,
|
||||
useScreens,
|
||||
screensEnabled,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user