feat: Custom Orientation (#715)
* feat: Custom Orientation * Update CameraView.swift * Update CameraView.swift * Try outputRotation approach * whoops * fix: Refactor `VideoCapture` instance * Update orientation in didSetProps * Update Orientation in iOS * expose to objc * Fix Orientation values * format
This commit is contained in:
61
ios/CameraView+Orientation.swift
Normal file
61
ios/CameraView+Orientation.swift
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// CameraView+Orientation.swift
|
||||
// VisionCamera
|
||||
//
|
||||
// Created by Marc Rousavy on 04.01.22.
|
||||
// Copyright © 2022 mrousavy. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension CameraView {
|
||||
/// Returns the current _interface_ orientation of the main window
|
||||
private var windowInterfaceOrientation: UIInterfaceOrientation {
|
||||
if #available(iOS 13.0, *) {
|
||||
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation ?? .unknown
|
||||
} else {
|
||||
return UIApplication.shared.statusBarOrientation
|
||||
}
|
||||
}
|
||||
|
||||
/// Orientation of the input connection (preview)
|
||||
private var inputOrientation: UIInterfaceOrientation {
|
||||
return windowInterfaceOrientation
|
||||
}
|
||||
|
||||
// Orientation of the output connections (photo, video, frame processor)
|
||||
private var outputOrientation: UIInterfaceOrientation {
|
||||
if let userOrientation = orientation as String?,
|
||||
let parsedOrientation = try? UIInterfaceOrientation(withString: userOrientation) {
|
||||
// user is overriding output orientation
|
||||
return parsedOrientation
|
||||
} else {
|
||||
// use same as input orientation
|
||||
return inputOrientation
|
||||
}
|
||||
}
|
||||
|
||||
internal func updateOrientation() {
|
||||
// Updates the Orientation for all rotable connections (outputs) as well as for the preview layer
|
||||
DispatchQueue.main.async {
|
||||
// `windowInterfaceOrientation` and `videoPreviewLayer` should only be accessed from UI thread
|
||||
let isMirrored = self.videoDeviceInput?.device.position == .front
|
||||
|
||||
self.videoPreviewLayer.connection?.setInterfaceOrientation(self.inputOrientation)
|
||||
|
||||
self.cameraQueue.async {
|
||||
// Run those updates on cameraQueue since they can be blocking.
|
||||
self.captureSession.outputs.forEach { output in
|
||||
output.connections.forEach { connection in
|
||||
if connection.isVideoMirroringSupported {
|
||||
connection.automaticallyAdjustsVideoMirroring = false
|
||||
connection.isVideoMirrored = isMirrored
|
||||
}
|
||||
connection.setInterfaceOrientation(self.outputOrientation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -56,6 +56,7 @@ public final class CameraView: UIView {
|
||||
@objc var hdr: NSNumber? // nullable bool
|
||||
@objc var lowLightBoost: NSNumber? // nullable bool
|
||||
@objc var colorSpace: NSString?
|
||||
@objc var orientation: NSString?
|
||||
// other props
|
||||
@objc var isActive = false
|
||||
@objc var torch = "off"
|
||||
@@ -116,15 +117,6 @@ public final class CameraView: UIView {
|
||||
return captureSession.isRunning
|
||||
}
|
||||
|
||||
/// Returns the current _interface_ orientation of the main window
|
||||
private var windowInterfaceOrientation: UIInterfaceOrientation {
|
||||
if #available(iOS 13.0, *) {
|
||||
return UIApplication.shared.windows.first?.windowScene?.interfaceOrientation ?? .unknown
|
||||
} else {
|
||||
return UIApplication.shared.statusBarOrientation
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience wrapper to get layer as its statically known type.
|
||||
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
|
||||
// swiftlint:disable force_cast
|
||||
@@ -205,6 +197,7 @@ public final class CameraView: UIView {
|
||||
let shouldUpdateTorch = willReconfigure || changedProps.contains("torch") || shouldCheckActive
|
||||
let shouldUpdateZoom = willReconfigure || changedProps.contains("zoom") || shouldCheckActive
|
||||
let shouldUpdateVideoStabilization = willReconfigure || changedProps.contains("videoStabilizationMode")
|
||||
let shouldUpdateOrientation = changedProps.contains("orientation")
|
||||
|
||||
if shouldReconfigure ||
|
||||
shouldReconfigureAudioSession ||
|
||||
@@ -213,7 +206,8 @@ public final class CameraView: UIView {
|
||||
shouldUpdateZoom ||
|
||||
shouldReconfigureFormat ||
|
||||
shouldReconfigureDevice ||
|
||||
shouldUpdateVideoStabilization {
|
||||
shouldUpdateVideoStabilization ||
|
||||
shouldUpdateOrientation {
|
||||
cameraQueue.async {
|
||||
if shouldReconfigure {
|
||||
self.configureCaptureSession()
|
||||
@@ -246,6 +240,10 @@ public final class CameraView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
if shouldUpdateOrientation {
|
||||
self.updateOrientation()
|
||||
}
|
||||
|
||||
// This is a wack workaround, but if I immediately set torch mode after `startRunning()`, the session isn't quite ready yet and will ignore torch.
|
||||
if shouldUpdateTorch {
|
||||
self.cameraQueue.asyncAfter(deadline: .now() + 0.1) {
|
||||
@@ -316,27 +314,7 @@ public final class CameraView: UIView {
|
||||
|
||||
@objc
|
||||
func onOrientationChanged() {
|
||||
// Updates the Orientation for all rotable connections (outputs) as well as for the preview layer
|
||||
DispatchQueue.main.async {
|
||||
// `windowInterfaceOrientation` and `videoPreviewLayer` should only be accessed from UI thread
|
||||
let isMirrored = self.videoDeviceInput?.device.position == .front
|
||||
let orientation = self.windowInterfaceOrientation
|
||||
|
||||
self.videoPreviewLayer.connection?.setInterfaceOrientation(orientation)
|
||||
|
||||
self.cameraQueue.async {
|
||||
// Run those updates on cameraQueue since they can be blocking.
|
||||
self.captureSession.outputs.forEach { output in
|
||||
output.connections.forEach { connection in
|
||||
if connection.isVideoMirroringSupported {
|
||||
connection.automaticallyAdjustsVideoMirroring = false
|
||||
connection.isVideoMirrored = isMirrored
|
||||
}
|
||||
connection.setInterfaceOrientation(orientation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateOrientation()
|
||||
}
|
||||
|
||||
// pragma MARK: Event Invokers
|
||||
|
@@ -45,6 +45,7 @@ RCT_EXPORT_VIEW_PROPERTY(preset, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(torch, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(zoom, NSNumber);
|
||||
RCT_EXPORT_VIEW_PROPERTY(enableZoomGesture, BOOL);
|
||||
RCT_EXPORT_VIEW_PROPERTY(orientation, NSString);
|
||||
// Camera View Events
|
||||
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onInitialized, RCTDirectEventBlock);
|
||||
|
31
ios/Parsers/UIInterfaceOrientation+descriptor.swift
Normal file
31
ios/Parsers/UIInterfaceOrientation+descriptor.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// UIInterfaceOrientation+descriptor.swift
|
||||
// VisionCamera
|
||||
//
|
||||
// Created by Marc Rousavy on 04.01.22.
|
||||
// Copyright © 2022 mrousavy. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension UIInterfaceOrientation {
|
||||
init(withString string: String) throws {
|
||||
switch string {
|
||||
case "portrait":
|
||||
self = .portrait
|
||||
return
|
||||
case "portraitUpsideDown":
|
||||
self = .portraitUpsideDown
|
||||
return
|
||||
case "landscapeLeft":
|
||||
self = .landscapeLeft
|
||||
return
|
||||
case "landscapeRight":
|
||||
self = .landscapeRight
|
||||
return
|
||||
default:
|
||||
throw EnumParserError.invalidValue
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,6 +14,8 @@
|
||||
B82FBA962614B69D00909718 /* RCTBridge+runOnJS.mm in Sources */ = {isa = PBXBuildFile; fileRef = B82FBA952614B69D00909718 /* RCTBridge+runOnJS.mm */; };
|
||||
B84760A62608EE7C004C3180 /* FrameHostObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = B84760A52608EE7C004C3180 /* FrameHostObject.mm */; };
|
||||
B84760DF2608F57D004C3180 /* CameraQueues.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84760DE2608F57D004C3180 /* CameraQueues.swift */; };
|
||||
B864005027849A2400E9D2CA /* UIInterfaceOrientation+descriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B864004F27849A2400E9D2CA /* UIInterfaceOrientation+descriptor.swift */; };
|
||||
B86400522784A23400E9D2CA /* CameraView+Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86400512784A23400E9D2CA /* CameraView+Orientation.swift */; };
|
||||
B86DC971260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86DC970260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift */; };
|
||||
B86DC974260E310600FB17B2 /* CameraView+AVAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86DC973260E310600FB17B2 /* CameraView+AVAudioSession.swift */; };
|
||||
B86DC977260E315100FB17B2 /* CameraView+AVCaptureSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86DC976260E315100FB17B2 /* CameraView+AVCaptureSession.swift */; };
|
||||
@@ -93,6 +95,8 @@
|
||||
B84760A52608EE7C004C3180 /* FrameHostObject.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FrameHostObject.mm; sourceTree = "<group>"; };
|
||||
B84760DE2608F57D004C3180 /* CameraQueues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraQueues.swift; sourceTree = "<group>"; };
|
||||
B84C10592694A182006EFA70 /* MakeJSIRuntime.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MakeJSIRuntime.h; sourceTree = "<group>"; };
|
||||
B864004F27849A2400E9D2CA /* UIInterfaceOrientation+descriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIInterfaceOrientation+descriptor.swift"; sourceTree = "<group>"; };
|
||||
B86400512784A23400E9D2CA /* CameraView+Orientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+Orientation.swift"; sourceTree = "<group>"; };
|
||||
B86DC970260E2D5200FB17B2 /* AVAudioSession+trySetAllowHaptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAudioSession+trySetAllowHaptics.swift"; sourceTree = "<group>"; };
|
||||
B86DC973260E310600FB17B2 /* CameraView+AVAudioSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+AVAudioSession.swift"; sourceTree = "<group>"; };
|
||||
B86DC976260E315100FB17B2 /* CameraView+AVCaptureSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CameraView+AVCaptureSession.swift"; sourceTree = "<group>"; };
|
||||
@@ -180,6 +184,7 @@
|
||||
B887515D25E0102000DB86D6 /* CameraView+RecordVideo.swift */,
|
||||
B887517125E0102000DB86D6 /* CameraView+TakePhoto.swift */,
|
||||
B887518225E0102000DB86D6 /* CameraView+Zoom.swift */,
|
||||
B86400512784A23400E9D2CA /* CameraView+Orientation.swift */,
|
||||
B887515F25E0102000DB86D6 /* CameraViewManager.m */,
|
||||
B887518125E0102000DB86D6 /* CameraViewManager.swift */,
|
||||
B8DB3BC9263DC4D8004C18D7 /* RecordingSession.swift */,
|
||||
@@ -248,6 +253,7 @@
|
||||
B887517E25E0102000DB86D6 /* AVCaptureDevice.FlashMode+descriptor.swift */,
|
||||
B887517F25E0102000DB86D6 /* AVCaptureDevice.Format.AutoFocusSystem+descriptor.swift */,
|
||||
B8DB3BCB263DC97E004C18D7 /* AVFileType+descriptor.swift */,
|
||||
B864004F27849A2400E9D2CA /* UIInterfaceOrientation+descriptor.swift */,
|
||||
);
|
||||
path = Parsers;
|
||||
sourceTree = "<group>";
|
||||
@@ -391,12 +397,14 @@
|
||||
B80E06A0266632F000728644 /* AVAudioSession+updateCategory.swift in Sources */,
|
||||
B887519425E0102000DB86D6 /* MakeReactError.swift in Sources */,
|
||||
B887519525E0102000DB86D6 /* ReactLogger.swift in Sources */,
|
||||
B86400522784A23400E9D2CA /* CameraView+Orientation.swift in Sources */,
|
||||
B887519B25E0102000DB86D6 /* AVCaptureSession.Preset+descriptor.swift in Sources */,
|
||||
B88751A725E0102000DB86D6 /* CameraView+Zoom.swift in Sources */,
|
||||
B887518525E0102000DB86D6 /* PhotoCaptureDelegate.swift in Sources */,
|
||||
B887518B25E0102000DB86D6 /* AVCaptureDevice.Format+isBetterThan.swift in Sources */,
|
||||
B8BD3BA2266E22D2006C80A2 /* Callback.swift in Sources */,
|
||||
B84760A62608EE7C004C3180 /* FrameHostObject.mm in Sources */,
|
||||
B864005027849A2400E9D2CA /* UIInterfaceOrientation+descriptor.swift in Sources */,
|
||||
B8103E1C25FF553B007A1684 /* FrameProcessorUtils.mm in Sources */,
|
||||
B887518E25E0102000DB86D6 /* AVFrameRateRange+includes.swift in Sources */,
|
||||
B88751A125E0102000DB86D6 /* AVCaptureDevice.Position+descriptor.swift in Sources */,
|
||||
|
Reference in New Issue
Block a user