17

I set the Expo camera to open on the middle tab with react navigation. However, the camera only opens when I click on that tab the first time. If I switch off of it and go back it's just a black screen. Also the take a picture button is not there. (I am new with react native and kinda coding as a whole)

'use strict';
import React, { Component } from 'react';
import { createBottomTabNavigator } from 'react-navigation';
import { Camera, Permissions } from 'expo';
import {
    AppRegistry,
    Dimensions,
    StyleSheet,
    Text,
    TouchableOpacity,
    View,
    Button
} from 'react-native';


class HomeScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Home!</Text>
      </View>
    );
  }
}

class CameraView extends React.Component {
  state = {
    hasCameraPermission: null,
    type: Camera.Constants.Type.back,
  };

  async componentWillMount() {
    const { status } = await Permissions.askAsync(Permissions.CAMERA);
    this.setState({ hasCameraPermission: status === 'granted' });
  }
  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 style={{ flex: 1 }} type={this.state.type}>
            <View
              style={{
                flex: 1,
                backgroundColor: 'transparent',
                flexDirection: 'row',
              }}>
              <TouchableOpacity
                style={{
                  flex: 0.1,
                  alignSelf: 'flex-end',
                  alignItems: 'center',
                }}
                onPress={() => {
                  this.setState({
                    type: this.state.type === Camera.Constants.Type.back
                      ? Camera.Constants.Type.front
                      : Camera.Constants.Type.back,
                  });
                }}>
                <Text
                  style={{ fontSize: 18, marginBottom: 10, color: 'white' }}>
                  {' '}Flip{' '}
                </Text>
              </TouchableOpacity>
            </View>
          </Camera>
        </View>
      );
    }
  }
}

class SettingsScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Settings!</Text>
      </View>
    );
  }
}


export default createBottomTabNavigator({
  Home: HomeScreen,
  Camera:CameraView,
  Settings: SettingsScreen
});

const styles = StyleSheet.create({
    container: {
        flex: 1,
        flexDirection: 'column',
        top: 250
    },

    capture: {
        flex: 0,
        backgroundColor: '#fff',
        borderRadius: 5,
        padding: 15,
        paddingHorizontal: 20,
        alignSelf: 'center',
        margin: 20
    }

});
borderal
  • 191
  • 1
  • 5

8 Answers8

22

With react navigation 5.x

import { useIsFocused } from '@react-navigation/native';

export const CameraView = (props) => {
    const isFocused = useIsFocused();
    return (
       <View>
         { isFocused && <RNCamera  />  }
       </View>
     )
}

useIsFocused Documentation

Avisek Chakraborty
  • 8,229
  • 10
  • 48
  • 76
Selmi Karim
  • 2,135
  • 14
  • 23
13

I had some issue.

This code solved it:

import { useIsFocused } from '@react-navigation/native';

function MyComponent() {
  const isFocused = useIsFocused()
  
  return (
    <View>
      { isFocused && <RNCamera />  }
    </View>
  )
}

Old navigation

import { withNavigationFocus } from 'react-navigation'

render() {
    const { isFocused } = this.props
    return (
       <View>
         { isFocused && <RNCamera  ... />  }
       </View>
     )
}


export default withNavigationFocus(Camera)
Vladislav Zaynchkovsky
  • 2,461
  • 2
  • 14
  • 22
7

To make this work you need to:

1.

import { NavigationEvents } from 'react-navigation';
state = { loaded: true }
render() {
    const { loaded } = this.state;
        return (
            <View style={styles.container}>
            <NavigationEvents
             onWillFocus={payload => this.setState({loaded: true})}
             onDidBlur={payload => this.setState({loaded: false})}/>
            <View style={styles.cameraArea}>
            {loaded && (
                <Camera
                 type={Camera.Constants.Type.back}
                 ref={ref => {
                     this.camera = ref;
                }}
              />
              )}          
            </View>

The idea is to hide this camera view (onDidBlur-> loaded: false), then when you come back (onWillFocus is triggered and change loaded to true). When render() function is called it will show the <Camera /> again. If you have any questions, feel free to ask.

Mark dG
  • 321
  • 1
  • 13
anurhd
  • 111
  • 1
  • 5
4

This works for me. ( Navigation 5.x ) if you are using a different screen for CAMERA, you can easy unmount the screen when moving to another.

Reason of this behavior : Only one Camera preview can be active at any given time. If you have multiple screens in your app, you should unmount Camera components whenever a screen is unfocused.

<Stack.Screen name="camera" component={CameraScreen} options={{unmountOnBlur: true}}/>
</Stack.Navigator>

Documentation Link : https://docs.expo.io/versions/latest/sdk/camera/

Thanks.

Sushil
  • 2,324
  • 1
  • 27
  • 26
2

I got it working by using NavigationEvents to determine if the tab is in focus and from there mount and unmount the camera. This also frees up the camera, if you need to use it in another screen. Here is what I would do in your example:

    import { NavigationEvents } from 'react-navigation';
    ...
    class CameraView extends React.Component {
      constructor(props) {
       super(props) 
       this.state = {
         hasCameraPermission: null,
         type: Camera.Constants.Type.back,
         isFocused:true
       };
      }

      ...

      render(){
       //...your existing if's
       } if(this.state.isFocused === false){
         return (
              <NavigationEvents
                    onWillFocus={payload => {
                        //console.log("will focus", payload);
                        this.setState({isFocused:true})
                    }}
                    onDidBlur={payload => {
                        //console.log('did leave',payload)
                        this.setState({isFocused:false})
                    }}
                />
         )
       }
       } else {
          return (
           <View style={{ flex: 1 }}>
            <Camera style={{ flex: 1 }} type={this.state.type}>
               <NavigationEvents
                    onWillFocus={payload => {
                        //console.log("will focus", payload);
                        this.setState({isFocused:true})
                    }}
                    onDidBlur={payload => {
                        //console.log('did leave',payload)
                        this.setState({isFocused:false})
                    }}
                />
          //...the rest of your camera code
      }

I hope it works for you as well

mmjensen
  • 41
  • 2
1

I solved it using the hook useIsFocused from react-navigation/native. I tested it on Android, iOS and Web

import { useIsFocused } from '@react-navigation/native';

...

const isFocused = useIsFocused();

...

return (
    isFocused && (
      <Camera
        ref={(ref) => {
          camera = ref;
        }}
        onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
        style={StyleSheet.absoluteFillObject}
      />
    )
  );
Juan Vieira
  • 173
  • 1
  • 9
  • I tried to incorporate the same method in my application, however, I am getting an error, `Couldn't find a navigation object. Is your component inside a screen in a navigator`. Any Idea what's wrong and how to go about fixing this? – Prajwal Kulkarni May 02 '21 at 08:39
1

in Expo react native

import { useIsFocused } from '@react-navigation/native';
    
const isFocused = useIsFocused();

{isFocused &&
        <Camera
            type={type}
            onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
            style={{ flex: 1 }}
            flashMode={flash}
            zoom={zoom}
        />
}
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Saqib00786
  • 29
  • 5
0

If you are using RN Expo with React Navigation - Tab Navigator.

Then just use unmountOnBlur - unmountOnBlur Documentation

This will force the camera to unmount on every navigation focus changes.

tHeSiD
  • 4,587
  • 4
  • 29
  • 49