How to crop AVCaptureScreenInput to a swiftui view frame?

Viewed 108

I want to use AVCaptureScreenInput to capture a part of the screen in a SwiftUI App. Specifically, I want to only capture the area defined by a View in my app. I tried reading the view position with GeometryReader and translate that frame to screen coordinates using NSWindow.convertToScreen(_) then set AVCaptureScreenInput.cropRect to that CGRect but the captured area is always wrong. What's the correct way to get the cropRect that matches a SwiftUI view?

struct RecorderView: View {
    let window: NSWindow
    let input: AVCaptureScreenInput
    
    var body: some View {
        GeometryReader { geometry -> AnyView  in
            let previewFrame = geometry.frame(in: .global)
            let screenFrame = self.window.convertToScreen(previewFrame)
            self.input.cropRect = screenFrame
            return AnyView(Spacer().frame(width: 200, height: 200, alignment: .center))
        }
    }
}
1 Answers

Converting works correctly (of course if window is correct there), so the important part is how to read a view rect and pass it to recording engine.

The proposed approach is to use view modifier with preference key.

demo

Here is demo part (tested with Xcode 13.3 / macOS 12.2)

VStack {
    HStack {
        // !!! view area to record !!!
        DemoView()
            .gettingInWindow(rect: $viewRect)   // << detect here !!

        Spacer()
    }
    Button(recording ? "Stop" : "Start") {
        if !recording && !validate(destinationURL) {
            return
        }
        recording.toggle()
    }
}
.onChange(of: recording) {
    if $0 {
        // here transfer viewRect, which is window coordinates, to recorder !!
        recording = recorder.startRecording(to: destinationURL, in: viewRect)
    } else {
        recorder.stop()
    }
}

Complete findings & code is here

Related