style html/css 사용 할 수 없다.
StyleSheet.create 메소드를 이용해 스타일 object를 작성하여 컴포넌트에 style props를 전달.
CSS
.ico { ... }
RN
ico: { ... }
* 각 스타일 속성의 구분은 ;가 아닌 ,로 한다.
- 스타일 object 뒤에 다른 스타일 object의 구분 역시 ,를 사용
CSS
.bigBlue { color: blue; font-weight: bold; font-size: 30px; } .red { color: red; }
RN
bigBlue: { color: 'blue', fontWeight: 'bold', fontSize: 30, }, red: { color: 'red', }
* 스타일 속성명의 하이픈(‘-’)이 아닌 카멜로 사용
CSS
.content { justify-content: space-between; background-color: #eee; }
RN
content: { justifyContent: 'space-between', backgroundColor: '#eee', },
*RN에서는 fontSize: 30, margin: 10와같이 ‘px’, ‘em’ 등의 단위를 생략
- 적용되는 단위는 iOS에서는 논리 픽셀, 안드로이드 환경에서는 iOS의 논리 pt와 유사한 DIP(DP)
* 스타일 우선 순위
- RN은 스타일을 props로 전달하기 때문에 inline 방식이나 internal, external에 의미가 없고 선택자, 구체성에 따른 우선 순위도 고려하지 않는다. 우선 순위에 영향을 주는 딱 한 가지는 컴포넌트에 스타일을 전달할 때 나중에 전달하는 스타일이 항상 우선순위가 높다는 것.
*의사 클래스(가상 클래스), 가상요소, 형제 선택자, 자식 선택자 등을 사용할 수 없다.
*os분기하기 용이
import {Platform, StyleSheet} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
...Platform.select({
ios: {
backgroundColor: 'red',
},
android: {
backgroundColor: 'blue',
},
}),
},
});
* CSS에서 존재하는 속성이나 값이 리액트 네이티브에서 존재하지 않을 수 있다.
- 예 // display 속성의 값이 flex와 none 두 가지 밖에 없다. display 속성의 기본 값은 flex. display: none보다는 props나 state값으로 결정될 때가 많기 때문에 스타일에 display 속성을 선언할 일은 거의 없다.
component
View 컴포넌트는 UI를 구축하기 위한 가장 기본적인 구성요소로서 웹에서는 div와 사용성이 유사.
Text는 텍스트를 표현하기 위한 컴포넌트. 중첩된 Text 컴포넌트는 inline-level처럼 동작하는데 margin, padding, border 등 box-model에 관련된 스타일이 적용되지 않는다.
TextInput을 사용해야만 텍스트를 삽입할 수 있기 때문에 필수적으로 사용되는 컴포넌트.
[Touchable]
View나 Text에도 터치 핸들링을 제공하고 있으며 button 역할을 하는 컴포넌트가 여러개 존재한다.
Button 기본적인 버튼 컴포넌트.
TouchableHightlight 터치시 하이라이트가 발생.
- 주요 props : underlayColor = 터치시 하이라이팅되는 색상을 지정
TouchableOpacity 터치시 opacity값이 적용
- 주요 props : - activeOpacity : 터치시 적용되는 opacity값을 설정합니다.(0~1)
TouchableNativeFeedback 터치시 사용자가 정의한 피드백을 표현할 수 있으며 단일 View 컴포넌트만 자식 요소로 가질 수 있다. (Android 환경만 지원)
[Image]
Image img태그처럼 이미지를 표현할 때 사용하는 컴포넌트.
- source props에 이미지 경로를 전달해 사용.
- 앱 내부의 이미지파일을 불러올 땐 require를 이용하며 width, height를 적용하지 않으면 이미지 원본의 사이즈대로 렌더링.
{/* 내부 이미지 호출 방식*/}
<Image
source={require('~/assets/images/icon_coin.png')}
/>
{/* 네트워크 이미지 호출 방식*/}
<Image
style={{width: 50, height: 50}}
source={{uri: 'https://reactnative.dev/img/tiny_logo.png'}}
/>
{/* data URI 이미지 호출 방식*/}
<Image
style={{width: 66, height: 58}}
source={{uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEX...'}}
/>
ImageBackground backgroundColor만 존재하는 리액트 네이티브에서 배경이미지를 필요할 때 사용하는 컴포넌트.
CSS의 background-image를 적용했을 때와 같이 동작하며 Image 컴포넌트와 사용방법이 유사.
SafeAreaView 기기의 안전한 영역 경계 내에서 콘텐츠를 렌더링할 때 사용.
- 적용하지 않는다면 기기의 둥근 코너나 카메라 노치와 같은 화면의 물리적인 부분과, 탐색 모음, 탭 바, 시간, 배터리 등 도구 모음 등의 뷰가 렌더링되는 내용과 겹치는 현상이 발생.
- 자동으로 padding을 적용하여 위와 같은 문제를 해결하고 안전한 영역에 콘텐츠가 렌더링.
- SafeAreaView 영역의 높이와 디바이스의 높이와 일치하지 않는다면 padding이 적용되지 않는다. (margin 적용 등)
ScrollView 웹 브라우저처럼 컨텐츠가 길어지면 자동적으로 스크롤을 생성하지 않는다.
- 스크롤이 필요하다면 ScrollView 컴포넌트를 사용 (CSS에서 요소에 overflow: auto을 선언한 것과 유사)
- position: 'fixed'를 지원하지 않지만 ScrollView를 사용해야만 스크롤이 생성되기 때문에 뷰포트에 고정된 UI를 구현할 수 있다.
- horizontal props를 통해 가로 스크롤을 제어할 수 있다.
- flex: 1이 기본 값으로 설정.
KeyboardAvodingView 키보드가 올라올 경우 컨텐츠가 키보드에 가려지는 문제를 해결하는 방법을 제공하는 컴포넌트.
- behavior props를 이용해 키보드 회피 방법을 정할 수 있다.
- input이 제공되어 키보드를 사용해야하는 페이지에서 필수적으로 사용되는 컴포넌트.
FlatList 목록형 UI를 렌더링 할 때 유용한 기능을 제공하는 컴포넌트.
- 목록이 길어지면 자동적으로 내부에 ScrollView가 적용되므로 데이터에 따라 스크롤 처리가 필요하다면 ScrollView보다 좋은 선택일 수 있다.
- header, footer, 데이터가 없을 경우 노출될 UI, 아래로 당겼을 때 refresh 기능 등을 props를 통해 설정할 수 있다. 행 별로 스타일 제어가 가능해 그리드 UI를 구현할 때도 매우 편리. (data와 renderItem props는 필수적으로 선언)
ListHeaderComponent props를 이용해 헤더를 생성하고 data, renderItem props로 컨텐츠를 렌더링.
numColumns으로 한 행에 3개의 아이템이 위치하도록 설정하고 columnWrapperStyle로 각 행의 스타일을 추가.
(numColumns 설정시 각 행은 flexDirection: 'row'가 설정됩니다.)
ScrollView와 비교해서 관리해야할 props는 많지만 그 만큼 지원하는 기능이 많기 때문에 UI에 맞게 적절하게 사용.
FlatList는 ScrollView의 모든 props를 사용할 수 있다.
Modal 어디에 선언되든 부모와 상관없이 항상 뷰포트 전체 width, height값을 가지며 기존 레이아웃 위에 노출.
커스텀 컴포넌트 사용하기
커스텀 컴포넌트 반복적인 코드나 스타일 리셋 코드 등을 넣어 컴포넌트를 사용한다면 코드를 매번 삽입할 필요가 없어짐.
import * as React from 'react'
import { Text, Platform, TextProps } from 'react-native'
const DefaultStyle = Platform.select({
ios: {},
android: { includeFontPadding: false },
})
const wrappedText: React.SFC<TextProps> = ({ children, style, ...bypass }) => (
<Text {...bypass} style={[DefaultStyle, style]}>
{children}
</Text>
)
export default wrappedText
Props
컴포넌트에 props를 전달할 수 있다. 다만 주의해야할 점은 style props 이외에도 리액트 네이티브는 UI 렌더링에 영향을 주는 props들이 많다는 것. props로 제공하는 기능을 모르고 있으면 구조상 처리가 불가능하거나 억지로 구현하기 위해 좋지 않은 코드가 삽입될 수 있기 때문에 협업이나 유지보수에 어려움을 겪을 수 있다.
[UI개발시 자주 사용하는 몇 가지 props]
numberOfLines, elliipsizeMode - 텍스트의 말줄임 처리시 스타일이 아닌 Text 컴포넌트의 numberOfLines와 ellipsizeMode props를 사용. 텍스트의 말줄임 처리시 스타일이 아닌 Text 컴포넌트의 numberOfLines와 ellipsizeMode props를 사용.
numberOfLines props에 말줄임이 될 라인수를 전달하면 간단하게 말줄임을 구현.
elliipsizeMode는 "head", "middle", "tail", "clip" 네 가지 값을 가질 수 있으며 말줄임의 위치나 방식을 조정.
- 보통 head,middle, clip은 잘 사용되지 않고 tail이 기본 값.
render() {
return (
<SafeAreaView style={styles.wrap}>
<Text style={styles.text} numberOfLines={1} ellipsizeMode="head">
ellipsizeMode is "head" ellipsizeMode is "head" ellipsizeMode is "head" ellipsizeMode is "head" ellipsizeMode is "head" ellipsizeMode is "head"
</Text>
<Text style={styles.text} numberOfLines={1} ellipsizeMode="middle">
ellipsizeMode is "middle" ellipsizeMode is "middle" ellipsizeMode is "middle" ellipsizeMode is "middle" ellipsizeMode is "middle"
</Text>
<Text style={styles.text} numberOfLines={1} ellipsizeMode="tail">
ellipsizeMode is "tail" ellipsizeMode is "tail" ellipsizeMode is "tail" ellipsizeMode is "tail" ellipsizeMode is "tail"
</Text>
<Text style={styles.text} numberOfLines={1} ellipsizeMode="clip">
ellipsizeMode is "clip" ellipsizeMode is "clip" ellipsizeMode is "clip" ellipsizeMode is "clip" ellipsizeMode is "clip"
</Text>
</SafeAreaView>
);
}
contentContainerStyle ScrollView 사용할 때 컨텐츠의 내용이 부족하더라도 ScrollView 영역만큼의 영역을 확보해야할 경우.
- 이 경우 ScrollView의 안쪽 View에 flex: 1이나 height: 100% 등을 선언하여도 영역을 확보할 수가 없다.
- 실제 ScrollView를 사용할 때 ScrollView 내부에 접근할 수 없는 컨테이너가 생성되기 때문.
- contentContainerStyle props를 이용해 이 컨테이너의 스타일을 부여할 수 있다.
** 주의할 점은 contentContainerStyle props에 flesBasis를 0으로 설정하면 스크롤이 되지 않는 이슈.
(해결 :: 따라서 contentContainerStyle에서 영역 확보시 flexBasis값을 0으로 만드는 flex: 1대신 flexGrow: 1을 사용.)
import React, { Component } from 'react';
import { StyleSheet, SafeAreaView, View, Text, ScrollView } from 'react-native';
const styles = StyleSheet.create({
wrap: {
flex: 1,
},
header: {
height: 60,
borderBottomWidth: 1,
justifyContent: 'center',
alignItems: 'center',
},
headerText: {
fontSize: 20,
fontWeight: 'bold',
},
empty: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'powderblue',
}
});
export default class AppView extends Component {
render() {
return (
<SafeAreaView style={styles.wrap}>
<View style={styles.header}>
<Text style={styles.headerText}>Header</Text>
</View>
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
<View style={styles.empty}>
<Text>contentContainer를 설정하면 영역이 확보됩니다.</Text>
</View>
</ScrollView>
</SafeAreaView>
);
}
}
hitSlop UI작업을 하다보면 모바일에서 터치 영역을 눈에 보이는 영역보다 확장해야 할 경우가 자주 있다.
- 웹에서는 주로 padding을 이용하지만 padding을 사용하면 주변 요소에 영향을 주지 않도록 수치 계산이 필요.
- hitSlop props를 이용하면 주변 요소의 렌더링에 영향을 주지 않고 터치 영역만 원하는 만큼 증가시킬 수 있음.
- hitSlop은 object내에 top, right, bottom, left 값을 통해 조정.
<TouchableOpacity hitSlop={{ top: 10, right: 10, bottom: 10, left: 10 }} style={styles.button}>
추천 오픈 소스 및 디버깅 툴
- react-native-debugger https://github.com/jhen0409/react-native-debugger
까다로운 네이티브 UI 디버깅을 브라우저 개발자 도구와 유사한 방식으로 사용할 수 있다. - react-native-extended-stylesheet https://github.com/vitalets/react-native-extended-stylesheet
스타일에 글로벌 변수를 사용 가능하며 제약이 있지만 nth-child 등의 확장된 스타일 기능을 사용할 수 있다. - react-native-iphone-x-helper https://github.com/ptelad/react-native-iphone-x-helper
iPhone의 노치 영역과 관련된 변수와 메소드를 제공합니다. SafeAreaView만으로 컨트롤하기 힘든 노치 영역을 제어할 때 사용할 수 있다. - react-native-safe-area-view https://github.com/react-native-community/react-native-safe-area-view
SafeAreaView의 padding을 부분적으로 설정할 수 있다.