5

I want to record the screen with ReplayKit. I have researched the method with UIKit. But my project used SwiftUI, so I want to use the ReplayKit to record the screen with SwiftUI.

How I record the screen with SwiftUI?

-

When I use the stopRecording function, the function will have previewViewController. But I cannot call present function to present previewViewController.

Jerry Lee
  • 823
  • 2
  • 7
  • 13
  • 4
    Swift UI is a framewok to help building User Interfaces. It doesn't **do** anything else. If you want to use ReplayKit to record the screen then look up how to use ReplayKit, not SwiftUI. Look at https://developer.apple.com/documentation/replaykit/rpscreenrecorder *this* is what records your screen – Scriptable Jan 21 '20 at 14:00
  • okay. make sence. – Jerry Lee Jan 21 '20 at 14:05
  • When I use the stopRecording function, the function will have previewViewController. But I cannot call present function to present previewViewController. – Jerry Lee Jan 21 '20 at 14:45
  • 1
    See: https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit for how to use a UIKit UIViewController with SwiftUI – Scriptable Jan 21 '20 at 14:59

3 Answers3

2

Try This

import SwiftUI
import ReplayKit

struct ContentView: View {
    
    let recorder = RPScreenRecorder.shared()
    @State var isBool = false
    @State var rp: RPPreviewView!
    @State var isRecording = false
    @State var isShowPreviewVideo = false
    
    var body: some View {
        ZStack {
            VStack {
                Button(action: {
                    if !self.isRecording {
                        self.startRecord()
                    } else {
                        self.stopRecord()
                    }
                }) {
                    Image(systemName: isRecording ? "stop.circle" : "video.circle")
                        .resizable()
                        .frame(width: 100, height: 100)
                }
            }
            if isShowPreviewVideo {
                rp
                    .transition(.move(edge: .bottom))
                    .edgesIgnoringSafeArea(.all)
                
            }
        }
    }
    
     func startRecord() {
        guard recorder.isAvailable else {
            print("Recording is not available at this time.")
            return
        }
        if !recorder.isRecording {
            recorder.startRecording { (error) in
                guard error == nil else {
                    print("There was an error starting the recording.")
                    return
                }
                print("Started Recording Successfully")
                self.isRecording = true
            }
        }
    }
    
     func stopRecord() {
        recorder.stopRecording { (preview, error) in
            print("Stopped recording")
            self.isRecording = false

            guard let preview = preview else {
                print("Preview controller is not available.")
                return
            }
            self.rp = RPPreviewView(rpPreviewViewController: preview, isShow: self.$isShowPreviewVideo)
            withAnimation {
                self.isShowPreviewVideo = true
            }
        }
    }
}


struct RPPreviewView: UIViewControllerRepresentable {
    let rpPreviewViewController: RPPreviewViewController
    @Binding var isShow: Bool
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIViewController(context: Context) -> RPPreviewViewController {
        rpPreviewViewController.previewControllerDelegate = context.coordinator
        rpPreviewViewController.modalPresentationStyle = .fullScreen
        
        return rpPreviewViewController
    }
    
    func updateUIViewController(_ uiViewController: RPPreviewViewController, context: Context) { }
    
    class Coordinator: NSObject, RPPreviewViewControllerDelegate {
        var parent: RPPreviewView
           
        init(_ parent: RPPreviewView) {
            self.parent = parent
        }
           
        func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
            withAnimation {
                parent.isShow = false
            }
        }
    }
}
Saad
  • 8,857
  • 2
  • 41
  • 51
0

Note: This is not a very practical answer.

In most cases, "SwiftUI.View" runs on top of "UIHostingController". You need to grab this to present the "RPPreviewViewController". You can find one of them by following the "UIApplication".

let scene = UIApplication.shared.connectedScenes.first as! UIWindowScene
let viewController = scene.windows.last!.rootViewController
viewController.present(previewViewController, animated: true, completion:nil)
yajamon
  • 1
  • 1
0

I have just open-sourced a simple ReplayKit application, which uses SwiftUI.

https://github.com/snakajima/ReplayStartUpKit

Please take a look at how it presents a RPBroadcastActivityViewController from SwiftUI.

It first stores the pointer to the controller in a property bavController, then set @Pubilshed property activePopup to initiate SwiftUI change.

RPBroadcastActivityViewController.load { controller, error in
    if let controller = controller {
        self.bavController = controller
        controller.delegate = self
        self.activePopup = .broadCast
    }
}

In SwiftUI (MainUIView.swift), the following view is activated when the property activePopup becomes .broadCast.

.sheet(item: $state.activePopup) { item in
    switch(item) {
    case .broadCast:
        BroadcastActivityController(controller: state.bavController!)
    }
}

BroadcastActivityController is bit long because of a work-around for iPad, but it is just a wrapper of RPBroadcastActivityController.

struct BroadcastActivityController: UIViewControllerRepresentable {
    let controller: RPBroadcastActivityViewController
    func makeUIViewController(context: Context) -> RPBroadcastActivityViewController {
        return controller
    }
    
    func updateUIViewController(_ uiViewController: RPBroadcastActivityViewController, context: Context) {
        // Hack to work around iPad issue
        if UIDevice.current.userInterfaceIdiom == .pad {
            guard let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate,
                  let vc = sceneDelegate.uiWindow?.rootViewController,
                  let view = vc.view else {
                print("somethign is really wrong")
                return
            }
            controller.modalPresentationStyle = .popover
            if let popover = controller.popoverPresentationController {
                popover.sourceRect = CGRect(origin: .zero, size: CGSize(width: 10, height: 10))
                popover.sourceView = view
                popover.permittedArrowDirections = []
            }
        }
    }
    
    typealias UIViewControllerType = RPBroadcastActivityViewController
}

You need to do something very similar to previewViewController.

Satoshi Nakajima
  • 1,863
  • 18
  • 29