🔃

React Nativeで横画面(landscape mode)に対応する

2024/08/15に公開

こんにちは!207株式会社 ソフトウェアエンジニアの若月(@wktq)です。
React Nativeで横画面に対応する際に少しクセがあったので、備忘録を兼ねてまとめてみました。

react-native-orientationは使わない

「React Native orientation」などで検索すると最初に出てくるreact-native-orientationですが、
4~5年前に最終更新されてからほとんどメンテされていない状態です(以下参照)。

https://github.com/yamill/react-native-orientation/issues/343

かわりにreact-native-orientation-lockerを使いましょう。
https://github.com/wonday/react-native-orientation-locker

react-native-orientation-lockerの使い方

セットアップ

パッケージの導入

yarn add react-native-orientation-locker

iOSはAppDelegate.mを以下のように修正します。

+#import "Orientation.h"

@implementation AppDelegate

// ...

+- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
+  return [Orientation getOrientation];
+}

@end

AndroidはAndroidManifest.xmlとMainActivity.javaを以下のように修正します。

AndroidManifest.xml

      <activity
        ....
+       android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:windowSoftInputMode="adjustResize">
          ....
      </activity>

MainActivity.java

+   @Override
+   public void onConfigurationChanged(Configuration newConfig) {
+       super.onConfigurationChanged(newConfig);
+       Intent intent = new Intent("onConfigurationChanged");
+       intent.putExtra("newConfig", newConfig);
+       this.sendBroadcast(intent);
+   }

...

+import org.wonday.orientation.OrientationActivityLifecycle;
  @Override
  public void onCreate() {
+    registerActivityLifecycleCallbacks(OrientationActivityLifecycle.getInstance());
  }

上記のセットアップが完了すると、デフォルトでデバイスを横にした際に画面が90度回転するようになります。

実装

react-native-orientation-lockerでできることは大まかに以下の2つです。

  • 横画面回転をロックするか解除するかを決める
  • 画面が回転した際のイベントリスナーを設定する

react-native-orientation-lockerでは、まずlandscapeモードが解除されます。
以下をAppDelegateまたはMainApplicationに追記することで、デバイスを横にした際に画面が自動的に90度回転するようになります。

その上で、内部のJSコード上でその回転をロックするかどうかを以下のように記述することができます。

import Orientation from 'react-native-orientation-locker';

...

  useEffect(() => {
    if (lockToPortrait) {
        Orientation.lockToPortrait();
    } else {
        Orientation. unlockAllOrientations();
    }
  }, [lockToPortrait]);
...

また、イベントリスナーについては2種類あります。

  • addOrientationListener -> 実際に画面が回転した際に発火。lockToPortrait()していれば画面が回転されないので発火しない。
  • addDeviceOrientationListener -> デバイスの自動回転オプションやlockToPortrait()されているかに関係なくデバイスの向きが変わったら発火。

↓ 詳しくはこちら
https://github.com/wonday/react-native-orientation-locker/issues/139#issuecomment-681194288

一般的に方向に応じてレイアウト変更などを行う場合にはaddOrientationListenerを使用するかと思います。
hooksコンポーネントの場合、以下のように記述します。(typescript)

  onOrientationDidChange = (orientation: OrientationType) => {
    if (orientation == 'LANDSCAPE-LEFT') {
      //do something with landscape left layout
    } else {
      //do something with portrait layout
    }
  };

  useEffect(() => {
    Orientation.addOrientationListener(onOrientationDidChange);
    return () => {
    Orientation.removeOrientationListener(onOrientationDidChange);
    }
  }, []);

OrientationTypeは以下のいずれかを返します。
"PORTRAIT" | "LANDSCAPE-LEFT" | "LANDSCAPE-RIGHT" | "PORTRAIT-UPSIDEDOWN" | "UNKNOWN"

おわりに

ちょっとクセがあるreact-nativeでの横画面対応について書いてみました。
複雑な実装ではないのですが、ライブラリの仕様の掴み所が分かるような内容になるよう意識して書きました。
ご参考になれば幸いです。

We're Hiring

207株式会社では、レガシーな物流業界の変革に挑む配達員向け効率化アプリ「TODOCUサポーター」を開発しています。
開発チームでは一緒に開発してくれるアプリエンジニア(React Native)やバックエンドエンジニアの仲間を大絶賛募集中です!
https://207-inc.super.site/recruit

207 Tech blog

Discussion