에뮬레이터, 또는 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 |