react-native-vision-camera icon indicating copy to clipboard operation
react-native-vision-camera copied to clipboard

🐛 Adding border around Camera's direct parent causes offset preview

Open High5Apps opened this issue 2 years ago • 3 comments

What's happening?

Using [email protected] on an iPhone 13 Mini, adding a border to the direct parent of a Camera component causes the preview to be offset and slightly smaller than it should be, despite the Camera having a style of StyleSheet.absolutefill (see first screenshot). Setting borderWidth: 0 prevents the issue (see second screenshot). I believe the offset size is related to the borderWidth, since doubling the borderWith seems to double the offset (see third screenshot). This only happens on my iPhone 13 Mini, not on my Android S10, leading me to believe that this is an iOS-only issue. I did not face this same issue when I was on 2.15.4, but only started hitting it when I recently upgraded from 2.15.4 to 3.3.1.

Here's a workaround for others hitting this issue (see fourth screenshot). Just add an additional View component without a border as the direct child of your bordered component, and as direct parent of the Camera.

<View style={{ borderWidth: 8, borderColor: 'red', ... }}>
  {/* Add the view below as a workaround */}
  <View style={StyleSheet.absoluteFill}>
    <Camera ... />
  </View>
</View>

With borderWidth: 8: with-border

With borderWidth: 0: without-border

With borderWidth: 16: with-double-sized-border

With borderWith: 8 and workaround: with-border-workaround

Reproduceable Code

// Modifying the 3.3.1 example app CameraPage.ts line 172
// https://github.com/mrousavy/react-native-vision-camera/blob/a498144fd5786c6dba1d78730d362e0f675cbaf7/package/example/src/CameraPage.tsx#L172
// The 2 Views are added, the ReanimatedCamera is unchanged, aside from disabling the fpsGraph
// Note that in order to get the 3.3.1 example app to build, I needed to comment out the line 289 in CameraView+AVCaptureSession.swift
// https://github.com/mrousavy/react-native-vision-camera/blob/a498144fd5786c6dba1d78730d362e0f675cbaf7/package/ios/CameraView%2BAVCaptureSession.swift#L289

<View style={{ flex: 1, justifyContent: 'center' }}>
  <View style={{ alignSelf: 'center', borderWidth: 8, borderColor: 'red', height: 150, width: 150 }}>
    <ReanimatedCamera ... />
  </View>
</View>

Relevant log output

No relevant log outputs or errors, just a style issue

Camera Device

{
  "maxZoom": 121.875,
  "minZoom": 1,
  "hasTorch": true,
  "isMultiCam": false,
  "hardwareLevel": "full",
  "position": "back",
  "physicalDevices": [
    "wide-angle-camera"
  ],
  "supportsRawCapture": false,
  "supportsFocus": true,
  "neutralZoom": 1,
  "name": "Back Camera",
  "hasFlash": true,
  "id": "com.apple.avfoundation.avcapturedevice.built-in_video:0",
  "supportsLowLightBoost": false,
  "sensorOrientation": "portrait",
  "formats": []
}

Device

iPhone 13 Mini (iOS 16.6.1)

VisionCamera Version

3.3.1

Can you reproduce this issue in the VisionCamera Example app?

Yes, I can reproduce the same issue in the Example app here

Additional information

High5Apps avatar Oct 10 '23 09:10 High5Apps

Regarding the note below from my code in the Reproduceable Code section:

// Note that in order to get the 3.3.1 example app to build, I needed to comment out the line 289 in CameraView+AVCaptureSession.swift
// https://github.com/mrousavy/react-native-vision-camera/blob/a498144fd5786c6dba1d78730d362e0f675cbaf7/package/ios/CameraView%2BAVCaptureSession.swift#L289

I believe that this build failure is likely unrelated to this bug report, but for completeness, here's the error message:

 'NSInvalidArgumentException', reason: '*** -[AVCaptureFigVideoDevice setAutomaticallyEnablesLowLightBoostWhenAvailable:] Not supported - use -isLowLightBoostSupported'

High5Apps avatar Oct 10 '23 11:10 High5Apps

Lol this is an interesting issue

mrousavy avatar Oct 10 '23 11:10 mrousavy

I have a similar issue with 3.6.4 on an iPhone XS Max

Page Structure:

It's a flexbox layout - the header and controls are 75 pixels high and the camera is 100% with flexShrink = 1, so I expect it to occupy the full space between the header above and controls below:

image

Camera Function

image

Stylesheet

image

Result: the magenta background is the camera background

If I change the Camera Function as follows, wrapping the camera in two containers (AbsoluteFill is needed on the camera and the immediate parent):

image

The display is as expected:

davidredshaw avatar Nov 10 '23 17:11 davidredshaw

@davidredshaw I had the same issue and was able to fix it by wrapping the Camera component in a View with flex 1. Try changing it to

<View style={{flex: 1, backgroundColor: 'green`}}>
    <Camera style={{flex:1}} ... />
</View>

ClaireL-spindance avatar Apr 10 '24 14:04 ClaireL-spindance

Hey - I think this issue has been fixed in VisionCamera 4.0.0. 🥳

Please try V4 and let me know if you still experience this issue;

  • if not, please consider 💖 sponsoring me on GitHub 💖 to support the development of VisionCamera and thank me for my time spent on fixing bugs and building new features.
  • if you still see this issue, please comment and we can re-open this. But please update your native logs with the native (Xcode/Android Studio) logs from running VisionCamera V4 so I can investigate this.

mrousavy avatar Apr 22 '24 11:04 mrousavy

Hey Marc- congrats on the v4 release, and thanks for all of your great work on this library!

After upgrading my app from [email protected] to [email protected], I can confirm that the general issue of borders causing an offset camera preview remains, although the details of the issue and workaround have changed.

I would recommend reopening this issue.

On iOS with v4, the extra-view workaround mentioned above still works fine. Without the workaround, the preview offset still occurs (see screenshot 1 below).

On Android with v4, I'm now seeing similar offset preview issues (see screenshot 2 below). Recall that in v3 this seemed like an iOS-only issue. Interestingly, the offset on Android is in the opposite direction as on iOS. On iOS the camera preview is offset to the bottom right, but on Android it is offset to the top left (compare screenshots 1 and 2 below)

Furthermore, the extra-view workaround mentioned above wasn't sufficient on Android. Instead I now need to wait for the Camera to be initialized before setting the absoluteFill style.

I tried to see if the waiting-workaround would be enough to prevent the issue on iOS too, but it still needed the extra-view workaround. Fortunately, the new combined workaround seems to be working on both iOS and Android. I tested this on an iPhone 13 mini and a Samsung Galaxy S10.

New workaround for v4

function MyBorderedCamera() {
  const [cameraInitialized, setCameraInitialized] = useState(false);

  // ...

  return (
    <View style={{ borderWidth: 8, borderColor: 'purple', ... }}>
      {/* Add the View below as a workaround on iOS */}
      <View style={StyleSheet.absoluteFill}>
        <Camera
          // ...

          // Wait for the camera to be initialized as a workaround on Android
          onInitialized={() => setCameraInitialized(true)}
          style={cameraInitialized && StyleSheet.absoluteFill}
        />
      </View>
    </View>
  );
}

Screenshot 1: On iPhone 13 Mini running v4 (without the workaround)

i13m-rnvc-4 0 1-clip

Screenshot 2: On Samsung Galaxy S10 running v4 (without the workaround)

s10-rnvc-4 0 1-clip

High5Apps avatar Apr 23 '24 09:04 High5Apps