react-native-view-shot
react-native-view-shot copied to clipboard
View nested inside Camera component doesn't show up in view shot
I want to put some overlays ontop of my camera view so that when the shot is taken by view shot it will be a picture with the overlays ontop. My issue is that when I put my <Overlay /> component nested inside the <Camera> component it doesn't show up in the result of the view-shot.
If I just do the Overlay then it works but once I nest it inside the Camera component ( using expo-camera import { Camera, CameraType } from "expo-camera"; ) only the camera shows up in the view-shot when I take it.
I have tried adding collapsable={false} to all my views inside the <Overlay /> component but that doesn't fix it. Is there something I am doing wrong?
import { Camera, CameraType } from "expo-camera";
import { useEffect, useRef, useState } from "react";
import {
Button,
Dimensions,
Platform,
StyleSheet,
Text,
TouchableOpacity,
View,
Image,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { Stack, useRouter } from "expo-router";
import Overlay from "./components/overlay";
import ViewShot from "react-native-view-shot";
const MyComponent: React.FC = () => {
const viewShotRef = useRef<ViewShot | null>(null);
const [screenshotUri, setScreenshotUri] = useState<string>("");
const handleRetake = () => {
setScreenshotUri("");
};
const handleCapture = async () => {
console.log("viewShotRef.current=", viewShotRef.current);
if (viewShotRef.current) {
try {
const uri = await viewShotRef.current.capture!();
console.log("Captured and do something with ", uri);
setScreenshotUri(uri);
} catch (error) {
console.error("Capture failed:", error);
}
}
};
const [type, setType] = useState(CameraType.front);
const [permission, requestPermission] = Camera.useCameraPermissions();
const [camera, setCamera] = useState<Camera | null>(null);
const toggleCameraType = () => {
setType((current) =>
current === CameraType.back ? CameraType.front : CameraType.back,
);
};
const [hasCameraPermission, setHasCameraPermission] = useState<boolean>();
// Screen Ratio and image padding
const [imagePadding, setImagePadding] = useState(0);
const [ratio, setRatio] = useState("4:3"); // default is 4:3
const { height, width } = Dimensions.get("window");
const screenRatio = height / width;
const [isRatioSet, setIsRatioSet] = useState(false);
// on screen load, ask for permission to use the camera
useEffect(() => {
const getCameraStatus = async () => {
const request = await requestPermission();
setHasCameraPermission(request.granted);
};
getCameraStatus();
}, []);
const prepareRatio = async () => {
let desiredRatio = "4:3";
if (Platform.OS === "android" && camera) {
const ratios = await camera.getSupportedRatiosAsync();
// ..... ratio calculation
setImagePadding(remainder);
setRatio(desiredRatio);
setIsRatioSet(true);
}
};
const setCameraReady = async () => {
if (!isRatioSet) {
await prepareRatio();
}
};
if (hasCameraPermission === null) {
return (
<View className="flex-1 items-center justify-center self-center">
<Text>Waiting for camera permissions</Text>
</View>
);
} else if (hasCameraPermission === false) {
return (
<View className="flex-1 items-center justify-center self-center">
<Text>No access to camera</Text>
</View>
);
} else {
return (
<SafeAreaView className="flex-1">
<Stack.Screen options={{ title: "Live" }} />
<View className="flex-1 justify-center bg-neutral-800">
{/*
We created a Camera height by adding margins to the top and bottom,
but we could set the width/height instead
since we know the screen dimensions
*/}
{screenshotUri == "" ? (
<ViewShot ref={viewShotRef} style={{ flex: 1 }}>
<Camera
style={{
flex: 1,
// marginTop: imagePadding,
marginBottom: imagePadding,
}}
onCameraReady={setCameraReady}
ratio={ratio}
type={type}
ref={(ref) => {
setCamera(ref);
}}
>
<Overlay
toggleCameraType={toggleCameraType}
handleCapture={handleCapture}
distance={1.32}
duration={4.55}
/>
</Camera>
</ViewShot>
) : (
<View>
<TouchableOpacity
onPress={handleRetake}
className="absolute left-2 top-2 z-10"
>
<Text>Retake</Text>
</TouchableOpacity>
<Image
source={{ uri: screenshotUri }}
style={{
height: undefined,
width: "100%",
aspectRatio: 9 / 16,
alignSelf: "center",
}}
/>
</View>
)}
</View>
</SafeAreaView>
);
}
Version & Platform
using react native expo (managed workflow, not expo go) version number of react-native-view-shot is "react-native-view-shot": "3.5.0",
**Platform: Android
<Overlay/> component below
import { TouchableOpacity, View, Text, Image } from "react-native";
import ff from "../../utils/globalStyles";
import run from "../../workout/run";
import { Ionicons } from "@expo/vector-icons";
import { Entypo } from "@expo/vector-icons";
import { useRouter } from "expo-router";
type OverlayProps = {
toggleCameraType: () => void;
handleCapture: () => Promise<void>;
distance: number;
duration: number;
};
const Overlay: React.FC<OverlayProps> = ({
toggleCameraType,
handleCapture,
distance,
duration,
}) => {
const router = useRouter();
return (
<View className="z-10 h-full justify-between" collapsable={false}>
<View className="flex-row justify-between px-2 pt-4" collapsable={false}>
<TouchableOpacity onPress={() => router.back()}>
<Entypo name="cross" size={36} color="white" />
</TouchableOpacity>
<Image
source={require("../../workout/img/myImage.png")}
style={{
height: undefined,
width: 140,
aspectRatio: 2.7,
alignSelf: "center",
}}
/>
<View className="w-9" />
</View>
<View className="flex-row justify-between px-10 pb-4" collapsable={false}>
<View className="w-12" />
<TouchableOpacity onPress={handleCapture}>
<View
className="h-20 w-20 justify-center rounded-full bg-white"
style={{ opacity: 0.5 }}
>
<View className="h-16 w-16 self-center rounded-full bg-white"></View>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={toggleCameraType} className="justify-center">
<Ionicons name="ios-camera-reverse-outline" size={48} color="white" />
</TouchableOpacity>
</View>
</View>
);
};
export default Overlay;
Hi @JordanHe were you able to solve this?
same issue