에뮬레이터, 또는 Expo로 연결된 모바일 기기를 가로로 기울인다고 해서 화면이 자동으로 돌아가지는 않는다.

landscape 모드를 적용하고자 한다면, 이에 따라 설정과 스타일링을 바꿔줘야 한다.

 

 

 

우선 app.json파일을 열고, orientation 속성을 "default"로 바꿔준다.

  "expo": {
    "name": "navigate",
    "slug": "navigate",
    "version": "1.0.0",
    "orientation": "default",	//기본값은 portrait
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },

 

 

참고로 orientation은 아래 값을 가질 수 있다.

  • default : 가로, 세로 모드 모두 지원
  • portrait : 세로 모드로 고정
  • landscape : 가로 모드로 고정
  • portrait_up : 세로 모드이되, 상단이 항상 위쪽을 향함
  • portrait_down : 세로 모드이되, 상단이 아래쪽을 향함
  • landscape_left : 가로 모드이되, 기기의 왼쪽이 위쪽을 향함
  • landscape_right : 가로 모드이되, 기기의 오른쪽이 위쪽을 향함

 

 

app.json의 변경 사항을 저장했다면, 에뮬레이터를 회전시켰을 때 정상적으로 반응하는 것을 확인할 수 있다.

컴포넌트로 돌아가 스타일링을 해보자.

 

 


 

 

 

현재 모드가 가로 모드인지 세로 모드인지 어떻게 추적할 수 있을까?

기본적으로 현재 화면의 가로, 세로 길이를 측정하고, 길이를 비교하여 조건을 적용하면 된다.

 

 

 

가로, 세로 길이 재는 방법은 두가지가 있다.

Dimensions

import { Dimensions, Text, View } from 'react-native';	//임포트 잊지 않을 것

const ScreenInfo = () => {
 const { width, height } = Dimensions.get('window');
  const isWideView = width > height;


  return (
    <View>
      <Text>{ isWideView ? 가로 모드 : 세로 모드 }</Text>
    </View>
  )
}

 

 

Dimensions의 get 메소드를 통해 가로와 세로의 길이를 구한다.

  • Dimensions.get('window') : 상태바나 네비게이션 바를 무시하고, 사용자가 상호작용할 수 있는 앱의 영역을 측정
  • Dimensions.get('screen') : 상태바와 네비게이션 바를 모두 포함한 전체 화면의 길이 측정
    • 다만 초기값만 가져온다는 단점이 있다.
    • 즉, 기기를 반복적으로 뒤집더라도 이 상태 변화를 감지하지 못하기 때문에 별도의 eventListener를 생성해야 한다.

 

 

 

useWindowDimensions()

import { useWindowDimensions, Text, View } from 'react-native';	//임포트 잊지 않을 것

const ScreenInfo = () => {
 const { width, height } = useWindowDimensions();
  const isWideView = width > height;


  return (
    <View>
      <Text>{ isWideView ? 가로 모드 : 세로 모드 }</Text>
    </View>
  )
}

 

useWindowDimenstions() 는 Dimensions와 유사하지만, 가로와 세로 변화를 실시간으로 감지하고 업데이트 해준다는 장점이 있다.

 

 

 


 

 

 

이것을 현재 실습 중인 프로젝트에 적용해보았다.

스타일링은 우선 StyleSheet (내장 라이브러리)를 사용했다.

 

import { StyleSheet, View, Text, Pressable, useWindowDimensions } from "react-native";

interface ProjectProps {
  title: string;
  color: string;
}

export default function Project({ title, color }: ProjectProps) {
    const { width, height } = useWindowDimensions()
    const isWideView = width > height;
  
    return (
    <View style={[styles.gridItem, isWideView && styles.gridItemWide]}>
      <Pressable>
        <View>
          <Text>{title}</Text>
        </View>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  gridItem: {
    flex: 1,
    margin: 16,
    height: 150,
    borderWidth: 1,
    borderColor: 'red',
  },

  gridItemWide: {
    height: 180,
    margin: 20,
  }
});

 

 

에뮬레이터를 회전할 때마다 덮어쓴 height와 margin이 잘 적용 및 해제되는 것을 확인할 수 있다.

 

 

 

 


 

 

 

작은 문제가 생겼다.

위 영상처럼 현재 데이터 리스트를 카드 형태로 FlatList에 순서대로 뿌려주고 있었다.

세로뷰에서는 2줄 씩 출력되던 것을 가로뷰에서는 3줄로 출력되도록 수정하고 싶었다.

따라서 아래와 같이 해당 출력 컴포넌트의 코드를 수정했다.

import { StyleSheet, FlatList, useWindowDimensions } from "react-native";
import { CATEGORIES } from "../assets/data/dummy";
import CategoryGridTile from "../components/CategoryGridTile";

function renderCategoryItem(itemData: any) {
  return (
    <CategoryGridTile title={itemData.item.title} color={itemData.item.color} />
  );
}

function CategoryScreen() {
    const { width, height } = useWindowDimensions()
    const isWideView = width > height;
  

  return (
    <FlatList
      data={CATEGORIES}
      keyExtractor={(item) => item.id}
      renderItem={renderCategoryItem}
      numColumns={isWideView ? 3 : 2}	// 출력될 줄 수에 삼항연산자를 적용했다.
    />
  );
}

export default CategoryScreen;

 

 

 

그러나 저장 후 가로뷰로 에뮬레이터를 뒤집는 순간, 아래 에러가 발생했다.

더보기

Invariant Violation: Changing numColumns on the fly is not supported. Change the key prop on FlatList when changing the number of columns to force a fresh render of the component.

위 에러는 FlatList의 numColumns를 실시간으로 변경할 수 없다는 뜻이다.

 

 

 

이런 경우는 해당 FlatList를 재렌더링을 하는 방식으로 줄 수를 바꿔야 한다.

 

 

 

그렇다면 어떻게 재렌더링을 트리거할 수 있을까?

기기를 회전했을 때 해당 리스트의 key를 바꿔서 다시 받아오는 방법으로 재렌더링이 가능하다.

 

//전략
  return (
    <FlatList
      data={CATEGORIES}
      keyExtractor={(item) => item.id}
      renderItem={renderCategoryItem}
      numColumns={isWideView ? 3 : 2}	// 삼항연산자 그대로 사용
      key={isWideView ? 'wide' : 'portrait'}  // FlatList의 key를 바꿔서 강제 리렌더링
    />
  );

 

 

위처럼 item 각각의 key는 이전과 마찬가지로 keyExtractor로 부여한다.

그리고 FlatList 덩어리 자체에 key를 추가하고, 이것을 가로, 세로 모드가 바뀔 때 다르게 적용하여 다시 렌더링되게 한다.

 

 

 

 

 

 

위 방법은 유저가 화면을 반복적으로 돌리는 경우 계속해서 컴포넌트 리렌더링이 일어나기 때문에 성능에 영향을 줄 수 있다.

 

다만, FlatList는 많은 데이터를 열거하더라도 보여지는 화면 영역에 대한 데이터를 우선 처리하기 때문에 성능 문제를 조금 줄일 수 있다.

만일 해당 컴포넌트가 복잡한 계산을 요하는 함수로 이루어져 있다면, useMemo() useCallback() 사용을 고려해보아도 좋겠다.

 

'React Native' 카테고리의 다른 글

Navigator 스타일링  (0) 2024.10.15
숫자 맞추기 게임 - 히스토리 상태 받아오기  (1) 2024.10.03
숫자 맞추기 게임  (8) 2024.09.30
React Native 기초  (0) 2024.09.26

+ Recent posts