I have successfully implemented Expo’s barcode scanner in my app and working wonders. now I need a normal camera to take pictures of documents mainly (app is for internal office use). So I decided to make a simple camera page based on the full example linked in the docs (expo/camerja).
However, I keep getting black screen and it seems that this.camera = ref
was never called because I keep getting undefined for this.camera
, which implies any other functions that depends on this can’t be tested either, and I can only assume that this means camera component itself was never mounted.
Not sure what I’m missing here. What I’ve done:
- Check if the barcode scanner was mounted in any way, so I even tried deleting the part imported the scanner in my js file.
- tried logging onCameraReady activity and it’s not even triggered.
Here’s my code:
import React from 'react';
import { Text, View, TouchableOpacity, StyleSheet } from 'react-native';
import { Camera, Permissions, Constants } from 'expo';
import { Actions } from 'react-native-router-flux';
class CameraPage extends React.Component {
constructor(props) {
super(props);
this.state = {
hasCameraPermission: null,
type: Camera.Constants.Type.back,
pictureSize: undefined,
pictureSizes: [],
pictureSizeId: 0,
zoom: 0,
}
}
componentDidMount() {
this._requestCameraPermission();
}
_requestCameraPermission = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({
hasCameraPermission: status === 'granted',
});
};
takePicture = () => {
if (this.camera) {
this.camera.takePictureAsync({ onPictureSaved: this.onPictureSaved });
}
};
onPictureSaved = async photo => {
this.props.pictureTaken(photo.uri);
}
collectPictureSizes = async () => {
console.log(this.camera)
if (this.camera) {
const pictureSizes = await this.camera.getAvailablePictureSizesAsync('4:3');
let pictureSizeId = 0;
if (Platform.OS === 'ios') {
pictureSizeId = pictureSizes.indexOf('High');
} else {
// returned array is sorted in ascending order - default size is the largest one
pictureSizeId = pictureSizes.length - 1;
}
this.setState({ pictureSizes, pictureSizeId, pictureSize: pictureSizes[pictureSizeId] });
}
};
zoomOut = () => this.setState({ zoom: this.state.zoom - 0.1 < 0 ? 0 : this.state.zoom - 0.1 });
zoomIn = () => this.setState({ zoom: this.state.zoom + 0.1 > 1 ? 1 : this.state.zoom + 0.1 });
zoomButtons() {
return (
<View style={styles.zoomContainer}>
<IconWrapper name="zoom-out" size={40} color={color.white} onPress={this.zoomOut} style={{ padding: 6 }} />
<IconWrapper name="zoom-in" size={40} color={color.white} onPress={this.zoomIn} style={{ padding: 6 }} />
</View>
);
}
previousPictureSize = () => this.changePictureSize(1);
nextPictureSize = () => this.changePictureSize(-1);
changePictureSize = direction => {
let newId = this.state.pictureSizeId + direction;
const length = this.state.pictureSizes.length;
if (newId >= length) {
newId = 0;
} else if (newId < 0) {
newId = length - 1;
}
this.setState({ pictureSize: this.state.pictureSizes[newId], pictureSizeId: newId });
};
pictureSizePicker() {
return (<View style={styles.pictureSizeContainer}>
<Text style={styles.pictureQualityLabel}>Picture quality</Text>
<View style={styles.pictureSizeChooser}>
<IconWrapper name="chevron-left" size={40} color={color.white} onPress={this.previousPictureSize} style={{ padding: 6 }} />
<View style={styles.pictureSizeLabel}>
<Text style={styles.textStyle}>{this.state.pictureSize}</Text>
</View>
<IconWrapper name="chevron-right" size={40} color={color.white} onPress={this.nextPictureSize} style={{ padding: 6 }} />
</View>
</View>);
}
cameraModeButton() {
return (<IconWrapper name="ios-reverse-camera-outline" type='ionicon' size={40} color={color.white} onPress={() => {
this.setState({
type: this.state.type === Camera.Constants.Type.back
? Camera.Constants.Type.front
: Camera.Constants.Type.back,
});
}} style={{ flex: 1, justifyContent: 'center' }} />);
}
render() {
const { hasCameraPermission } = this.state;
if (hasCameraPermission === null) {
return <View />;
} else if (hasCameraPermission === false) {
return <Text>No access to camera</Text>;
} else {
return (
<View style={{ flex: 1 }}>
<Camera
ref={ref => { this.camera = ref; }}
style={styles.camera}
onCameraReady={this.collectPictureSizes}
type={this.state.type}
flashMode={Camera.Constants.FlashMode.auto}
autoFocus={Camera.Constants.AutoFocus.on}
zoom={this.state.zoom}
whiteBalance={Camera.Constants.WhiteBalance.auto}
pictureSize={this.state.pictureSize}>
<View style={styles.topBar}>
{this.pictureSizePicker()}
<IconWrapper name='close' color={color.white} size={40} onPress={() => Actions.pop()} style={{ flex: 0.3, padding: 6 }} />
</View>
<View style={styles.bottomBar}>
{this.zoomButtons()}
<IconWrapper name='radio-button-unchecked' color={color.white} size={50} onPress={this.takePicture} style={{ padding: 6 }} />
{this.cameraModeButton()}
</View>
</Camera>
</View>
);
}
}
}
export default CameraPage;
Can anyone point me in the right direction here?
Thanks in advance
UPDATE:
after trying it on actual phone it seems that the camera actually opened but still none of the callbacks are triggered, so there might be something wrong with the way i’m configuring it?
this.camera
staying undefined seems to be the root.