기능목록
- 사진첨부하기 클릭시 카메라와 갤러리를 선택 할 수 있는 모달
- 카메라
- 카메라 권한 묻기
- 사진촬영
- 사진 촬영 시 재촬영 또는 저장하기 선택
- 갤러리
- 사진 권한 묻기
- 사진 목록 불러오기
- 사진 선택하기, 선택시 카운트 올리기
모달
const gotoCamera = (type: string) => {
navigation.navigate('CameraScreen', {
screen: 'CameraScreen',
params: {type},
});
setIsShowModal(false);
};
return( <Modal
isVisible={isShowModal}
onBackdropPress={toggleModal}
style={addFileModalStyle}>
<AttachTypeContainer>
<TypeContainer onPress={() => gotoCamera('camera')}>
<IconCircle>
<CameraIcon />
</IconCircle>
<TypeText>카메라</TypeText>
</TypeContainer>
<TypeContainer onPress={() => gotoCamera('gallery')}>
<IconCircle>
<GalleryIcon />
</IconCircle>
<TypeText>갤러리</TypeText>
</TypeContainer>
</AttachTypeContainer>
<AttachGuide>첨부 방법을 선택해 주세요.</AttachGuide>
</Modal>
)
const CameraScreen = () => {
const {params}: RouteProp<CameraProps> = useRoute();
return (
<Container>
{params?.type === 'camera' && <CameraModule />}
{params?.type === 'gallery' && <GalleryModule />}
</Container>
);
};
카메라
const CameraModule = () => {
const cameraRef = useRef<Camera | null>(null);
const [cameraType, setCameraType] = useState(CameraType.back);
const [photo, setPhoto] = useState('');
const [isPermission, setIsPermission] = useState(false);
const takePictureHandler = async () => {
if (!cameraRef.current) return;
const photo = await cameraRef.current.takePictureAsync();
setPhoto(photo.uri);
};
useEffect(() => {
const getCameraPermission = async () => {
const {status} = await Camera.requestCameraPermissionsAsync();
if (status === 'granted') {
setIsPermission(true);
} else {
alert('카메라 접근 허용은 필수입니다.');
}
};
getCameraPermission();
}, []);
const savePicture = async () => {
if (photo) {
const {status} = await MediaLibrary.requestPermissionsAsync();
if (status === 'granted') {
const asset = await MediaLibrary.createAssetAsync(photo);
retakePicture();
alert('저장완료!');
try {
await MediaLibrary.createAlbumAsync('Camera', asset, false);
} catch (error) {
console.error(
'An error occurred while saving the photo to camera roll:',
error,
);
}
}
}
};
const retakePicture = () => {
setPhoto('');
};
return (
<>
<CustomHeader isBackButton></CustomHeader>
{isPermission && (
<>
{photo !== '' ? (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
source={{uri: photo}}
style={{flex: 1, width: '100%'}}
resizeMode="contain"
/>
<AfterBtn>
<Pressable onPress={retakePicture}>
<ButtonText>다시 찍기</ButtonText>
</Pressable>
<Pressable onPress={savePicture}>
<ButtonText>저장</ButtonText>
</Pressable>
</AfterBtn>
</View>
) : (
<>
<Camera
ref={cameraRef}
type={cameraType}
autoFocus={AutoFocus.on}
style={{flex: 1}}
/>
<ShootingBtn onPress={takePictureHandler}>
<ButtonText>촬영</ButtonText>
</ShootingBtn>
</>
)}
</>
)}
</>
);
};
갤러리
const GalleryModule = () => {
const [endCursor, setEndCursor] = useState('');
const [hasNextPage, setHasNextPage] = useState(false);
const [galleryList, setGalleryList] = useState<MediaLibrary.Asset[]>([]);
const [isPermission, setIsPermission] = useState(false);
const [selectedPhotos, setSelectedPhotos] = useRecoilState(SelectedPhotos);
const navigation = useNavigation<CameraProps>();
const windowWidth = Dimensions.get('window').width;
const itemWidth = windowWidth / 3;
useEffect(() => {
const getGalleryPermission = async () => {
const {status} = await MediaLibrary.requestPermissionsAsync();
if (status !== 'granted') {
console.log('Media library permission denied');
Linking.openSettings();
return;
}
setIsPermission(true);
fetchPhotos();
};
getGalleryPermission();
}, []);
const renderPhotoItem: React.FC<{item: MediaLibrary.Asset}> = ({item}) => {
const selectedIndex = [...selectedPhotos].findIndex(
({id}) => item.id === id,
);
const isSelected = selectedIndex !== -1;
return (
<Pressable
style={{width: itemWidth, aspectRatio: 1, position: 'relative'}}
onPress={() => {
if (isSelected) {
const clone = [...selectedPhotos];
clone.splice(selectedIndex, 1);
setSelectedPhotos(clone);
} else {
if (selectedPhotos.length < 6) {
const clone = [...selectedPhotos];
clone.push(item);
setSelectedPhotos(clone);
} else {
}
}
``;
}}>
<DotStyle isSelected={isSelected}>
{isSelected && decideSelectedIndicator(selectedIndex)}
</DotStyle>
<Image
source={{uri: item.uri}}
style={{flex: 1, resizeMode: 'cover'}}
/>
</Pressable>
);
};
const decideSelectedIndicator = (idx: number) => {
return (
<Text style={{color: '#ffffff', backgroundColor: '#287FFF'}}>
{idx + 1}
</Text>
);
};
const renderCameraButton = () => {
const moveToCamera = () => {
navigation.navigate('CameraScreen', {
screen: 'CameraScreen',
params: {type: 'camera'},
});
};
return (
<Pressable
onPress={moveToCamera}
style={{
width: itemWidth,
aspectRatio: 1,
backgroundColor: '#ffffff',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}>
<CameraIcon />
<Text>사진 촬영</Text>
</Pressable>
);
};
const handleEndReached = () => {
if (hasNextPage) {
fetchPhotos(endCursor);
}
};
const fetchPhotos = async (cursor?: string | undefined) => {
try {
const {
assets,
endCursor: newEndCursor,
hasNextPage,
} = await MediaLibrary.getAssetsAsync({
mediaType: MediaLibrary.MediaType.photo,
first: 20,
after: cursor,
});
setGalleryList(prevPhoto => [...prevPhoto, ...assets]);
setEndCursor(newEndCursor);
setHasNextPage(hasNextPage);
} catch (error) {
console.log('Error fetching photos:', error);
}
};
const submitHandler = () => {
setSelectedPhotos(selectedPhotos.slice(0, 5));
navigation.goBack();
};
return (
<>
<View style={{backgroundColor: '#ffffff'}}>
<CustomHeader isBackButton>
<Text style={{fontSize: 20, marginLeft: -10}}>최근 항목</Text>
</CustomHeader>
</View>
{isPermission && (
<>
<FlatList
data={[null, ...galleryList]}
keyExtractor={(item, index) => index.toString()}
renderItem={({item, index}) =>
item
? renderPhotoItem({item})
: index === 0
? renderCameraButton()
: null
}
numColumns={3}
onEndReached={handleEndReached}
onEndReachedThreshold={0.1}
/>
<ShootingBtn onPress={submitHandler}>
<ButtonText>완료</ButtonText>
</ShootingBtn>
</>
)}
</>
);
};
참고블로그