TIL_220109 리액트 네이티브 Swiper

그래도 해야지·2023년 1월 9일
0

TIL

목록 보기
14/44
  • Day2 1 UI Homework
    • StyleSheet - absoluteFill
      • position: 'absolute’를 쓰는 이유는 배경과 뷰 컴포넌트들을 겹쳐서 보이게 하고 싶을 때 씀
      • position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 이것들을 모두 쓰는 대신 absoluteFill 하나만 써도 됨
    • 스와이퍼 배너 만들기 코드
      <SwiperChildView>
        <BackgroundImg
          style={StyleSheet.absoluteFill}
          source={require('../assets/banner.png')}
        />
        <LinearGradient
          style={StyleSheet.absoluteFill}
          colors={['transparent', 'black']}
        />
      </SwiperChildView>
    • linear-gradient 사용하기 npx expo install expo-linear-gradient
      import * as React from 'react';
      import { StyleSheet, Text, View } from 'react-native';
      import { LinearGradient } from 'expo-linear-gradient';
      
      export default function App() {
        return (
          <View style={styles.container}><LinearGradient
              // Background Linear Gradient
              colors={['rgba(0,0,0,0.8)', 'transparent']}
              style={styles.background}
            />
            <LinearGradient
              // Button Linear Gradient
              colors={['#4c669f', '#3b5998', '#192f6a']}
              style={styles.button}>
              <Text style={styles.text}>Sign in with Facebook</Text></LinearGradient></View>);
      }
    • react-native-swiper 스와이퍼 쓰기 GitHub - leecade/react-native-swiper: The best Swiper component for React Native.
      • 설치 npm i react-native-swiper --save

      • swiper import import Swiper from 'react-native-swiper'

      • 그냥 Swiper컴포넌트 안에 View컴포넌트 넣고 하면 됨

      • flex: 1; 해줘야함

        Movies.js
        
        export default class SwiperComponent extends Component {
          render() {
            return (
              <Swiper style={styles.wrapper} showsButtons={true}>
                <View style={styles.slide1}>
                  <Text style={styles.text}>Hello Swiper</Text>
                </View>
                <View style={styles.slide2}>
                  <Text style={styles.text}>Beautiful</Text>
                </View>
                <View style={styles.slide3}>
                  <Text style={styles.text}>And simple</Text>
                </View>
              </Swiper>
            )
          }
        }
        const styles = StyleSheet.create({ ... });
      • SwiperChildView

        // 스와이퍼 3분의 1높이만큼 표시한다
        
        const SwiperChildView = styled.View`
          flex: 1;
          justify-content: flex-end;
          height: ${SCREEN_HEIGHT / 3 + 'px'};
          background-color: green;
        `;
      • Dimensions

        • 기기의 실제 너비와 높이 구하기

          import { Dimensions } from 'react-native';
          // 구조분해할당을 통해 WIDTH와 HEIGHT 뽑아내기 
          export const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } =
            Dimensions.get('window');
      • 페이징 인디케이터 가리기

        <*Swiper* *height*='100%' *showsPagination*={false}>
      • 손으로 스와이퍼를 넘기지 않아도 일정시간이 되면 자동으로 넘길 수 있게 해줌

        autoplay

      • 마지막 페이지에서 맨 앞페이지로 스와이프할 수 있게 해주는 속성

        loop

        <Swiper height='100%' showsPagination={false} autoplay loop>
    • Movie 스크린 UI 셋업까지 코드
      // Movies.js 
      
      import React from 'react';
      import { ScrollView, StyleSheet } from 'react-native';
      import { LinearGradient } from 'expo-linear-gradient';
      import Swiper from 'react-native-swiper';
      
      export default function Movies({ navigation: { navigate } }) {
        const title = '영화제목';
        const rating = 7.5;
        const overview = '재밌어요';
        return (
          <ScrollView>
            <Swiper height='100%' showsPagination={false} autoplay loop>
              <SwiperChildView>
                <Text style={styles.text}>Hello Swiper</Text>
                <BackgroundImg
                  style={StyleSheet.absoluteFill}
                  source={require('../assets/image.jpg')}
                />
                <LinearGradient
                  // Background Linear Gradient
                  style={StyleSheet.absoluteFill}
                  colors={['transparent', 'black']}
                />
                <Row>
                  <Poster source={require('../assets/image.jpg')} />
                  <Column>
                    <Title>{title}</Title>
                    <Rating>{rating}</Rating>
                    <Overview>
                      {overview.slice(0, 150)}
                      {overview.length > 150 && '...'}
                    </Overview>
                  </Column>
                </Row>
              </SwiperChildView>
            </Swiper>
            <ListTitle>Top Rated Movies</ListTitle>
            <ScrollView></ScrollView>
            <ListTitle>Upcoming Movies</ListTitle>
            <UpcomingRow onPress={() => {}}></UpcomingRow>
            <UpcomingRow onPress={() => {}}></UpcomingRow>
          </ScrollView>
        );
      }
      
      const Rating = styled.View`
        color: white;
        margin-top: 5px;
        margin-bottom: 5px;
      `;
      
      const SwiperChildView = styled.View`
        flex: 1;
        justify-content: flex-end;
        height: ${SCREEN_HEIGHT / 3 + 'px'};
        background-color: green;
      `;
      
      const BackgroundImg = styled.Image`
        height: 100%;
        width: 100%;
      `;
  • Day2 2 NowPlayings
    • API 연결하기, 영화 데이터 불러오기, 맨 윗부분에 현재 방영중인 영화들에 대한 정보를 이미지와 텍스트들을 입혀서 표현해보기
    • API정보를 받아올 웹사이트에 회원가입하기 The Movie Database (TMDB)
    • 비동기 함수를 쓸 때 꽤 긴 데이터를 받아오기때문에 응답을 받는 데까지 시간이 많이 소요됨
    • 로딩중 상태 보여주기
      • 로딩중인지 아닌지를 확인하기 위한 state필요
        // 기본값은 true
        const [isLoading, setIsLoading] = useState(true);
      • 요청, result가 잘 받아와지면 false로 해서 뒤의 return값이 될 수 있도록 만들기
        // 로딩중이라면 Loader안의 ActivityIndicator가 보이고 
        // 로딩중이 아니라면 return값이 나온다. 
        if (isLoading) {
            return (
              <Loader>
                <ActivityIndicator />
              </Loader>
            );
          }
        
          return (
            <FlatList
              onEndReached={fetchMore}
              onEndReachedThreshold={0.5}
              refreshing={isRefreshing}
              onRefresh={onRefresh}
              ListHeaderComponent={
                <>
      • 코드
        const getNowPlayings = async () => {
            const {results} = await fetch(
              `${BASE_URL}/now_playing?api_key=${API_KEY}&language=en=US&page=1`
            ).then((res) => res.json());
            setNowPlayings(results);
        // setIsLoading이 false가 되어야 함
        setIsLoading(false)
          };
          useEffect(() => {
            getNowPlayings();
          }, [])
          
          if (isLoading) {
              return (
                <Loader>
                  <ActivityIndicator />
                </Loader>
              );
            }
            
          return (
        <FlatList
          onEndReached={fetchMore}
          onEndReachedThreshold={0.5}
          refreshing={isRefreshing}
          onRefresh={onRefresh}
          ListHeaderComponent={
            <>
      • Loader컴포넌트를 View로 정의해놓기
        const Loader = styled.View`
          flex: 1;
          justify-content: center;
          align-items: center;
        `;
    • map으로 movie객체 뿌리기
      <Swiper height='100%' showsPagination={false} autoplay loop>
      // nowPlayingData라는 배열을 가지고 map으로 뿌릴 것
      // 배열 하나의 요소는 영화 객체 movie인데 이 movie객체를 갖고 map을 뿌릴 것
        {nowPlayingData.results.map((movie) => (
          <Slide key={movie.id} movie={movie} />
        ))}
      </Swiper>
    • 이미지 패스만 가지고는 소용이 없다.
      • 이미지를 불러올 땐 이미지 path만 하면 소용이 없고 BASE_URL도 알아야 함
      • 이미지 api에 backdrop_path를 붙인다. (api주소 + backdrop_path) Untitled
    • 이미지 베이스 url가져오기
      <Swiper height='100%' showsPagination={false} autoplay loop>
      {nowPlayingData.results.map((movie) => (
      // 인터넷 상에 있는 이미지를 가져올 땐 객체{}안에 uri넣어주기 
      <BackgroundImg 
      source={{uri: `https://image.tmdb.org/t/p/w500${movie.backdrop_path}}}
      />
    • 이미지 베이스 별도의 함수로 빼기(리팩토링)
      // util.js 
      // path를 매개변수로 받아서 return하기 
      // 전체 url을 문자열로 리턴해주는 함수  
      
      export const getImgPath = (path) => {
        return `https://image.tmdb.org/t/p/w500${path}`;
      };
      // Movie.js 
      // 위의 uri를 이걸로 바꾸기 
      <Poster
      source={{uri: getImgPath(movie.poster_path)}}
      />
    • map뿌려주는 Slide컴포넌트 리팩토링하기
      // Slide.js컴포넌트 만들기 
      
      // Movie.js에서 복붙해서 컴포넌트로 따로 빼기 
      // 컴포넌트가 길다보니 불러와야하는 게 많음 
      import React from "react";
      import { StyleSheet } from "react-native";
      import { getImgPath, SCREEN_HEIGHT } from "../util";
      import styled from "@emotion/native";
      import { LinearGradient } from "expo-linear-gradient";
      import { useNavigation } from "@react-navigation/native";
      export default function Slide({ movie }) {
        const { navigate } = useNavigation();
        return (
          <SwiperChildView>
            <BackgroundImg
              style={StyleSheet.absoluteFill}
              // style={{ position: "absolute", top: 0, left: 0 }}
              source={{
                uri: getImgPath(movie.backdrop_path),
              }}
            />
            <LinearGradient
              style={StyleSheet.absoluteFill}
              colors={["transparent", "black"]}
            />
            <Row
              onPress={() =>
                navigate("Stacks", {
                  screen: "Detail",
                  params: { movieId: movie.id },
                })
              }
            >
              <Poster
                source={{
                  uri: getImgPath(movie.poster_path || ""),
                }}
              />
              <Column>
                <Title>{movie.title}</Title>
                <Rating>⭐️{movie.vote_average}/10</Rating>
                <Overview>
                  {movie.overview.slice(0, 150)}
                  {movie.overview.length > 150 && "..."}
                </Overview>
              </Column>
            </Row>
          </SwiperChildView>
        );
      }
      const SwiperChildView = styled.TouchableOpacity`
        flex: 1;
        justify-content: flex-end;
        height: ${SCREEN_HEIGHT / 3 + "px"};
        background-color: green;
      `;
      
      const BackgroundImg = styled.Image`
        height: 100%;
        width: 100%;
      `;
      
      const Row = styled.TouchableOpacity`
        flex: 1;
        flex-direction: row;
        align-items: flex-end;
      `;
      
      const Column = styled.View`
        width: 65%;
        margin-left: 10px;
        margin-bottom: 10px;
      `;
      
      const Poster = styled.Image`
        width: 100px;
        height: 160px;
        /* height: 100%; */
        margin-left: 10px;
        margin-bottom: 10px;
      `;
      
      const Title = styled.Text`
        font-size: 20px;
        font-weight: 600;
        color: white;
      `;
      
      const Overview = styled.Text`
        font-size: 12px;
        color: white;
      `;
      
      const Rating = styled.Text`
        color: white;
        margin-top: 5px;
        margin-bottom: 5px;
      `;
    • Slide
      <Swiper height='100%' showsPagination={false} autoplay loop>
        {nowPlayingData.results.map((movie) => (
      // 맵을 미리 뿌렸으니까 키값넣기    
      // 키값은 movie.id 
      	<Slide key={movie.id} movie={movie} />
        ))}
      </Swiper>
      • Slide컴포넌트에 props로 movie 받아오기
        export default function Slide({ movie }) {
          const { navigate } = useNavigation();
          return (
            <SwiperChildView>
              <BackgroundImg
                style={StyleSheet.absoluteFill}
                // style={{ position: "absolute", top: 0, left: 0 }}
                source={{
                  uri: getImgPath(movie.backdrop_path),
                }}
              />
              <LinearGradient
                style={StyleSheet.absoluteFill}
                colors={["transparent", "black"]}
              />
              <Row
                onPress={() =>
                  navigate("Stacks", {
                    screen: "Detail",
                    params: { movieId: movie.id },
                  })
                }
              >
                <Poster
                  source={{
                    uri: getImgPath(movie.poster_path || ""),
                  }}
                />
                <Column>
                  <Title>{movie.title}</Title>
                  <Rating>⭐️{movie.vote_average}/10</Rating>
                  <Overview>
                    {movie.overview.slice(0, 150)}
                    {movie.overview.length > 150 && "..."}
                  </Overview>
                </Column>
              </Row>
            </SwiperChildView>
          );
        }

0개의 댓글