Skip to content

Commit

Permalink
feat(Android): add ios like slide animation (#1945)
Browse files Browse the repository at this point in the history
## Description

This PR adds iOS like slide in animation to Android.
But why?
iOS like slide in animations are extremely popular in Android apps.
Twitter, Telegram, Slack and many other apps stack navigation resembles
or flat out copies iOS behaviour.
`react-navigation` non native stack gives us ability to have
reimplementation of iOS slide in behavior. However, `expo-router`
doesn't support non native stack. Besides that if you want native
performance and iOS like behavior you are kinda stuck 🤷.

## Changes

<!--
Please describe things you've changed here, make a **high level**
overview, if change is simple you can omit this section.

For example:

- Updated `about.md` docs

-->

<!--

PR updates:
`native-stack/GUIDE_FOR_LIBRARY_AUTHORS.md`
`native-stack/README.md`
`src/types.tsx`

Additionally adds entries to iOS and Windows

## Screenshots / GIFs


https://github.com/software-mansion/react-native-screens/assets/5978212/68541271-7bc7-4417-b004-17be2b855c05




## Test code and steps to reproduce

- Run the Example app
- Go to Animations
- Press on "Stack Animation: default"
- Choose `cupertino`
- Play around the push/pop

## Checklist

- [x] Included code example that can be used to test this change
- [x] Updated TS types
- [x] Updated documentation: <!-- For adding new props to native-stack
-->
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx
- [x]
https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx
- [x] Ensured that CI passes
  • Loading branch information
alexandrius authored Oct 30, 2023
1 parent 5eb15d8 commit 7994eda
Show file tree
Hide file tree
Showing 17 changed files with 43 additions and 6 deletions.
1 change: 1 addition & 0 deletions Example/src/screens/Animations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const MainScreen = ({
'slide_from_bottom',
'slide_from_right',
'slide_from_left',
'ios',
'none',
]}
/>
Expand Down
1 change: 1 addition & 0 deletions Example/src/screens/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ const MainScreen = ({
'slide_from_bottom',
'slide_from_right',
'slide_from_left',
'ios',
'none',
]}
/>
Expand Down
2 changes: 1 addition & 1 deletion android/src/main/java/com/swmansion/rnscreens/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ class Screen constructor(context: ReactContext?) : FabricEnabledViewGroup(contex
}

enum class StackAnimation {
DEFAULT, NONE, FADE, SLIDE_FROM_BOTTOM, SLIDE_FROM_RIGHT, SLIDE_FROM_LEFT, FADE_FROM_BOTTOM
DEFAULT, NONE, FADE, SLIDE_FROM_BOTTOM, SLIDE_FROM_RIGHT, SLIDE_FROM_LEFT, FADE_FROM_BOTTOM, IOS
}

enum class ReplaceAnimation {
Expand Down
5 changes: 4 additions & 1 deletion android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ class ScreenStack(context: Context?) : ScreenContainer(context) {
R.anim.rns_slide_in_from_bottom, R.anim.rns_no_animation_medium
)
StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_fade_from_bottom, R.anim.rns_no_animation_350)
StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_slide_in_from_right_ios, R.anim.rns_slide_out_to_left_ios)
}
} else {
when (stackAnimation) {
Expand All @@ -156,6 +157,7 @@ class ScreenStack(context: Context?) : ScreenContainer(context) {
R.anim.rns_no_animation_medium, R.anim.rns_slide_out_to_bottom
)
StackAnimation.FADE_FROM_BOTTOM -> it.setCustomAnimations(R.anim.rns_no_animation_250, R.anim.rns_fade_to_bottom)
StackAnimation.IOS -> it.setCustomAnimations(R.anim.rns_slide_in_from_left_ios, R.anim.rns_slide_out_to_right_ios)
}
}
}
Expand Down Expand Up @@ -331,6 +333,7 @@ class ScreenStack(context: Context?) : ScreenContainer(context) {

private fun needsDrawReordering(fragmentWrapper: ScreenFragmentWrapper): Boolean =
fragmentWrapper.screen.stackAnimation === StackAnimation.SLIDE_FROM_BOTTOM ||
fragmentWrapper.screen.stackAnimation === StackAnimation.FADE_FROM_BOTTOM
fragmentWrapper.screen.stackAnimation === StackAnimation.FADE_FROM_BOTTOM ||
fragmentWrapper.screen.stackAnimation === StackAnimation.IOS
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class ScreenViewManager : ViewGroupManager<Screen>(), RNSScreenManagerInterface<
"slide_from_left" -> Screen.StackAnimation.SLIDE_FROM_LEFT
"slide_from_bottom" -> Screen.StackAnimation.SLIDE_FROM_BOTTOM
"fade_from_bottom" -> Screen.StackAnimation.FADE_FROM_BOTTOM
"ios" -> Screen.StackAnimation.IOS
else -> throw JSApplicationIllegalArgumentException("Unknown animation type $animation")
}
}
Expand Down
5 changes: 5 additions & 0 deletions android/src/main/res/base/anim/rns_slide_in_from_left_ios.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="-30%"
android:toXDelta="0%" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:fromXDelta="100%"
android:toXDelta="0%" />
5 changes: 5 additions & 0 deletions android/src/main/res/base/anim/rns_slide_out_to_left_ios.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="0%"
android:toXDelta="-30%"/>
6 changes: 6 additions & 0 deletions android/src/main/res/base/anim/rns_slide_out_to_right_ios.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:interpolator/accelerate_decelerate"
android:fromXDelta="0%"
android:toXDelta="100%"/>
1 change: 1 addition & 0 deletions guides/GUIDE_FOR_LIBRARY_AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ Allows for the customization of how the given screen should appear/disappear whe
- `"slide_from_bottom"` - slide in the new screen from bottom to top
- `"slide_from_right"` - slide in the new screen from right to left (Android only, resolves to default transition on iOS)
- `"slide_from_left"` - slide in the new screen from left to right (Android only, resolves to default transition on iOS)
- `"ios"` - iOS like slide in animation (Android only, resolves to default transition on iOS)
- `"none"` – the screen appears/disappears without an animation

### `stackPresentation`
Expand Down
3 changes: 2 additions & 1 deletion ios/RNSConvert.mm
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ + (RNSScreenStackPresentation)RNSScreenStackPresentationFromCppEquivalent:
+ (RNSScreenStackAnimation)RNSScreenStackAnimationFromCppEquivalent:(react::RNSScreenStackAnimation)stackAnimation
{
switch (stackAnimation) {
// these three are intentionally grouped
// these four are intentionally grouped
case react::RNSScreenStackAnimation::Slide_from_right:
case react::RNSScreenStackAnimation::Slide_from_left:
case react::RNSScreenStackAnimation::Ios:
case react::RNSScreenStackAnimation::Default:
return RNSScreenStackAnimationDefault;
case react::RNSScreenStackAnimation::Flip:
Expand Down
1 change: 1 addition & 0 deletions ios/RNSScreen.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,7 @@ @implementation RCTConvert (RNSScreen)
@"slide_from_bottom" : @(RNSScreenStackAnimationSlideFromBottom),
@"slide_from_right" : @(RNSScreenStackAnimationDefault),
@"slide_from_left" : @(RNSScreenStackAnimationDefault),
@"ios" : @(RNSScreenStackAnimationDefault),
}),
RNSScreenStackAnimationDefault,
integerValue)
Expand Down
1 change: 1 addition & 0 deletions native-stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ How the given screen should appear/disappear when pushed or popped at the top of
- `slide_from_bottom` – performs a slide from bottom animation
- `slide_from_right` - slide in the new screen from right to left (Android only, resolves to default transition on iOS)
- `slide_from_left` - slide in the new screen from left to right (Android only, resolves to default transition on iOS)
- `ios` - iOS like slide in animation (Android only, resolves to default transition on iOS)
- `none` - the screen appears/disappears without an animation.

Defaults to `default`.
Expand Down
3 changes: 2 additions & 1 deletion src/fabric/ScreenNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ type StackAnimation =
| 'slide_from_right'
| 'slide_from_left'
| 'slide_from_bottom'
| 'fade_from_bottom';
| 'fade_from_bottom'
| 'ios';

type SwipeDirection = 'vertical' | 'horizontal';

Expand Down
1 change: 1 addition & 0 deletions src/native-stack/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ export type NativeStackNavigationOptions = {
* - "slide_from_bottom" – performs a slide from bottom animation
* - "slide_from_right" - slide in the new screen from right to left (Android only, resolves to default transition on iOS)
* - "slide_from_left" - slide in the new screen from left to right (Android only, resolves to default transition on iOS)
* - "ios" - iOS like slide in animation (Android only, resolves to default transition on iOS)
* - "none" – the screen appears/dissapears without an animation
*/
stackAnimation?: ScreenProps['stackAnimation'];
Expand Down
4 changes: 3 additions & 1 deletion src/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export type StackAnimationTypes =
| 'simple_push'
| 'slide_from_bottom'
| 'slide_from_right'
| 'slide_from_left';
| 'slide_from_left'
| 'ios';
export type BlurEffectTypes =
| 'extraLight'
| 'light'
Expand Down Expand Up @@ -319,6 +320,7 @@ export interface ScreenProps extends ViewProps {
* - `slide_from_bottom` – performs a slide from bottom animation
* - "slide_from_right" - slide in the new screen from right to left (Android only, resolves to default transition on iOS)
* - "slide_from_left" - slide in the new screen from left to right (Android only, resolves to default transition on iOS)
* - "ios" - iOS like slide in animation (Android only, resolves to default transition on iOS)
* - "none" – the screen appears/dissapears without an animation
*/
stackAnimation?: StackAnimationTypes;
Expand Down
3 changes: 2 additions & 1 deletion windows/RNScreens/Screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ enum class StackAnimation {
FADE,
SIMPLE_FROM_BOTTOM,
SLIDE_FROM_RIGHT,
SLIDE_FROM_LEFT
SLIDE_FROM_LEFT,
IOS
};

enum class ReplaceAnimation { PUSH, POP };
Expand Down

0 comments on commit 7994eda

Please sign in to comment.