diff --git a/package/example/ios/Podfile.lock b/package/example/ios/Podfile.lock index dc65072d6d..6bceb811fd 100644 --- a/package/example/ios/Podfile.lock +++ b/package/example/ios/Podfile.lock @@ -1391,16 +1391,16 @@ PODS: - ReactCommon/turbomodule/core - Yoga - SocketRocket (0.7.0) - - VisionCamera (4.4.3): - - VisionCamera/Core (= 4.4.3) - - VisionCamera/FrameProcessors (= 4.4.3) - - VisionCamera/React (= 4.4.3) - - VisionCamera/Core (4.4.3) - - VisionCamera/FrameProcessors (4.4.3): + - VisionCamera (4.5.0): + - VisionCamera/Core (= 4.5.0) + - VisionCamera/FrameProcessors (= 4.5.0) + - VisionCamera/React (= 4.5.0) + - VisionCamera/Core (4.5.0) + - VisionCamera/FrameProcessors (4.5.0): - React - React-callinvoker - react-native-worklets-core - - VisionCamera/React (4.4.3): + - VisionCamera/React (4.5.0): - React-Core - VisionCamera/FrameProcessors - Yoga (0.0.0) @@ -1688,7 +1688,7 @@ SPEC CHECKSUMS: RNStaticSafeAreaInsets: 055ddbf5e476321720457cdaeec0ff2ba40ec1b8 RNVectorIcons: 2a2f79274248390b80684ea3c4400bd374a15c90 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - VisionCamera: 7435f20f7ee7756a1e307e986195a97764da5142 + VisionCamera: c75705e486d3ba2e0e850e98aa314010f04e922d Yoga: 2f71ecf38d934aecb366e686278102a51679c308 PODFILE CHECKSUM: 49584be049764895189f1f88ebc9769116621103 diff --git a/package/ios/Core/Types/CoordinateSystem.swift b/package/ios/Core/Types/CoordinateSystem.swift new file mode 100644 index 0000000000..0f41f12388 --- /dev/null +++ b/package/ios/Core/Types/CoordinateSystem.swift @@ -0,0 +1,26 @@ +// +// CoordinateSystem.swift +// VisionCamera +// +// Created by Marc Rousavy on 17.07.24. +// + +import Foundation + +@frozen +enum CoordinateSystem: String, JSUnionValue { + case previewView = "preview-view" + case camera = "camera" + + init(jsValue: String) throws { + if let parsed = CoordinateSystem(rawValue: jsValue) { + self = parsed + } else { + throw CameraError.parameter(.invalid(unionName: "hardwareLevel", receivedValue: jsValue)) + } + } + + var jsValue: String { + return rawValue + } +} diff --git a/package/ios/React/CameraView+Focus.swift b/package/ios/React/CameraView+Focus.swift index 29e2ea3cdb..0a7b47ba16 100644 --- a/package/ios/React/CameraView+Focus.swift +++ b/package/ios/React/CameraView+Focus.swift @@ -10,7 +10,7 @@ import AVFoundation import Foundation extension CameraView { - func focus(point: CGPoint, promise: Promise) { + func focus(pointInPreviewView point: CGPoint, promise: Promise) { withPromise(promise) { guard let previewView = self.previewView else { throw CameraError.capture(.focusRequiresPreview) @@ -20,4 +20,11 @@ extension CameraView { return nil } } + + func focus(pointInCameraCoordinates point: CGPoint, promise: Promise) { + withPromise(promise) { + try cameraSession.focus(point: point) + return nil + } + } } diff --git a/package/ios/React/CameraViewManager.swift b/package/ios/React/CameraViewManager.swift index f7bd9e8d0e..b7f81e89f4 100644 --- a/package/ios/React/CameraViewManager.swift +++ b/package/ios/React/CameraViewManager.swift @@ -86,14 +86,31 @@ final class CameraViewManager: RCTViewManager { } @objc - final func focus(_ node: NSNumber, point: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + final func focus(_ node: NSNumber, focusOptions: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { let promise = Promise(resolver: resolve, rejecter: reject) - guard let x = point["x"] as? NSNumber, let y = point["y"] as? NSNumber else { - promise.reject(error: .parameter(.invalid(unionName: "point", receivedValue: point.description))) + + guard let coordinateSystemString = focusOptions["coordinateSystem"] as? String, + let pointDictionary = focusOptions["point"] as? NSDictionary else { + promise.reject(error: .parameter(.invalid(unionName: "focusOptions", receivedValue: focusOptions.description))) + return + } + guard let x = pointDictionary["x"] as? NSNumber, let y = pointDictionary["y"] as? NSNumber else { + promise.reject(error: .parameter(.invalid(unionName: "focusOptions.point", receivedValue: pointDictionary.description))) + return + } + guard let coordinateSystem = try? CoordinateSystem(jsValue: coordinateSystemString) else { + promise.reject(error: .parameter(.invalid(unionName: "focusOptions.coordinateSystem", receivedValue: coordinateSystemString))) return } let component = getCameraView(withTag: node) - component.focus(point: CGPoint(x: x.doubleValue, y: y.doubleValue), promise: promise) + let point = CGPoint(x: x.doubleValue, y: y.doubleValue) + + switch coordinateSystem { + case .previewView: + component.focus(pointInPreviewView: point, promise: promise) + case .camera: + component.focus(pointInCameraCoordinates: point, promise: promise) + } } @objc diff --git a/package/src/Camera.tsx b/package/src/Camera.tsx index c896e1542b..ad695d0827 100644 --- a/package/src/Camera.tsx +++ b/package/src/Camera.tsx @@ -125,6 +125,11 @@ export class Camera extends React.PureComponent { return nodeHandle } + private get enablePreviewView(): boolean { + const isRenderingWithSkia = isSkiaFrameProcessor(this.props.frameProcessor) + return isRenderingWithSkia ? false : this.props.preview ?? true + } + //#region View-specific functions (UIViewManager) /** * Take a single photo and write it's content to a temporary file. @@ -385,7 +390,27 @@ export class Camera extends React.PureComponent { */ public async focus(point: Point): Promise { try { - return await CameraModule.focus(this.handle, point) + if (this.enablePreviewView) { + // Use Preview coordinate system + return await CameraModule.focus(this.handle, { + coordinateSystem: 'preview-view', + point: point, + }) + } else { + // Use Camera-native coordinate system + const format = this.props.format + if (format == null) { + // TODO: Move this to native so it isn't required. + throw new Error('Format is required to focus without a Preview View!') + } + return await CameraModule.focus(this.handle, { + coordinateSystem: 'camera', + point: { + x: point.x / format.videoWidth, + y: point.y / format.videoHeight, + }, + }) + } } catch (e) { throw tryParseNativeCameraError(e) } @@ -669,7 +694,7 @@ export class Camera extends React.PureComponent { codeScannerOptions={codeScanner} enableFrameProcessor={frameProcessor != null} enableBufferCompression={props.enableBufferCompression ?? shouldEnableBufferCompression} - preview={isRenderingWithSkia ? false : props.preview ?? true}> + preview={this.enablePreviewView}> {isRenderingWithSkia && (