Fix Android ViewShot dropping frames and date overlays by temporarily lifting pointerEvents=none during capture

This commit is contained in:
2026-06-01 10:39:06 +02:00
parent 50df853ca8
commit 0a46baaa9b
2 changed files with 13 additions and 4 deletions
+3 -2
View File
@@ -3,6 +3,7 @@ import { StyleSheet, Image, View } from 'react-native';
interface FrameOverlayProps { interface FrameOverlayProps {
frameAsset: any; // require() asset frameAsset: any; // require() asset
isCapturing?: boolean;
} }
/** /**
@@ -10,11 +11,11 @@ interface FrameOverlayProps {
* The frame PNG must have a transparent center (alpha=0) so the photo shows through. * The frame PNG must have a transparent center (alpha=0) so the photo shows through.
* Positioned absolutely to cover the entire photo canvas. * Positioned absolutely to cover the entire photo canvas.
*/ */
export default function FrameOverlay({ frameAsset }: FrameOverlayProps) { export default function FrameOverlay({ frameAsset, isCapturing = false }: FrameOverlayProps) {
if (!frameAsset) return null; if (!frameAsset) return null;
return ( return (
<View style={styles.container} pointerEvents="none" collapsable={false}> <View style={styles.container} pointerEvents={isCapturing ? "auto" : "none"} collapsable={false}>
<Image <Image
source={frameAsset} source={frameAsset}
style={styles.frameImage} style={styles.frameImage}
+10 -2
View File
@@ -64,6 +64,7 @@ export default function PreviewScreen({
}: PreviewScreenProps) { }: PreviewScreenProps) {
const [layout, setLayout] = useState<CollageLayout>('single'); const [layout, setLayout] = useState<CollageLayout>('single');
const [isProcessing, setIsProcessing] = useState<boolean>(false); const [isProcessing, setIsProcessing] = useState<boolean>(false);
const [isCapturing, setIsCapturing] = useState<boolean>(false);
const [statusMessage, setStatusMessage] = useState<string>(''); const [statusMessage, setStatusMessage] = useState<string>('');
@@ -204,14 +205,21 @@ export default function PreviewScreen({
try { try {
logger.log(`captureComposite started. Has ViewShot ref: ${!!viewShotRef.current}`); logger.log(`captureComposite started. Has ViewShot ref: ${!!viewShotRef.current}`);
if (viewShotRef.current) { if (viewShotRef.current) {
setIsCapturing(true);
// Wait for React to re-render and remove pointerEvents="none" which blocks ViewShot on Android
await new Promise(resolve => setTimeout(resolve, 300));
logger.log('Executing viewShotRef.current.capture()...'); logger.log('Executing viewShotRef.current.capture()...');
const uri = await viewShotRef.current.capture(); const uri = await viewShotRef.current.capture();
setIsCapturing(false);
logger.log(`ViewShot successfully generated collage URI: ${uri}`); logger.log(`ViewShot successfully generated collage URI: ${uri}`);
return uri; return uri;
} }
logger.log('[WARNING] viewShotRef.current was null, falling back to raw photo.'); logger.log('[WARNING] viewShotRef.current was null, falling back to raw photo.');
return fallbackPhoto; return fallbackPhoto;
} catch (e: any) { } catch (e: any) {
setIsCapturing(false);
logger.error('Failed to capture composite (ViewShot crashed)', e?.message || e); logger.error('Failed to capture composite (ViewShot crashed)', e?.message || e);
return fallbackPhoto; return fallbackPhoto;
} }
@@ -453,11 +461,11 @@ export default function PreviewScreen({
{renderCollageView()} {renderCollageView()}
{/* Frame Overlay */} {/* Frame Overlay */}
{activeFrame && <FrameOverlay frameAsset={activeFrame.asset} />} {activeFrame && <FrameOverlay frameAsset={activeFrame.asset} isCapturing={isCapturing} />}
{/* Date/Time Overlay */} {/* Date/Time Overlay */}
{dateOverlay !== 'off' && ( {dateOverlay !== 'off' && (
<View style={[styles.dateOverlay, getDatePositionStyle()]} pointerEvents="none"> <View style={[styles.dateOverlay, getDatePositionStyle()]} pointerEvents={isCapturing ? "auto" : "none"}>
<Text style={styles.dateOverlayText}> <Text style={styles.dateOverlayText}>
{formatDate(dateOverlay)} {formatDate(dateOverlay)}
</Text> </Text>