SwiftUI - Camera Frame messes up with Orientation - ios

I am a swift/swiftUI newbie and I have a CameraService that is used in CameraView which is also used in CustomCameraView where I change the orientation and there is a button for taking photos.
I also have a ContentView which is the main page of the app. There is a button for taking photos which opens up the CustomCameraView.
When the user changes the orientation of the device in CustomCameraView the CameraView still stays in the Portrait Orientation (I think). So this causes the camera to look like this:
I did try some updates but they did not work. To summarize the flow goes like CameraService -> CameraView -> CustomCameraView -> ContentView -> CameraApp
import Foundation
import AVFoundation
class CameraService {
var session: AVCaptureSession?
var delegate: AVCapturePhotoCaptureDelegate?
let output = AVCapturePhotoOutput()
let previewLayer = AVCaptureVideoPreviewLayer()
func start(delegate : AVCapturePhotoCaptureDelegate,
completion: #escaping (Error?) -> ()) {
self.delegate = delegate
checkPermission(completion: completion)
}
private func checkPermission(completion: #escaping (Error?) -> ()) {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
guard granted else { return }
DispatchQueue.main.async {
self?.setupCamera(completion: completion)
}
}
case .restricted:
break
case .denied:
break
case .authorized:
setupCamera(completion: completion)
#unknown default:
break
}
}
private func setupCamera(completion: #escaping (Error?) -> ()) {
let session = AVCaptureSession()
previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
if let device = AVCaptureDevice.default(for: .video){
do {
let input = try AVCaptureDeviceInput(device: device)
if session.canAddInput(input){
session.addInput(input)
}
if session.canAddOutput(output){
session.addOutput(output)
}
previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
previewLayer.videoGravity = .resizeAspectFill
previewLayer.session = session
session.startRunning()
self.session = session
} catch {
completion(error)
}
}
}
func capturePhoto(with settings: AVCapturePhotoSettings = AVCapturePhotoSettings()) {
output.capturePhoto(with: settings, delegate: delegate!)
}
func captureVideo(){
// TODO
}
func ViewUpdated(){
previewLayer.videoGravity = .resizeAspectFill
previewLayer.session = session
}
}
Camera View:
import SwiftUI
import AVFoundation
struct CameraView : UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator(self, didFinishProcessingPhoto: didFinishProcessingPhoto)
}
typealias UIViewControllerType = UIViewController
let cameraService : CameraService
let didFinishProcessingPhoto: (Result<AVCapturePhoto, Error>) -> ()
func makeUIViewController(context: Context) -> UIViewController {
cameraService.start(delegate: context.coordinator) { err in
if let err = err {
didFinishProcessingPhoto(.failure(err))
return
}
}
let viewController = UIViewController()
viewController.view.backgroundColor = .black
viewController.view.layer.addSublayer(cameraService.previewLayer)
cameraService.previewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.landscapeLeft
cameraService.previewLayer.frame = UIViewController().view.bounds
cameraService.previewLayer.videoGravity = .resizeAspectFill
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
cameraService.previewLayer.videoGravity = .resizeAspectFill
cameraService.previewLayer.frame = UIViewController().view.bounds
}
class Coordinator: NSObject, AVCapturePhotoCaptureDelegate {
let parent: CameraView
private var didFinishProcessingPhoto: (Result<AVCapturePhoto, Error>) -> ()
init(_ parent : CameraView,
didFinishProcessingPhoto: #escaping (Result<AVCapturePhoto, Error>) -> ())
{
self.parent = parent
self.didFinishProcessingPhoto = didFinishProcessingPhoto
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let error = error {
didFinishProcessingPhoto(.failure(error))
return
}
didFinishProcessingPhoto(.success(photo))
}
}
}
CustomCameraView:
import SwiftUI
struct CustomCameraView : View {
let cameraService = CameraService()
#Binding var capturedImage : UIImage?
#Environment(\.presentationMode) private var presentationMode
var body : some View{
ZStack {
CameraView(cameraService: cameraService) {result in
switch result {
case .success(let photo):
if let data = photo.fileDataRepresentation(){
capturedImage = UIImage(data: data)
presentationMode.wrappedValue.dismiss()
}
else {
print("Error: NO IMAGE DATA FOUND")
}
case .failure(let err):
print(err.localizedDescription)
}
}
VStack{
Image(systemName: "ellipsis.message").font(.system(size: 72))
Spacer()
Button(action: {
cameraService.capturePhoto()
}, label: {
Image(systemName: "circle").font(.system(size: 72))
.foregroundColor(.blue)
})
.padding(.bottom)
}
}.onAppear{
// Forcing the orientation
UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
// Locking Orientation
AppDelegate.orientationLock = .landscapeLeft
cameraService.ViewUpdated()
}.onDisappear{
// Unlocking Orientation
AppDelegate.orientationLock = .all
}
}
}
As you can see I have tried some orientation lock with the class below which is defined in app:
#UIApplicationDelegateAdaptor (AppDelegate.self) var appDelegate
// Added for locking/unlocking the orientation
class AppDelegate : NSObject, UIApplicationDelegate {
static var orientationLock = UIInterfaceOrientationMask.all // By default you want all your views to rotate freely
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window : UIWindow?) -> UIInterfaceOrientationMask {
return AppDelegate.orientationLock
}
}
The lock actually works for CustomCameraView(inherits View) but It doesn't do anything for CameraView(inherits UIViewControllerRepresentable). I have seen some answers to similar questions in UIKit but I don't use it. I need a SwiftUI answer specifically. Thanks in advance!

Related

Sheet and modal UIKit and SwiftUI [duplicate]

I have been trying to use EKEventEditViewController with SwiftUI. I tried to follow some suggestions from dev forums as to how to go about adding it with the help of a UIViewControllerRepresentable wrapper with a Coordinator. I am adding the code below.
The EKEventEditViewController is presented correctly but the problem I'm facing is that only some of the fields are editable. I'm attaching a gif showing the interactions.
Has anyone faced this issue ?
Here is the code:
import Foundation
import SwiftUI
import EventKitUI
let eventStore = EKEventStore()
struct NewEventGenerator: UIViewControllerRepresentable {
typealias UIViewControllerType = EKEventEditViewController
#Binding var isShowing: Bool
var theEvent = EKEvent.init(eventStore: eventStore)
func makeUIViewController(context: UIViewControllerRepresentableContext<NewEventGenerator>) -> EKEventEditViewController {
let controller = EKEventEditViewController()
controller.event = theEvent
controller.eventStore = eventStore
controller.editViewDelegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: NewEventGenerator.UIViewControllerType, context: UIViewControllerRepresentableContext<NewEventGenerator>) {
uiViewController.view.backgroundColor = .red
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing)
}
class Coordinator : NSObject, UINavigationControllerDelegate, EKEventEditViewDelegate {
#Binding var isVisible: Bool
init(isShowing: Binding<Bool>) {
_isVisible = isShowing
}
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
switch action {
case .canceled:
isVisible = false
case .saved:
do {
try controller.eventStore.save(controller.event!, span: .thisEvent, commit: true)
}
catch {
print("Event couldn't be created")
}
isVisible = false
case .deleted:
isVisible = false
#unknown default:
isVisible = false
}
}
}}
Works fine with Xcode 12 / iOS 14. Literally copy-pasted your code added requestAccess & descriptions in Info.plist.
Full tested module, for the case if something might be helpful.
import SwiftUI
import EventKitUI
let eventStore = EKEventStore()
struct NewEventGenerator: UIViewControllerRepresentable {
typealias UIViewControllerType = EKEventEditViewController
#Binding var isShowing: Bool
var theEvent: EKEvent
init(isShowing: Binding<Bool>) {
eventStore.requestAccess(to: .event) { allow, error in
print("Result: \(allow) or [\(error.debugDescription)]")
}
theEvent = EKEvent.init(eventStore: eventStore)
_isShowing = isShowing
}
func makeUIViewController(context: UIViewControllerRepresentableContext<NewEventGenerator>) -> EKEventEditViewController {
let controller = EKEventEditViewController()
controller.event = theEvent
controller.eventStore = eventStore
controller.editViewDelegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: NewEventGenerator.UIViewControllerType, context: UIViewControllerRepresentableContext<NewEventGenerator>) {
uiViewController.view.backgroundColor = .red
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing)
}
class Coordinator : NSObject, UINavigationControllerDelegate, EKEventEditViewDelegate {
#Binding var isVisible: Bool
init(isShowing: Binding<Bool>) {
_isVisible = isShowing
}
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
switch action {
case .canceled:
isVisible = false
case .saved:
do {
try controller.eventStore.save(controller.event!, span: .thisEvent, commit: true)
}
catch {
print("Event couldn't be created")
}
isVisible = false
case .deleted:
isVisible = false
#unknown default:
isVisible = false
}
}
}}
struct TestEventKitViewInSheet: View { // just created in ContentView body
#State private var showIt = false
var body: some View {
Button("Events") { showIt = true }
.sheet(isPresented: $showIt) {
NewEventGenerator(isShowing: $showIt)
}
}
}

SwiftUI - AdMob Interstitial ad crashes only on iOS 16

I have a view in which there is an interstitial ad that gets presented. On iOS 15 everything works fine, but on iOS 16 the app crashes with the following error:
SwiftUI/UIViewControllerRepresentable.swift:332: Fatal error:
UIViewControllerRepresentables must be value types: InterstitialAdView
2022-09-22 09:33:06.740743+0200 HowRich[47572:2135353]
SwiftUI/UIViewControllerRepresentable.swift:332: Fatal error:
UIViewControllerRepresentables must be value types: InterstitialAdView
(lldb)
And where there's the #main I get this error:
The code is the following:
InterstitialAdsManager.swift
import GoogleMobileAds
import SwiftUI
import UIKit
class InterstitialAd: NSObject {
var interstitialAd: GADInterstitialAd?
static let shared = InterstitialAd()
func loadAd(withAdUnitId id: String) {
let req = GADRequest()
GADInterstitialAd.load(withAdUnitID: id, request: req) { interstitialAd, err in
if let err = err {
print("Failed to load ad with error: \(err)")
return
}
self.interstitialAd = interstitialAd
}
}
}
final class InterstitialAdView: NSObject, UIViewControllerRepresentable, GADFullScreenContentDelegate {
let interstitialAd = InterstitialAd.shared.interstitialAd
#Binding var isPresented: Bool
var adUnitId: String
init(isPresented: Binding<Bool>, adUnitId: String) {
self._isPresented = isPresented
self.adUnitId = adUnitId
super.init()
interstitialAd?.fullScreenContentDelegate = self
}
func makeUIViewController(context: Context) -> UIViewController {
let view = UIViewController()
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1)) {
self.showAd(from: view)
}
return view
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
func showAd(from root: UIViewController) {
if let ad = interstitialAd {
ad.present(fromRootViewController: root)
} else {
print("Ad not ready")
self.isPresented.toggle()
}
}
func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
InterstitialAd.shared.loadAd(withAdUnitId: adUnitId)
isPresented.toggle()
}
}
struct FullScreenModifier<Parent: View>: View {
#Binding var isPresented: Bool
var adUnitId: String
var parent: Parent
var body: some View {
ZStack {
parent
if isPresented {
EmptyView()
.edgesIgnoringSafeArea(.all)
InterstitialAdView(isPresented: $isPresented, adUnitId: adUnitId)
}
}
.onAppear {
InterstitialAd.shared.loadAd(withAdUnitId: adUnitId)
}
}
}
extension View {
public func presentInterstitialAd(isPresented: Binding<Bool>, adUnitId: String) -> some View {
FullScreenModifier(isPresented: isPresented, adUnitId: adUnitId, parent: self)
}
}
The View with the ad:
struct CheckoutView: View {
#State var showAd = false
var body: some View {
ScrollView {
ZStack {
VStack {
//The view
}
.onAppear {
if UserDefaults.standard.string(forKey: "AdCounter") == "0" && UserDefaults.standard.bool(forKey: "AdFree") == false {
showAd = true
UserDefaults.standard.setValue("1", forKey: "AdCounter")
}
UserDefaults.standard.setValue("0", forKey: "AdCounter")
}
.presentInterstitialAd(isPresented: $showAd, adUnitId: myIntersId)
}
}
}
I have updated all pods to the latest available versions (Google Mobile Ads SDK is version 9.11.0).
What's causing the crash on iOS 16 and how can I fix it? Thanks
#State var interstitial = InterstitialAd()
interstitial.showAd()
///InterstitialAd.swift
import GoogleMobileAds
class InterstitialAd: NSObject, GADFullScreenContentDelegate {
var interstitialAd: GADInterstitialAd?
var unitId: String = "your interstitial unitId"
override init() {
super.init()
loadAd()
}
func loadAd() {
let req = GADRequest()
req.scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
GADInterstitialAd.load(withAdUnitID: unitId, request: req) { [self] interstitialAd, err in
if let err = err {
print("Failed to load ad with error: \(err)")
}
self.interstitialAd = interstitialAd
self.interstitialAd?.fullScreenContentDelegate = self
}
}
//Presents the ad if it can, otherwise dismisses so the user's experience is not interrupted
func showAd() {
if let ad = interstitialAd, let root = UIApplication.shared.windows.first?.rootViewController {
ad.present(fromRootViewController: root)
} else {
print("Ad not ready")
self.loadAd()
}
}
func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
//Prepares another ad for the next time view presented
self.loadAd()
print("Ad Closed")
}
func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
print("Ad Error")
self.loadAd()
}
}
The solution is to change
final class InterstitialAdView: NSObject, UIViewControllerRepresentable, GADFullScreenContentDelegate {
into
struct InterstitialAdView: NSObject, UIViewControllerRepresentable, GADFullScreenContentDelegate {

SwiftUI EKEventEditViewController fields not editable

I have been trying to use EKEventEditViewController with SwiftUI. I tried to follow some suggestions from dev forums as to how to go about adding it with the help of a UIViewControllerRepresentable wrapper with a Coordinator. I am adding the code below.
The EKEventEditViewController is presented correctly but the problem I'm facing is that only some of the fields are editable. I'm attaching a gif showing the interactions.
Has anyone faced this issue ?
Here is the code:
import Foundation
import SwiftUI
import EventKitUI
let eventStore = EKEventStore()
struct NewEventGenerator: UIViewControllerRepresentable {
typealias UIViewControllerType = EKEventEditViewController
#Binding var isShowing: Bool
var theEvent = EKEvent.init(eventStore: eventStore)
func makeUIViewController(context: UIViewControllerRepresentableContext<NewEventGenerator>) -> EKEventEditViewController {
let controller = EKEventEditViewController()
controller.event = theEvent
controller.eventStore = eventStore
controller.editViewDelegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: NewEventGenerator.UIViewControllerType, context: UIViewControllerRepresentableContext<NewEventGenerator>) {
uiViewController.view.backgroundColor = .red
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing)
}
class Coordinator : NSObject, UINavigationControllerDelegate, EKEventEditViewDelegate {
#Binding var isVisible: Bool
init(isShowing: Binding<Bool>) {
_isVisible = isShowing
}
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
switch action {
case .canceled:
isVisible = false
case .saved:
do {
try controller.eventStore.save(controller.event!, span: .thisEvent, commit: true)
}
catch {
print("Event couldn't be created")
}
isVisible = false
case .deleted:
isVisible = false
#unknown default:
isVisible = false
}
}
}}
Works fine with Xcode 12 / iOS 14. Literally copy-pasted your code added requestAccess & descriptions in Info.plist.
Full tested module, for the case if something might be helpful.
import SwiftUI
import EventKitUI
let eventStore = EKEventStore()
struct NewEventGenerator: UIViewControllerRepresentable {
typealias UIViewControllerType = EKEventEditViewController
#Binding var isShowing: Bool
var theEvent: EKEvent
init(isShowing: Binding<Bool>) {
eventStore.requestAccess(to: .event) { allow, error in
print("Result: \(allow) or [\(error.debugDescription)]")
}
theEvent = EKEvent.init(eventStore: eventStore)
_isShowing = isShowing
}
func makeUIViewController(context: UIViewControllerRepresentableContext<NewEventGenerator>) -> EKEventEditViewController {
let controller = EKEventEditViewController()
controller.event = theEvent
controller.eventStore = eventStore
controller.editViewDelegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: NewEventGenerator.UIViewControllerType, context: UIViewControllerRepresentableContext<NewEventGenerator>) {
uiViewController.view.backgroundColor = .red
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing)
}
class Coordinator : NSObject, UINavigationControllerDelegate, EKEventEditViewDelegate {
#Binding var isVisible: Bool
init(isShowing: Binding<Bool>) {
_isVisible = isShowing
}
func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
switch action {
case .canceled:
isVisible = false
case .saved:
do {
try controller.eventStore.save(controller.event!, span: .thisEvent, commit: true)
}
catch {
print("Event couldn't be created")
}
isVisible = false
case .deleted:
isVisible = false
#unknown default:
isVisible = false
}
}
}}
struct TestEventKitViewInSheet: View { // just created in ContentView body
#State private var showIt = false
var body: some View {
Button("Events") { showIt = true }
.sheet(isPresented: $showIt) {
NewEventGenerator(isShowing: $showIt)
}
}
}

Scanning barcodes one at a time in SwiftUI

I've used this guide to create my scanner:
I first run QRCodeScan() from content view with an overlay:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
QRCodeScan()
ScannerOverlayView()
}
}
}
QRCodeScan:
import SwiftUI
struct QRCodeScan: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> ScannerViewController {
let vc = ScannerViewController()
vc.delegate = context.coordinator
return vc
}
func updateUIViewController(_ vc: ScannerViewController, context: Context) {
}
class Coordinator: NSObject, QRCodeScannerDelegate {
#State var barcode = ""
func codeDidFind(_ code: String) {
barcode = code
FoundItemSheet(barcode: self.$barcode) //This creates a sheet at the bottom of the screen, saying if a product was found with the barcode
}
var parent: QRCodeScan
init(_ parent: QRCodeScan) {
self.parent = parent
}
}
}
ScannerViewController:
protocol QRCodeScannerDelegate {
func codeDidFind(_ code: String)
}
import AVFoundation
import UIKit
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
var delegate: QRCodeScannerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black
captureSession = AVCaptureSession()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
let videoInput: AVCaptureDeviceInput
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
} catch {
return
}
if (captureSession.canAddInput(videoInput)) {
captureSession.addInput(videoInput)
} else {
failed()
return
}
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.ean13, .ean8, .qr]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(previewLayer)
captureSession.startRunning()
}
func failed() {
let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
captureSession = nil
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if (captureSession?.isRunning == false) {
captureSession.startRunning()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.isRunning == true) {
captureSession.stopRunning()
}
}
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: stringValue)
}
dismiss(animated: true)
}
func found(code: String) {
self.delegate?.codeDidFind(code)
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}
It scans a barcode successfully, but that picture remains on the screen. I want a barcode to be scanned; the code be passed do FoundItemSheet(), then open the scanner again.
I've tried running QRCodeScan() inside of codeDidFind() and calling the ScannerViewController again.

ReplayKit with SwiftUI

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.
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)
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.
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
}
}
}
}

Resources