React Native NavigationのTypeScriptの型定義にない実装を呼ぶ

# 概要

[React Native Navigation](https://wix.github.io/react-native-navigation/#/)のshowModalのAndroid側のアニメーションだけ変えたいが、型定義ファイルにないけど実装はされているので無理やり呼ぶ方法を紹介。
ReactNativeやTypeScriptについてなどの基本的な説明は省略しています。わかる人だけ呼んでください。

## React Native Navigationについて

React Navigationなどに似たReact Nativeに置けるサードパーティ製画面遷移ライブラリ。
特徴的なのは画面遷移時にネイティブのメソッドを呼ぶので、JavaScriptのViewだけを用いたライブラリと違ってOSの標準に準拠したアニメーションができること。iOS/Android対応。

## React Native Navigationの画面遷移アニメーションについて
[React Native Navigation](https://wix.github.io/react-native-navigation/#/)のshowModal()などを呼ぶと、画面遷移アニメーションがデフォルトで下から上にスライドするアニメーションになる

| OS | push | modal|
|:-:|:-:|:-:|
| ios | ![ios_push.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/99b8c8b7-5cf3-914e-0a40-1828b2d9e541.gif) | ![ios_modal.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/7362a1af-0bf1-0edd-54b4-990d917b8027.gif) |
| android| ![android_push.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/a8574463-b95d-7b40-ef0c-d2e8f731386f.gif) | !![android_modal2.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/ba2cbb3e-1475-0041-17c1-e716b9ca4782.gif) |

動かしたければ → https://github.com/natsuki-yamanaka/TestReactNativeNavigation

## 問題点

iOSではいいがAndroidでは違和感のある動き
自分のプロジェクトではデフォルトの画面遷移アニメーションにしたい意向だったので修正したい。

## 対応策1

Androidではpush,iOSではshowModalを使う
問題点
    - 戻る為のメソッドが違う
    - pushの時は `this.props.navigator.pop`
    - showModalの時は `this.props.navigator.dismissModal`
    - 違うメソッドでは戻れないので、複雑な画面遷移の際に制御が複雑化しそう

## 対応策2

animationTypeを変える

# React Native NavigationのanimationTypeについて

ここがこの記事の本題
React Native Navigationの画面遷移メソッド(push, pop, showModal, dismissModal, resetTo, popToRootなど)では、

```
this.props.navigator.push({
  screen: 'Screen1',
  animationType: 'fade',
});
```

のように、animationTypeを指定できる([公式ドキュメントを参照](https://wix.github.io/react-native-navigation/#/screen-api?id=pushparams))
使えるanimationTypeはメソッド毎に別。

method | 使えるanimationType
|:-:|:-:|
push | fade / slide-horizontal(androidのみ)
pop | fade / slide-horizontal(androidのみ)
showModal | none / slide-up
dismissModal | none / slide-down

animationTypeを引数に与えない場合はdefaultのアニメーションになりそうですが、実際にはundefinedの場合Androidのpushはfadeでもslide-horizontalでもないアニメーションになります。

## animationType毎の挙動

push/modal | animationType | ios | android
|:-:|:-:|:-:|:-:|
push | 指定なし | ![ios_push.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/99b8c8b7-5cf3-914e-0a40-1828b2d9e541.gif) | ![android_push.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/a8574463-b95d-7b40-ef0c-d2e8f731386f.gif)
push | fade | ![ios_push_fade.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/25e818b0-c5eb-98f4-bf37-ca18f9459334.gif) | ![android_push_fade.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/7830acaa-cc6c-20d5-0322-f1e33a40c204.gif)
push | slide-holizontal | ![ios_push_slide_holizontal.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/8a12a652-1354-c7e2-18e1-c32b981d2997.gif) | ![android_push_horizontal.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/afa8813d-308b-48bc-a2fa-0ce5427e12e6.gif)
modal | 指定なし | ![ios_modal.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/7362a1af-0bf1-0edd-54b4-990d917b8027.gif) | ![android_modal2.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/ba2cbb3e-1475-0041-17c1-e716b9ca4782.gif)
modal | none | ![ios_modal_none.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/7026e07c-dbb2-808c-70ac-ef841b6f83a3.gif) | ![android_modal_none.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/978acade-1461-a89a-58d7-a40fe55b8459.gif)
modal | slide-up | ![ios_modal_slide_up.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/0d22dc47-9de8-f9bc-4d7d-0af42bcfb882.gif) | ![android_modal_slide_up.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/8b5825c4-6c4d-abbc-7e58-2d83ccf90180.gif)

push/showModalに渡すanimationTypeはあくまで遷移時のanimationTypeしか指定しません。
画面が消える時のanimationを指定したい場合はpop/dismissModalにてanimationTypeを指定することができます。

公式Githubの [app/src/main/java/com/reactnativenavigation/screens/ScreenAnimator.java](https://github.com/wix/react-native-navigation/blob/35514acd27abb6618a0436124ce48b001db8a42f/android/app/src/main/java/com/reactnativenavigation/screens/ScreenAnimator.java) あたりをみるとAndroidのデフォルトはObjectAnimatorのtranslationYアニメーションを行うらしい…

## 今回やりたかったこと

showModalの時のアニメーションがnoneかslide-upしかない
Androidの場合はshowModalでもpushのようなアニメーションはできない…？

## 実はこれ以外にも実装されている
showModalのanimationTypeを判別してアニメーションを行う公式Githubのソースは[ModalAnimationFactory.java](https://github.com/wix/react-native-navigation/blob/20f6944157b2d38b68e9e7567e665398814e40cb/android/app/src/main/java/com/reactnativenavigation/params/parsers/ModalAnimationFactory.java)で、下記のようになっている

```
switch (params.animationType) {
    case "fade":
        return R.style.ModalFadeAnimation;
    case "slide-horizontal":
        return R.style.ModalSlideHorizontal;
    case "screen":
        return R.style.ModalScreenAnimations;
    default:
        return R.style.ModalDefaultAnimations;
}
```

fade, slide-horizontal, screen などがあります…
仮にAndroidのshowModalでも

- デフォルトの下から出てくるアニメーション - `animationType: screen`
- 横スライドのアニメーション - `animationType: slide-horizontal`
- フェードインアニメーション - `animationType: fade`

を指定するとちゃんと動きます。
Javascriptなら単純にanimationType指定でいいんですが、TypeScriptの場合型定義ファイルに `slide-up` と `none` しか定義がないので、Object.assignなどで無理やり引数に入れるとしっかりアニメーションが変わります。

```
const param = {
  screen: 'Screen2',
  navigatorButtons: { leftButtons: [{ id: 'cancel' }] },
} as ModalScreen;
this.props.navigator.showModal(
  Object.assign(param, { animationType: 'screen' }),
);
```

### animationType: screenを指定した結果

Android | iOS
|:-:|:-:|
![android_modal_custom.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/9ebba708-e214-ac24-9c54-49e99a491abe.gif) | ![ios_modal_custom.gif](https://qiita-image-store.s3.amazonaws.com/0/99683/d20ea2ef-cfb9-be06-7f97-0a749ed3f6c0.gif)

Androidはデフォルトのpushと同じ、iOSはデフォルトのshowModalのアニメーションになりました。
dismissのanimationType指定してないと違和感ありますが…

### 以上

型定義になくてもソース読むと実装されてることもあるらしい