React Native Calendarsのレスポンシブ対応が大変だった話

またまた顔面神経麻痺ケアアプリの話。

iPadやAndroidに対応できないかと素人なりに色々模索しているところ。(GitHub)
iPadOSにはSplit ViewやSlide Overがあるし、Androidもフォルダブルスマートフォン等増えてきたので、可変サイズに対応したい。
しかしReact Native Calendarsはそう簡単にはいかないのだ。

やりたいこと

React Native Calendarsのバージョンは1.1300.0。
Horizontal CalendarListを使用している。

import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import { View, SafeAreaView, ScrollView, useWindowDimensions } from 'react-native';
import { Button } from 'react-native-paper';
import { CalendarList } from 'react-native-calendars';

const HomeScreen = ({ navigation }) => {
  const window = useWindowDimensions();

  const [calendarKey, setCalendarKey] = useState(0);
  const reloadCalendar = () => {
    setCalendarKey(calendarKey + 1);
  };

  return (
    <ScrollView contentInsetAdjustmentBehavior="automatic">
      <SafeAreaView>
        <CalendarList
          key={calendarKey}
          staticHeader
          calendarHeight={400}
          calendarWidth={window.width}
          horizontal={true}
          pagingEnabled={true}
        />
        <View>
          <Button onPress={reloadCalendar}>今日</Button>
        </View>
      </SafeAreaView>
      <StatusBar style="auto" />
    </ScrollView>
  );
};

export default HomeScreen;

しかしこれだと画面サイズが変わってもカレンダーのサイズが変わらない。
ちなみにCalendarListkeyを更新してもダメだった。

画面サイズが変更されたタイミングを取得

どういう方法でカレンダーをリロードするにせよ、まず画面サイズが変わったタイミングで実行しなければならない。
Dimensionsを使うことにした。

import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import { View, SafeAreaView, ScrollView, Dimensions, useWindowDimensions } from 'react-native';
import { Button } from 'react-native-paper';
import { useFocusEffect } from '@react-navigation/native';
import { CalendarList } from 'react-native-calendars';

const HomeScreen = ({ navigation }) => {
  const window = useWindowDimensions();

  const [calendarKey, setCalendarKey] = useState(0);
  const reloadCalendar = () => {
    setCalendarKey(calendarKey + 1);
  };

  useFocusEffect(
    React.useCallback(() => {
      const handleDimensionsChange = () => {
        // リロードのロジックをここに追加
      };
      dimensionsHandler=Dimensions.addEventListener('change',handleDimensionsChange)
      return ()=>dimensionsHandler.remove()
    }, [])
  );

  return (
    <ScrollView contentInsetAdjustmentBehavior="automatic">
      <SafeAreaView>
        <CalendarList
          key={calendarKey}
          staticHeader
          calendarHeight={400}
          calendarWidth={window.width}
          horizontal={true}
          pagingEnabled={true}
        />
        <View>
          <Button onPress={reloadCalendar}>今日</Button>
        </View>
      </SafeAreaView>
      <StatusBar style="auto" />
    </ScrollView>
  );
};

export default HomeScreen;

カレンダーのリロード

カレンダーのリロードは、結局CalendarListを出し入れするだけにした。
他の画面を表示中に画面サイズが変更される可能性も考慮し、useIsFocusedも使っている。

import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import { View, SafeAreaView, ScrollView, Dimensions, useWindowDimensions } from 'react-native';
import { ActivityIndicator, Button } from 'react-native-paper';
import { useFocusEffect, useIsFocused } from '@react-navigation/native';
import { CalendarList } from 'react-native-calendars';

const HomeScreen = ({ navigation }) => {
  const window = useWindowDimensions();

  const [calendarKey, setCalendarKey] = useState(0);
  const [calendarVisibility, setCalendarVisibility] = useState(true);
  const reloadCalendar = () => {
    setCalendarVisibility(false);
    setTimeout(() => {
      setCalendarKey(calendarKey + 1);
      setCalendarVisibility(true);
    },100);
  };

  useFocusEffect(
    React.useCallback(() => {
      const handleDimensionsChange = () => {
        // リロードのロジックをここに追加
        reloadCalendar()
      };
      dimensionsHandler=Dimensions.addEventListener('change',handleDimensionsChange)
      return ()=>dimensionsHandler.remove()
    }, [])
  );

  return (
    <ScrollView contentInsetAdjustmentBehavior="automatic">
      <SafeAreaView>
        {useIsFocused() === false ?
          <View>
            <ActivityIndicator animating={true} />
          </View>
        : calendarVisibility ?
          <CalendarList
            key={calendarKey}
            staticHeader
            calendarHeight={400}
            calendarWidth={window.width}
            horizontal={true}
            pagingEnabled={true}
          />
        :
          <View>
            <ActivityIndicator animating={true} />
          </View>
        }
        <View>
          <Button onPress={reloadCalendar}>今日</Button>
        </View>
      </SafeAreaView>
      <StatusBar style="auto" />
    </ScrollView>
  );
};

export default HomeScreen;

落とし穴

試したところDimensionsを使ったウィンドウサイズ変更タイミングの判定は、AppleシリコンMacではできなかった。
結局Mac対応を諦めるか、Split View & Slide Over対応を諦めるかのどちらかになりそうだ。

Split ViewとSlide Overを無効にする場合は、app.jsonに次を追加する。

{
  "expo": {
    "ios": {
      "requireFullScreen": true
    }
  }
}

参考

関連記事

コメント

この記事へのコメントはありません。

TOP