Introduction
React-Native-Camera, while once popular and powerful, has been deprecated, leaving developers in search of an alternative. Enter VisionCamera, a fully featured camera library developed by mrousavy.
In this series of articles, we will explore the power and versatility of VisionCamera. From installation and basic usage to more advanced features such as picture capture, video recording, and even face detection, this guide will equip you with the knowledge and skills to leverage VisionCamera to its fullest potential in your React Native applications. So let's dive in and unlock the possibilities of VisionCamera together.
Section 1: Setting up Vision Camera
1.1 Installing react-native-vision-camera:
Install react-native-vision-camera with yarn:
yarn add react-native-vision-camera
And make sure to install pod dependencies:
cd ios && pod install && cd ..
1.2 Configuring iOS:
Open your project's Info.plist
and add the following lines inside the outermost <dict>
tag:
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Camera.</string'>
The $(PRODUCT_NAME)
is a variable of your application name
1.3 Configuring Android:
Open your project's AndroidManifest.xml
and add the following lines inside the <manifest>
tag:
<uses-permission android:name="android.permission.CAMERA" />
1.4 Getting/Requesting Permissions:
To find out if a user has granted or denied:
const cameraPermission = await Camera.getCameraPermissionStatus()
To get permission to use Camera:
const newCameraPermission = await Camera.requestCameraPermission()
But if user denied, user has go to settings and allow camera permission manually. There for we should double check the camera permission and open settings if user is not granted.
So this is the full permission handle code:
import {Alert, Linking} from 'react-native';
import {Camera} from 'react-native-vision-camera';
export const requestCameraPermission = async () => {
const cameraPermission = await Camera.requestCameraPermission();
if (cameraPermission !== 'authorized') {
Alert.alert(
'You need to allow camera permission.',
'Please go to Settings and allow camera permission',
[
{
text: 'Open Settings',
onPress: Linking.openSettings,
},
],
);
}
return cameraPermission;
};
Section 2: Picture Capture
2.1: Camera setup
Camera devices
Camera devices are the physical (or "virtual") devices that can be used to record videos or capture photos.
To get a camera device, we use the useCameraDevices
hook and specific with camera could be used
const devices = useCameraDevices();
const device = devices.back;
if (!device) {
return <ActivityIndicator />;
}
return (
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive
/>
);
camera ref
we use ref to access the photo capture function
const camera = useRef<Camera>(null);
/*...*/
return (
<Camera
style={StyleSheet.absoluteFill}
device={device}
isActive
// Add this
ref={camera}
photo
/>
);
CameraWrapper component
I added this component with the aim of being able to easily switch between showing the camera as a separate screen or as a modal. You can skip this component
// CameraWrapper.tsx
import React from 'react';
import {ActivityIndicator, Modal, SafeAreaView, StyleSheet} from 'react-native';
interface CameraWrapperProps {
isActive: boolean;
onInactive?: () => void;
children: React.ReactNode;
loading?: boolean;
}
const CameraWrapper = (props: CameraWrapperProps) => {
const {isActive, onInactive, children, loading} = props;
return (
<Modal
visible={isActive}
onRequestClose={onInactive}
style={styles.container}>
<SafeAreaView style={StyleSheet.absoluteFill}>
{loading ? <ActivityIndicator style={styles.loading} /> : children}
</SafeAreaView>
</Modal>
);
};
export default CameraWrapper;
const styles = StyleSheet.create({
container: {
flex: 1,
},
loading: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
2.2 Taking picture:
To taking a picture use the camera ref:
const photo = await camera.current.takePhoto()
Capture button
To do the image capture, I write a component that manages the capture. This is the photo button that receives the props as the camera ref and a callback function that returns the captured image:
import React, {RefObject, useCallback} from 'react';
import {StyleSheet, TouchableOpacity, View, ViewProps} from 'react-native';
import {Camera, PhotoFile} from 'react-native-vision-camera';
interface CaptureButtonProps extends ViewProps {
camera: RefObject<Camera>;
onMediaCaptured: (file: PhotoFile) => void;
}
const CaptureButton = (props: CaptureButtonProps) => {
const {camera, onMediaCaptured, style, ...rest} = props;
const onPress = useCallback(async () => {
const photo = await camera.current?.takePhoto();
onMediaCaptured(photo!);
}, [camera, onMediaCaptured]);
return (
<TouchableOpacity
{...rest}
onPress={onPress}
style={[styles.captureButton, style]}>
<View style={styles.captureButtonInner} />
</TouchableOpacity>
);
};
export default CaptureButton;
const styles = StyleSheet.create({
captureButton: {
width: 80,
height: 80,
borderRadius: 40,
padding: 4,
borderWidth: 2,
borderStyle: 'dotted',
borderColor: 'white',
},
captureButtonInner: {
flex: 1,
borderRadius: 40,
backgroundColor: 'white',
},
});
And use it inside our TakePicture component:
const onPhotoCaptured = useCallback((file: PhotoFile) => {
console.log(file);
}, []);
return (
<CameraWrapper isActive={isActive} loading={!device} onInactive={onInactive}>
<Camera
ref={camera}
style={StyleSheet.absoluteFill}
device={device!}
isActive={isActive}
photo
/>
<CaptureButton
camera={camera}
style={styles.captureButton}
onMediaCaptured={onPhotoCaptured}
/>
</CameraWrapper>
);
Console result:
2.3 Preview captured picture
We can use react-native's Image to display the result of our capture feature
import React, {useMemo} from 'react';
import {Button, Image, Modal, SafeAreaView, StyleSheet} from 'react-native';
interface MediaPreviewProps {
mediaPath?: string;
onInactive?: () => void;
}
const MediaPreview = ({mediaPath, onInactive}: MediaPreviewProps) => {
const source = useMemo(() => ({uri: mediaPath}), [mediaPath]);
return (
<Modal visible={!!mediaPath} onRequestClose={onInactive}>
<SafeAreaView style={[StyleSheet.absoluteFill]}>
<Image source={source} style={StyleSheet.absoluteFill} />
<Button onPress={onInactive} title="Close" />
</SafeAreaView>
</Modal>
);
};
export default MediaPreview;
2.4 Zooming
To let user to zoom with native pinch to zoom gesture you can enable with the enableZoomGesture prop
<Camera
ref={camera}
style={StyleSheet.absoluteFill}
device={device!}
isActive={isActive}
photo
// for zooming
enableZoomGesture
/>
2.5 Focusing
To focus the camera to a specific point, simply use the Camera's focus(...) function with onTouchStart
of any View component:
<Camera
ref={camera}
style={StyleSheet.absoluteFill}
device={device!}
isActive={isActive}
photo
// For focusing
onTouchStart={handleFocus}
enableZoomGesture
/>
Handling focus touch:
const handleFocus = useCallback(
async ({nativeEvent}: GestureResponderEvent) => {
await camera?.current?.focus({
x: Math.round(nativeEvent.pageX),
y: Math.round(nativeEvent.pageX),
});
},
[],
);
Section 3: Recording Videos
3.1 Record audio permissions (Optional):
If you want to record audio on recording video, you have to update your application's manifests
iOS
Open your project's Info.plist
and add the following lines inside the outermost <dict>
tag:
key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Microphone.</string>
Android
Open your project's AndroidManifest.xml
and add the following lines inside the <manifest>
tag:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
and we also have the requestMicrophonePermission
function:
import {Alert, Linking} from 'react-native';
import {Camera} from 'react-native-vision-camera';
export const requestMicrophonePermission = async () => {
const microphonePermission = await Camera.requestMicrophonePermission();
if (microphonePermission !== 'authorized') {
Alert.alert(
'You need to allow microphone permission.',
'Please go to Settings and allow microphone permission',
[
{
text: 'Open Settings',
onPress: Linking.openSettings,
},
],
);
}
return microphonePermission;
};
3.2 Update the Camera component:
In this section I will copy the component of the photography section, which means that video recording will also have full features like focus or zoom. For Camera to record video add video
prop and if you want to record audio add audio
prop:
<Camera
ref={camera}
style={StyleSheet.absoluteFill}
device={device!}
isActive={isActive}
onTouchStart={handleFocus}
enableZoomGesture
photo
video
audio // optional
/>
3.3 Hold Capture button to start recording video
To start recording, the user will hold down the capture button. The button will turn red indicating the recording status and when the user clicks this button again, the video recording will end.
I also updated the onMediaCaptured callback to add the VideoFile data type
// import
interface CaptureButtonProps extends ViewProps {
camera: RefObject<Camera>;
onMediaCaptured: (
media: PhotoFile | VideoFile,
type: 'photo' | 'video',
) => void;
}
const CaptureButton = (props: CaptureButtonProps) => {
const {camera, onMediaCaptured, style, ...rest} = props;
const [isRecording, setIsRecording] = React.useState(false);
const innerStyle = useMemo(
() => ({
backgroundColor: isRecording ? 'red' : 'white',
}),
[isRecording],
);
const takePhoto = useCallback(async () => {
if (isRecording) {
setIsRecording(false);
return camera.current?.stopRecording();
}
const photo = await camera.current?.takePhoto();
onMediaCaptured(photo!, 'photo');
}, [camera, onMediaCaptured, isRecording]);
const startRecordingVideo = useCallback(async () => {
setIsRecording(true);
return camera.current?.startRecording({
onRecordingFinished: video => onMediaCaptured(video, 'video'),
onRecordingError: error => console.error(error),
});
}, [camera, onMediaCaptured]);
return (
<TouchableOpacity
{...rest}
style={[styles.captureButton, style]}
onPress={takePhoto}
onLongPress={startRecordingVideo}>
<View style={[styles.captureButtonInner, innerStyle]} />
</TouchableOpacity>
);
};
export default CaptureButton;
3.4 Preview video
We use react-native-video to display the recorded video.
Install it with yarn:
yarn add react-native-video
And make sure to install pod dependencies:
cd ios && pod install && cd ..
Usage:
<Video
source={source}
style={StyleSheet.absoluteFill}
resizeMode="cover"
posterResizeMode="cover"
allowsExternalPlayback={false}
automaticallyWaitsToMinimizeStalling={false}
disableFocus={true}
repeat={true}
useTextureView={false}
controls={false}
playWhenInactive={true}
/>
Section 4: Saving the media
4.1 Requesting Permissions
import {Alert, Linking, PermissionsAndroid, Platform} from 'react-native';
export const requestSavePermission = async () => {
if (Platform.OS !== 'android') {
return true;
}
const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE;
if (permission == null) {
return false;
}
let hasPermission = await PermissionsAndroid.check(permission);
if (!hasPermission) {
const permissionRequestResult = await PermissionsAndroid.request(
permission,
);
hasPermission = permissionRequestResult === 'granted';
}
if (!hasPermission) {
Alert.alert(
'You need to allow storage permission.',
'Please go to Settings and allow storage permission',
[
{
text: 'Open Settings',
// On pressing the button, we will open the settings
onPress: Linking.openSettings,
},
],
);
}
return hasPermission;
};
We use @react-native-camera-roll/camera-roll to saving media.
Install it with yarn:
yarn add @react-native-camera-roll/camera-roll
And make sure to install pod dependencies:
cd ios && pod install && cd ..
Open your project's Info.plist
and add the following lines inside the outermost <dict>
tag:
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) needs access to your Photo Library.</string>
Open your project's AndroidManifest.xml
and add android:requestLegacyExternalStorage="true"
to application
tag and the following inside manifest
tag:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Usage:
const handleSave = useCallback(async () => {
setSaveStatus('saving');
const hasPermission = await requestSavePermission();
if (hasPermission) {
await CameraRoll.save(`file://${mediaPath}`, {
type,
});
setSaveStatus('saved');
}
}, [mediaPath, type]);
Conclusion:
We have explored the power and versatility of VisionCamera as an alternative to the deprecated React-Native-Camera library. We have covered the installation process and basic configuration steps for both iOS and Android platforms. Additionally, we have discussed how to handle camera permissions and provided code examples for requesting and checking camera permissions.
I hope that this series of articles has provided you with a comprehensive understanding of VisionCamera and its capabilities. By leveraging VisionCamera, you can enhance your React Native applications with powerful camera features, such as picture capture and video recording. In the next article, we will dive deeper into customizing the camera interface by exploring options for zooming and configuring photo/video settings.
Stay tuned for the next article, where we will explore custom zoom options and provide insights on configuring photo and video settings to further enhance your camera functionality.
To access the complete source code and examples discussed in this series, please visit the GitHub repository: this
References:
https://www.react-native-vision-camera.com
https://github.com/mrousavy/react-native-vision-camera
https://www.npmjs.com/package/react-native-video
https://www.npmjs.com/package/@react-native-camera-roll/camera-roll
Top comments (0)