Кнопка «Закрыть» исчезает: кнопка «Закрыть», видимая в версии для разработки, отсутствует в версии для сборки.
Невыровненные элементы: выравнивание элементов пользовательского интерфейса в сборке отключено, хотя в приложении Expo Go оно выглядит правильно.
Жест двойного касания не работает. В приложении Expo Go я могу дважды коснуться экрана, чтобы сделать снимок, но эта функция не работает в версии сборки. В журналах сборки ошибок нет.
Вот два экрана:
Версия для разработчиков (Expo Go)

< strong>Версия сборки

Вот соответствующий код, который обрабатывает жест двойного касания и отрисовку пользовательского интерфейса:
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { StyleSheet, Text, View, Dimensions, Image, TouchableWithoutFeedback, TouchableOpacity } from 'react-native';
// libraries
import { CameraView, useCameraPermissions } from 'expo-camera';
import { Button, Divider, IconButton } from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialIcons';
import * as ImageManipulator from 'expo-image-manipulator';
import { useTranslation } from 'react-i18next';
// components
import CustomDashedLine from '../components/CustomDashline';
// ----------------------------------------------------------------------
const STEPS = {
CLOSE: 'close',
OPEN: 'open',
TOP_BOTTOM: 'topBottom',
CLOSE_AL: 'closeAl',
OPEN_AL: 'openAl',
};
const initialImagesState = {
close: [],
open: [],
topBottom: [],
closeAl: [],
openAl: [],
};
const HELPING_IMAGES = {
'close-face': require('../assets/scan-icons/close-face.png'),
'open-face': require('../assets/scan-icons/open-face.png'),
'top-bottom': require('../assets/scan-icons/top-bottom.png'),
};
const { height: DEVICE_HEIGHT, width: DEVICE_WIDTH } = Dimensions.get('window');
// ----------------------------------------------------------------------
const CameraScreen = ({ navigation, route }) => {
const { updatedImages } = route.params;
const { t, i18n } = useTranslation();
const textDirection = i18n.dir();
const [permission, requestPermission] = useCameraPermissions();
const [isTakingPicture, setIsTakingPicture] = useState(false);
const [step, setStep] = useState(STEPS.CLOSE);
const [images, setImages] = useState(updatedImages || initialImagesState);
const [enableTorch, setEnableTorch] = useState(false);
const [showPrompt, setShowPrompt] = useState(true);
const tapRef = useRef(null);
const handleDoubleTap = useCallback(() => {
if (tapRef.current !== null) {
clearTimeout(tapRef.current);
setShowPrompt(false);
tapRef.current = null;
} else {
tapRef.current = setTimeout(() => {
tapRef.current = null;
}, 200);
}
}, []);
useEffect(() => {
setShowPrompt(true);
}, [step]);
const takePicture = async () => {
// take the pic and send with API
};
const renderIcons = (step) => {
if (step === STEPS.CLOSE || step === STEPS.CLOSE_AL) {
return (
{images[step].length > 2 ?
:
3
}
{images[step].length > 1 ?
:
2
}
{images[step].length > 0 ?
:
1
}
{step === STEPS.CLOSE_AL &&
{t('camera.alert')}
}
);
}
if (step === STEPS.OPEN || step === STEPS.OPEN_AL) {
return (
{images[step].length > 2 ?
:
3
}
{images[step].length > 1 ?
:
2
}
{images[step].length > 0 ?
:
1
}
{step === STEPS.OPEN_AL &&
{t('camera.alert')}
}
);
}
};
const renderHelperBar = (step) => {
if (step === STEPS.TOP_BOTTOM) {
return (
{images[step].length > 0 ?
:
1
}
{images[step].length > 1 ?
:
2
}
);
}
return (
{renderIcons(step)}
)
}
return (
{
camera = ref;
}}
onCameraReady={() => {
setEnableTorch(true);
}}
shutterSound={false}
mute={true}
cameraRatio="16:9"
>
}
size={40}
onPress={() => {
navigation.goBack();
setEnableTorch(false)
}}
style={styles.iconButton}
/>
{showPrompt && (
{HELPING_TEXT[step].text}
{t('camera.double-tap')}
{t('camera.aim')}
)}
{!showPrompt && (
)}
{renderHelperBar(step)}
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
camera: {
flex: 1,
width: '100%',
height: '100%',
},
buttonContainer: {
position: 'absolute',
right: 20,
bottom: 20,
alignSelf: 'center',
alignItems: 'center',
zIndex: 10,
backgroundColor: 'transparent',
},
iconButton: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderRadius: 50,
},
text: {
fontSize: 24,
fontWeight: 'bold',
color: 'white',
},
permissionContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
},
permissionButton: {
position: 'absolute',
bottom: 20,
right: 20,
},
helperBarContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'flex-start',
paddingLeft: 10,
},
helperBar: {
alignItems: 'center',
},
divider: {
backgroundColor: 'white',
width: 2,
height: 200,
marginVertical: 25,
opacity: 0.5,
},
helperBarVerContainer: {
position: 'absolute',
top: 50,
left: 40,
justifyContent: 'center',
alignItems: 'center',
},
helperBarVer: {
flexDirection: 'row-reverse',
alignItems: 'center',
},
horizontalDivider: {
backgroundColor: 'white',
height: 2,
width: 110,
marginHorizontal: 10,
opacity: 0.5,
},
helperIcon: {
opacity: 0.5,
transform: [{ rotate: '90deg' }],
},
sizeClose: {
height: 30,
width: 80,
},
sizeOpen: {
height: 40,
width: 60,
},
sizeTopBottom: {
height: 50,
width: 60,
},
alertContainer: {
position: 'absolute',
top: '50%',
left: -100,
backgroundColor: 'rgba(7, 141, 238, 0.2)',
padding: 10,
borderRadius: 10,
transform: [{ scaleY: -1 }, { rotate: '90deg' }],
alignItems: 'center',
alignItems: 'flex-start',
justifyContent: 'center',
flexDirection: 'row',
},
fullScreenTouchable: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
},
promptContainer: {
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: 20,
borderRadius: 10,
transform: [{ rotate: '90deg' }],
},
promptText: {
color: 'white',
fontSize: 18,
textAlign: 'center',
transform: [{ scaleX: -1 }]
},
helperNumbers: {
color: 'white',
transform: [{ scaleY: -1 }, { rotate: '90deg' }],
position: 'absolute',
left: 0,
},
helperNumbersVer: {
color: 'white',
transform: [{ scaleY: -1 }, { rotate: '90deg' }],
position: 'absolute',
top: -7,
},
overlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
cropLine: {
position: 'absolute',
left: '20%',
width: '70%',
},
});
export default CameraScreen;
Вот мой app.json:
{
"expo": {
"name": "my-app",
"slug": "my-app",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/ic_launcher.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"permissions": [
"android.permission.RECORD_AUDIO",
"android.permission.CAMERA",
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE",
"CAMERA",
"RECORD_AUDIO",
"READ_EXTERNAL_STORAGE",
"WRITE_EXTERNAL_STORAGE"
],
"package": "com.my.app"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.my.app"
},
"web": {
"favicon": "./assets/favicon.png"
},
"plugins": [
[
"expo-image-picker",
{
"photosPermission": "The app accesses your photos to let you share them with your friends."
}
]
],
"extra": {
"eas": {
"projectId": "my_id"
}
}
}
}
И мой eas.json
{
"cli": {
"version": ">= 10.0.2"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"previewApk": {
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"previewBundle": {
"distribution": "internal",
"android": {
"buildType": "app-bundle"
}
},
"preview2": {
"android": {
"gradleCommand": ":app:assembleRelease"
}
},
"production": {
"android": {
"buildType": "app-bundle"
}
}
},
"submit": {
"production": {}
}
}
Подробнее здесь: https://stackoverflow.com/questions/791 ... -and-build
Мобильная версия