Last active
September 12, 2023 16:42
-
-
Save jasoneveleth/4fa2faced568cb1dbd2da296e8f50e88 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// THIS DOESN'T WORK, see the final comment for fix | |
// | |
import SwiftUI | |
import AVFoundation | |
struct ContentView: View { | |
@StateObject private var cameraManager = CameraManager() | |
var body: some View { | |
ZStack { | |
if cameraManager.isSessionInitialized { | |
VideoPreviewView().edgesIgnoringSafeArea(.all) | |
} else { | |
Text("loading cam") | |
} | |
RecordButton() | |
} | |
.environmentObject(cameraManager) | |
} | |
} | |
class RecordingHandler: NSObject, AVCaptureFileOutputRecordingDelegate { | |
func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {} | |
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {} | |
} | |
class CameraManager: ObservableObject { | |
var captureSession = AVCaptureSession() | |
@Published var isSessionInitialized = false | |
private var movieOutput: AVCaptureMovieFileOutput? | |
private var recordingHandler: RecordingHandler | |
init() { | |
self.recordingHandler = RecordingHandler() | |
DispatchQueue.global(qos: .background).async { [weak self] in | |
guard let self = self else { return } | |
guard let camera = AVCaptureDevice.default(for: .video) else { return } | |
do { | |
let input = try AVCaptureDeviceInput(device: camera) | |
self.captureSession.addInput(input) | |
self.movieOutput = AVCaptureMovieFileOutput() | |
if self.captureSession.canAddOutput(self.movieOutput!) { | |
self.captureSession.addOutput(self.movieOutput!) | |
} | |
// Start running the capture session on the background queue | |
self.captureSession.startRunning() | |
DispatchQueue.main.async { | |
self.isSessionInitialized = true | |
} | |
} catch { | |
print(error.localizedDescription) | |
} | |
} | |
} | |
func startRecording() { | |
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] | |
let outputURL = documentsDirectory.appendingPathComponent(UUID().uuidString).appendingPathExtension("mov") | |
movieOutput?.startRecording(to: outputURL, recordingDelegate: recordingHandler) | |
} | |
func stopRecording() { | |
movieOutput?.stopRecording() | |
} | |
} | |
struct RecordButton: View { | |
@EnvironmentObject private var cameraManager: CameraManager | |
@State private var isRecording = false | |
var body: some View { | |
Button(action: {}) { | |
Image(systemName: "circle.fill") | |
.resizable() | |
.aspectRatio(contentMode: .fit) | |
.frame(width: 75, height: 75) | |
.foregroundColor(isRecording ? .white : .red) | |
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local) | |
.onChanged({ _ in | |
if !isRecording { | |
cameraManager.startRecording() | |
isRecording = true | |
} | |
}) | |
.onEnded({ _ in | |
isRecording = false | |
cameraManager.stopRecording() | |
}) | |
) | |
} | |
} | |
} | |
struct VideoPreviewView: UIViewRepresentable { | |
@EnvironmentObject var cameraManager: CameraManager | |
func makeUIView(context: Context) -> UIView { | |
let view = UIView() | |
let previewLayer = AVCaptureVideoPreviewLayer(session: cameraManager.captureSession) | |
previewLayer.frame = view.layer.bounds | |
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill | |
view.layer.addSublayer(previewLayer) | |
return view | |
} | |
func updateUIView(_ uiView: UIView, context: Context) {} | |
} |
The issue as @vadian pointed out is the bounds were zero since I the view I was taking the bounds from was just initialized. The correct code is a tiny change from my desired modifications. Use UIScreen.main.bounds
rather than view.layer.bounds
. In the initial code.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Another interpretation of that comment is this, which also still has a blank view (but does make the "now" print statement fire).
and