-
[React Native] 사진개수에 따른 레이아웃 변경 (페이스북 게시물 형태)► React Native/개발일기 2023. 6. 23. 22:24반응형
페이스북에서 사진이 게시될 때의 레이아웃을 그대로 구현하고 싶었다.
페이스북처럼 1~6개이상일경우에 따라, 각각 다른 사진 레이아웃을 적용하고 싶었다.
그래서 라이브러리를 찾아보았는데,
마땅한 라이브러리를 찾지 못해 직접 구현했다.
(혹시 라이브러리 아시는 분들은…. 알려주세여 ㅜㅜㅜ)📍 구현코드
나중에 다시 쓸 수 있게 참고하기 위해 코드를 적어놨다…
나만 알아볼 수 있게찌 ….?;;;
PictureViewer.js 파일에 ‘사진 레이아웃’을 구현하였다.
ImageViewer 라이브러리를 활용하여, 클릭한 이미지를 전체화면으로 띄웠다.
PicsLayout.js 파일을 만들어, 1~6개이상의 사진갯수마다의 레이아웃을 각각의 컴포넌트를 만들어 모아놨다.
▼ PictureViewer.jsimport React, { useState } from 'react'; import { StyleSheet } from 'react-native'; import { Layout, Icon, Button } from '@ui-kitten/components'; import Modal from 'react-native-modal'; import { heightPercentageToDP as hp, widthPercentageToDP as wp, } from '../../utils/ResponsiveScreen'; import ImageViewer from 'react-native-image-zoom-viewer'; const PictureViewer = () => { // [사진모음 배열] 구조 const picsData = [ { Created_Date: '2023-04-06 09:56:18', Public_Link: 'https://images.pexels.com/photos/786357/pexels-photo-786357.jpeg?auto=compress&cs=tinysrgb&w=800', Title: '다운로드', }, { Created_Date: '2023-04-06 09:56:18', Public_Link: 'https://images.pexels.com/photos/786357/pexels-photo-786357.jpeg?auto=compress&cs=tinysrgb&w=800', Title: '다운로드', }, { Created_Date: '2023-04-06 09:56:18', Public_Link: 'https://images.pexels.com/photos/786357/pexels-photo-786357.jpeg?auto=compress&cs=tinysrgb&w=800', Title: '다운로드', }, ]; // 사진뷰어 기본값 const [pictureViewer, setPictureViewer] = useState({ visible: false, // 불린값 item: {}, // 선택사진 객체 itemList: [], // 사진모음 배열 }); // 사진뷰어 창닫기 기능 const onViewerClose = () => { setPictureViewer({ viewerVisible: false, item: {}, itemList: [] }); }; // 사진 이미지 뷰어 const handlePictureView = (item, itemList) => { setPictureViewer({ viewerVisible: true, item: {선택사진 객체}, itemList: [사진모음 배열], }); }; const images = pictureViewer.itemList.map((v, i, arr) => { return { url: v.Public_Link, props: { index: i, }, }; }); return ( <> <PicFBPackage data={[사진모음 배열]} // 사진배열 setPictureView={handlePictureView} // 선택한 사진의 뷰어를 여는 기능 /> <Modal style={} isVisible={불린값} animationIn={'fadeIn'} animationOut={'fadeOut'}> <ImageViewer imageUrls={images} index={[사진모음 배열].indexOf({선택사진 객체})} /> <Button onPress={() => onViewerClose()}>닫기</Button> </Modal> </> ); }; export { PictureViewer };
▼ PicsLayout.js
import React, { useEffect, useState } from 'react'; import { default as _color } from '../../theme/colorTheme.json'; import { Text, Image, StyleSheet, TouchableOpacity } from 'react-native'; import { Layout } from '@ui-kitten/components'; import { heightPercentageToDP as hp, widthPercentageToDP as wp, } from '../../utils/ResponsiveScreen'; const PicFB1 = ({ data, handlePictureView, stylesTouchablePics, stylesImage, }) => { const styles = StyleSheet.create({ touchablePics: {}, image: { flexDirection: 'row', flex: 1, borderWidth: 1.5, borderColor: _color['color-background-100'], height: wp('100%'), resizeMode: 'cover', }, }); const renderItem = data.map((value, idx) => ( <TouchableOpacity onPress={() => handlePictureView(value, data)} style={{ ...styles.touchablePics, stylesTouchablePics, }}> <Image source={{ uri: value.Public_Link ? value.Public_Link : '', }} style={{ ...styles.image, ...stylesImage, }} /> </TouchableOpacity> )); return <>{renderItem}</>; }; const PicFB2 = ({ data, handlePictureView, stylesPicsContainer, stylesTouchablePics, stylesImage, }) => { const styles = StyleSheet.create({ picsContainer: { flexDirection: 'row', justifyContent: 'space-between', }, touchablePics: {}, image: { borderWidth: 1.5, borderColor: _color['color-background-100'], height: wp('44.5%'), width: wp('44.5%'), resizeMode: 'cover', }, }); const renderItem = data.map((value, idx) => ( <TouchableOpacity onPress={() => handlePictureView(value, data)} style={{ ...styles.touchablePics, stylesTouchablePics, }}> <Image source={{ uri: value.Public_Link ? value.Public_Link : '', }} style={{ ...styles.image, ...stylesImage, }} /> </TouchableOpacity> )); return ( <> <Layout style={{ ...styles.picsContainer, stylesPicsContainer, }}> {renderItem} </Layout> </> ); }; const PicFB3 = ({ data, stylesPicsContainer, stylesPicsContainerL, stylesPicsContainerR, stylesImageL, stylesImageR, handlePictureView, }) => { const [arrPicsR, setArrPicsR] = useState([]); useEffect(() => { let makeArrPicsR = data.slice(1, 3); setArrPicsR(makeArrPicsR); }, [data]); const styles = StyleSheet.create({ picsContainer: { flexDirection: 'row', justifyContent: 'space-between', }, picsContainerL: {}, picsContainerR: { justifyContent: 'space-between' }, imageL: { borderWidth: 1.5, borderColor: _color['color-background-100'], height: wp('89%'), width: wp('44.5%'), resizeMode: 'cover', }, imageR: { borderWidth: 1.5, borderColor: _color['color-background-100'], height: wp('44%'), width: wp('44.5%'), resizeMode: 'cover', }, }); const picL = ( <TouchableOpacity onPress={() => handlePictureView(data[0], data)} style={{}}> <Image source={{ uri: data[0].Public_Link ? data[0].Public_Link : '', }} style={{ ...styles.imageL, ...stylesImageL, }} /> </TouchableOpacity> ); const picR = arrPicsR.map((value, idx) => ( <TouchableOpacity onPress={() => handlePictureView(value, data)} style={{}}> <Image source={{ uri: value.Public_Link ? value.Public_Link : '', }} style={{ ...styles.imageR, ...stylesImageR, }} /> </TouchableOpacity> )); return ( <> <Layout style={{ ...styles.picsContainer, stylesPicsContainer, }}> <Layout style={{ ...styles.picsContainerL, stylesPicsContainerL, }}> {picL} </Layout> <Layout style={{ ...styles.picsContainerR, stylesPicsContainerR, }}> {picR} </Layout> </Layout> </> ); }; const PicFB4 = ({ data, handlePictureView, stylesPicsContainer, stylesTouchablePics, stylesImage, }) => { const styles = StyleSheet.create({ picsContainer: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', }, touchablePics: {}, image: { borderWidth: 1.5, borderColor: _color['color-background-100'], height: wp('44.5%'), width: wp('44.5%'), resizeMode: 'cover', }, }); const renderItem = data.map((value, idx) => ( <TouchableOpacity onPress={() => handlePictureView(value, data)} style={{ ...styles.touchablePics, stylesTouchablePics, }}> <Image source={{ uri: value.Public_Link ? value.Public_Link : '', }} style={{ ...styles.image, ...stylesImage, }} /> </TouchableOpacity> )); return ( <> <Layout style={{ ...styles.picsContainer, stylesPicsContainer, }}> {renderItem} </Layout> </> ); }; const PicFB5 = ({ data, stylesPicsContainer, stylesPicsGroup, stylesImage1, stylesImage2, handlePictureView, }) => { const [arrPics1, setArrPics1] = useState([]); const [arrPics2, setArrPics2] = useState([]); useEffect(() => { let makeArrPics1 = data.slice(0, 2); setArrPics1(makeArrPics1); }, [data]); useEffect(() => { let makeArrPics2 = data.slice(2, 5); setArrPics2(makeArrPics2); }, [data]); const styles = StyleSheet.create({ picsContainer: { height: wp('75%'), justifyContent: 'space-between', }, picsGroup: { flexDirection: 'row', justifyContent: 'space-between', }, image1: { borderWidth: 1.5, width: wp('44.5%'), height: wp('44.5%'), resizeMode: 'cover', borderColor: _color['color-background-100'], }, image2: { borderWidth: 1.5, width: wp('29.5%'), resizeMode: 'cover', height: wp('29.5%'), borderColor: _color['color-background-100'], }, }); const renderPic1 = arrPics1.map((value, idx) => ( <TouchableOpacity onPress={() => handlePictureView(value, data)} style={{}}> <Image source={{ uri: value.Public_Link ? value.Public_Link : '', }} style={{ ...styles.image1, ...stylesImage1, }} /> </TouchableOpacity> )); const renderPic2 = arrPics2.map((value, idx) => ( <TouchableOpacity onPress={() => handlePictureView(value, data)} style={{}}> <Image source={{ uri: value.Public_Link ? value.Public_Link : '', }} style={{ ...styles.image2, ...stylesImage2, }} /> </TouchableOpacity> )); return ( <> <Layout style={{ ...styles.picsContainer, stylesPicsContainer, }}> <Layout style={{ ...styles.picsGroup, stylesPicsGroup, }}> {renderPic1} </Layout> <Layout style={{ ...styles.picsGroup, stylesPicsGroup, }}> {renderPic2} </Layout> </Layout> </> ); }; const PicFBOver6 = ({ data, handlePictureView, stylesPicsContainer, stylesTouchablePics, stylesImage, }) => { const styles = StyleSheet.create({ picsContainer: { flexDirection: 'row', flexWrap: 'wrap', }, touchablePics: { marginRight: wp('0.5%') }, image: { borderWidth: 1.5, borderColor: _color['color-background-100'], height: wp('29.5%'), width: wp('29.5%'), resizeMode: 'cover', }, }); const renderItem = data.map((value, idx) => ( <TouchableOpacity onPress={() => handlePictureView(value, data)} style={{ ...styles.touchablePics, stylesTouchablePics, }}> <Image source={{ uri: value.Public_Link ? value.Public_Link : '', }} style={{ ...styles.image, ...stylesImage, }} /> </TouchableOpacity> )); return ( <> <Layout style={{ ...styles.picsContainer, stylesPicsContainer, }}> {renderItem} </Layout> </> ); }; const PicFBPackage = ({ data, setPictureView }) => { return ( <> {/* 조건식에서, [ ] 빈 배열도 True를 리턴하기 때문에 (값이 존재하다고 인식함), data.length 로 수정하였다. data.length는 [ ] 빈 배열을 받으면, False로 리턴한다. 그 다음에는 null일 경우, 에러가 발생하여 (옵셔널체이닝)data?.length으로 변경해 주엇다 */} {data?.length ? ( (() => { switch (data.length) { case 1: return <PicFB1 data={data} handlePictureView={setPictureView} />; case 2: return <PicFB2 data={data} handlePictureView={setPictureView} />; case 3: return <PicFB3 data={data} handlePictureView={setPictureView} />; case 4: return <PicFB4 data={data} handlePictureView={setPictureView} />; case 5: return <PicFB5 data={data} handlePictureView={setPictureView} />; default: return ( <PicFBOver6 data={data} handlePictureView={setPictureView} /> ); } })() ) : ( <Text>사진이 존재하지 않습니다.</Text> )} </> ); }; export { PicFB1, PicFB2, PicFB3, PicFB4, PicFB5, PicFBOver6, PicFBPackage, // 사진장수에 따라, picFB1~PicFBOver6 실행됨 };
📍 결과물
▼ 사진1개, 2개, 3개일 때의 레이아웃
▼ 사진4개, 5개, 6개이상일 때의 레이아웃
개인적으로 개발시행착오를 겪으면서, 그런 경험들을 기록하기도하고, 모은정보들을 메모하며, 개인공부내용을 공유하는 게시물입니다. 친절한 조언과 다양한 의견 남겨주시고, 소통해주시는분들은 언제든지 환영합니다 :D
반응형'► React Native > 개발일기' 카테고리의 다른 글
[React Native] 중복클릭 방지 (1) 2023.09.01 [React Native] 라이브러리 및 속성 사용법 (react-native-safe-area-context) (0) 2023.08.29 [React Native] android와 ios의 경로가 다른걸 알게됨. (0) 2023.06.21 [React Native] 이미지경로가 에러일때, 알림띄우기 (<Image />) (0) 2023.06.19 [React Native] 다운로드한 이미지가, '파일'앱에 안나옴 (rn-fetch-blob) (0) 2023.06.15