본문 바로가기
코딩생활/ReactNative

[React Native] 3-4 WriteScreen 준비하기

by InfoJun 2023. 3. 14.
반응형

screens/RootStack.js

import React from 'react';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import MainTab from './MainTab';
import WriteScreen from './WriteScreen';

const Stack = createNativeStackNavigator();

function RootStack() {
  return (
    <Stack.Navigator>
      <Stack.Screen
        name={'MainTab'}
        component={MainTab}
        options={{headerShown: false}}
      />
      <Stack.Screen
        name={'Write'}
        component={WriteScreen}
        options={{headerShown: false}}
      />
    </Stack.Navigator>
  );
}

export default RootStack;

 

일단 WriteScreen의 기본적으로 보이는 헤더를 숨겼습니다.

 

다음으론 WriteScreen에서 사용할 헤더를 직접 만들겠습니다.

 

components/WriteHeader

import React from 'react';
import {View, StyleSheet, Pressable} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import {useNavigation} from '@react-navigation/native';

function WriteHeader() {
  const navigation = useNavigation();
  const onGoBack = () => {
    navigation.goBack();
  };
  return (
    <View style={styles.block}>
      <View style={styles.iconButtonWrapper}>
        <Pressable
          style={styles.iconButton}
          onPress={onGoBack}
          android_ripple={{color: '#ededed'}}>
          <Icon name={'arrow-back'} size={24} color={'#424242'} />
        </Pressable>
      </View>
      <View style={styles.buttons}>
        <View style={[styles.iconButtonWrapper, styles.marginRight]}>
          <Pressable
            style={styles.iconButton}
            android_ripple={{color: '#ededed'}}>
            <Icon name={'delete-forever'} size={24} color={'#ef5350'} />
          </Pressable>
        </View>
        <View style={styles.iconButtonWrapper}>
          <Pressable
            style={styles.iconButton}
            android_ripple={{color: '#ededed'}}>
            <Icon name={'check'} size={24} color={'#009688'} />
          </Pressable>
        </View>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  block: {
    height: 48,
    paddingHorizontal: 8,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  iconButtonWrapper: {
    width: 32,
    height: 32,
    borderRadius: 16,
    overflow: 'hidden',
  },
  iconButton: {
    alignItems: 'center',
    justifyContent: 'center',
    width: 32,
    height: 32,
    borderRadius: 16,
  },
  buttons: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  marginRight: {
    marginRight: 8,
  },
});

export default WriteHeader;

 

styles.block에서 flex-direction : 'row' 로 설정하여 컴포넌트들을 가로방향으로 바꿨습니다.

 

여기서 justifyContent : 'space-between' 이라는 스타일을 주었는데요

 

이는 요소 사이마다 일정한 간격이 주어지는 스타일로

 

Flex를 공부하셨다면 충분히 알 수 있는 스타일 입니다.

 

지금 컴포넌트를 보면 비슷한 코드가 여러번 작성되는 것을 확인할 수 있습니다.

 

이를 컴포넌트로 만들어서 재사용하는 리팩토링을 해보도록 하겠습니다.

 

components/TransparentCircleButton.js

import React from 'react';
import {View, StyleSheet, Platform, Pressable} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';

function TransparentCircleButton({name, color, hasMarginRight, onPress}) {
  return (
    <View
      style={[styles.iconButtonWrapper, hasMarginRight && styles.marginRight]}>
      <Pressable
        style={({pressed}) => [
          styles.iconButton,
          Platform.OS === 'ios' &&
            pressed && {
              backgroundColor: '#efefef',
            },
        ]}
        onPress={onPress}
        android_ripple={{color: '#ededed'}}>
        <Icon name={name} size={24} color={color} />
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  iconButtonWrapper: {
    width: 32,
    height: 32,
    borderRadius: 16,
    overflow: 'hidden',
  },
  iconButton: {
    alignItems: 'center',
    justifyContent: 'center',
    width: 32,
    height: 32,
    borderRadius: 16,
  },
  marginRight: {
    marginRight: 8,
  },
});

export default TransparentCircleButton;

 

이 컴포넌트는 4개의 Props를 받아옵니다.

 

이제 WriteHeader.js를 수정해주겠습니다

 

conponents/WriteHeader.js

import React from 'react';
import {View, StyleSheet, Pressable} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import {useNavigation} from '@react-navigation/native';
import TransparentCircleButton from './TransparentCircleButton';

function WriteHeader() {
  const navigation = useNavigation();
  const onGoBack = () => {
    navigation.goBack();
  };
  return (
    <View style={styles.block}>
      <View style={styles.iconButtonWrapper}>
        <TransparentCircleButton
          onPress={onGoBack}
          name="arrow-back"
          color={'#424242'}
        />
      </View>
      <View style={styles.buttons}>
        <TransparentCircleButton
          name="delete-forever"
          color={'#ef5350'}
          hasMarginRight
        />
        <TransparentCircleButton name={'check'} color={'#009688'} />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  block: {
    height: 48,
    paddingHorizontal: 8,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  buttons: {
    flexDirection: 'row',
    alignItems: 'center',
  },
});

export default WriteHeader;

 

screens/WriteScreen.js

import React from 'react';
import {View, StyleSheet} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';
import WriteHeader from '../components/WriteHeader';

function WriteScreen() {
  return (
    <SafeAreaView style={styles.block}>
      <WriteHeader />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  block: {
    flex: 1,
    backgroundColor: 'white',
  },
});

export default WriteScreen;

 

 

이렇게 사용까지 해주시면 나타나게 됩니다.

 

나타나셨나요?

 

현재 네비게이션의 헤더를 없애고 저희가 직접 만든 헤더를 사용하고 있기 때문에

 

SafeAreaView를 사용하여 ios의 상태바와 영역과 내용이 겹치지 않게 해주어야합니다.

 


WriteEditor 만들기

이번에는 글을 쓸 수 있는 컴포넌트를 만들어 볼 것입니다.

 

components/WriteEditor.js

import React from 'react';
import {View, StyleSheet, TextInput} from 'react-native';

function WriteEditor({title, body, onChangeTitle, onChangeBody}) {
  return (
    <View style={styles.block}>
      <TextInput
        placeholder={'제목을 입력하세요'}
        style={styles.titleInput}
        returnKeyType={'next'}
        onChangeText={onChangeTitle}
        value={title}
      />
      <TextInput
        placeholder={'당신의 오늘을 기록해보세요'}
        style={styles.bodyInput}
        multiline
        textAlignVertical="top"
        onChangeText={onChangeBody}
        value={body}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  block: {
    flex: 1,
    padding: 16,
  },
  titleInput: {
    paddingVertical: 0,
    fontSize: 18,
    marginBottom: 16,
    color: '#263238',
    fontWeight: 'bold',
  },
  bodyInput: {
    flex: 1,
    fontSize: 16,
    paddingVertical: 0,
    color: '#263238',
  },
});

export default WriteEditor;

 

두번째 TextView에 multiline Props에 값이 지정되지 않은 것은 true로 지정됩니다.

 

이제 여러줄을 작성할 수 있습니다.

 

이제 이 컴포넌트를 WriteScreen에 보이게 합니다.

 

screens/WriteScreen.js

import React from 'react';
import {View, StyleSheet} from 'react-native';
import {SafeAreaView} from 'react-native-safe-area-context';
import WriteHeader from '../components/WriteHeader';
import WriteEditor from "../components/WriteEditor";

function WriteScreen() {
  return (
    <SafeAreaView style={styles.block}>
      <WriteHeader />
      <WriteEditor />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  block: {
    flex: 1,
    backgroundColor: 'white',
  },
});

export default WriteScreen;

 

다음과 같이 나타나셨나요?

반응형

댓글