How to start a ReplayKit screen recording in a SpriteKit SKScene class

I implemented ReplayKit in my SpriteKit game, but since everything is done in GameViewController , the record button appears too soon. See below my GameViewController class:

 class GameViewController: UIViewController, RPPreviewViewControllerDelegate { var videoRecButton: UIButton! var videoRecImage: UIImage! override func viewDidLoad() { super.viewDidLoad() let skView = self.view as? SKView if skView?.scene == nil { skView?.showsFPS = true skView?.showsNodeCount = true skView?.showsPhysics = true skView?.ignoresSiblingOrder = false //starting the game with the Poster Scene let posterScene = PosterScene(size: skView!.bounds.size) posterScene.scaleMode = .aspectFill skView?.presentScene(posterScene) } videoRecButton = UIButton(type: .custom) videoRecImage = UIImage(named:"videoRecButton.png") videoRecButton.frame = CGRect(x:0, y: 0, width: (videoRecImage?.size.width)!, height: (videoRecImage?.size.height)!) videoRecButton.setImage(videoRecImage, for: .normal) videoRecButton.addTarget(self, action:#selector(self.videoRecButtonClicked), for: .touchUpInside) self.view.addSubview(videoRecButton) } func videoRecButtonClicked() { print("Button Clicked") startRecording() } func startRecording() { let recorder = RPScreenRecorder.shared() recorder.startRecording{ [unowned self] (error) in if let unwrappedError = error { print(unwrappedError.localizedDescription) } else { self.videoRecButton.addTarget(self, action:#selector(self.stopRecording), for: .touchUpInside) } } } func stopRecording() { let recorder = RPScreenRecorder.shared() recorder.stopRecording { [unowned self] (preview, error) in self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Start", style: .plain, target: self, action: #selector(self.startRecording)) if let unwrappedPreview = preview { unwrappedPreview.previewControllerDelegate = self self.present(unwrappedPreview, animated: true) } } } func previewControllerDidFinish(_ previewController: RPPreviewViewController) { dismiss(animated: true) } override var shouldAutorotate: Bool { return true } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { if UIDevice.current.userInterfaceIdiom == .phone { return .allButUpsideDown } else { return .all } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Release any cached data, images, etc that aren't in use. } override var prefersStatusBarHidden: Bool { return true } } 
  • How can I call the startRecording and stopRecording from a class that inherits from SKScene as a GameScene class?

  • How to enable, disable and hide the videoRecButton button from the GameScene class?

UPDATE

Based on the response from crashoverride777 put the following code in my SKScene class, but the screen a few seconds before the navigation controller appears with a preview of the recorded video. The recorded video is only a black screen, and the cancel and save buttons do not respond.

  func startRecording() { let recorder = RPScreenRecorder.shared() if #available(iOS 10.0, *) { recorder.startRecording{ [unowned self] (error) in if let unwrappedError = error { print(unwrappedError.localizedDescription) } else { self.stopRecording() } } } else { // Fallback on earlier versions } } func stopRecording() { let recorder = RPScreenRecorder.shared() recorder.stopRecording { [unowned self] (preview, error) in self.view?.window?.rootViewController?.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Start", style: .plain, target: self, action: #selector(self.startRecording)) if let unwrappedPreview = preview { unwrappedPreview.previewControllerDelegate = self self.view?.window?.rootViewController?.present(unwrappedPreview, animated: true) } } } func previewControllerDidFinish(_ previewController: RPPreviewViewController) { view?.window?.rootViewController?.dismiss(animated: true) } 

I created a record button:

 let videoRecButtonSprite = SKSpriteNode(imageNamed: "videoButton") videoRecButtonSprite.position = CGPoint(x: self.frame.width/15, y: self.frame.height - self.frame.height/12) self.addChild(videoRecButtonSprite) override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { for touch: AnyObject in touches { let location = touch.location(in: self) if videoRecButtonSprite.contains(location){ startRecording() } } } 
+5
source share
2 answers

You do not have to create your button in the GameViewController, create it directly in your SKScene. It's not a good practice to use GameViewController for the user interface in SpriteKit games.

SpriteKit has many button tutorials.

As for ReplayKit, you can use it directly in the SKScene you want, just grab the code you already have and move it to the corresponding scene.

To represent the view controller in SKScene, you can say this

 view?.window?.rootViewController?.present(unwrappedPreview, animated: true) 

I also noticed that you are presenting a View controller after stopping recording. Are you sure you want to do this? Usually you have a separate menu button in your game where you can watch the recording.

Here is the generic code. I also recommend that you check the apples DemoBots game example.

I personally use the Singleton class to write managers, so its a lighter manager that calls all the methods you need for different scenes. To start class masonry, enter a new quick file and add this code.

  class ScreenRecoder: NSObject { /// Shared instance static let shared = ScreenRecorder() /// Preview controller var previewController: RPPreviewViewController? /// Private singleton init private override init() { } } 

To start recording, add this method to the ScreenRecorder class.

 func start() { let sharedRecorder = RPScreenRecorder.shared() // Do nothing if screen recording is not available guard sharedRecorder.isAvailable else { return } // Stop previous recording if necessary if sharedRecorder.isRecording { stopScreenRecording() } print("Starting screen recording") // Register as the recorder delegate to handle errors. sharedRecorder.delegate = self // Start recording if #available(iOS 10.0, *) { #if os(iOS) sharedRecorder.isMicrophoneEnabled = true //sharedRecorder.isCameraEnabled = true // fixme #endif sharedRecorder.startRecording { [unowned self] error in if let error = error as? NSError, error.code != RPRecordingErrorCode.userDeclined.rawValue { print(error.localizedDescription) // Show alert return } } } else { // Fallback on earlier versions sharedRecorder.startRecording(withMicrophoneEnabled: true) { error in if let error = error as? NSError, error.code != RPRecordingErrorCode.userDeclined.rawValue { print(error.localizedDescription) // Show alert return } } } } 

To stop recording, call this. You noticed that I do not show a preview yet, I just store it for later use. This is the usual way you do it.

  func stop() { let sharedRecorder = RPScreenRecorder.shared() // Do nothing if screen recording is not available guard sharedRecorder.isAvailable else { return } // Stop recording sharedRecorder.stopRecording { [unowned self] (previewViewController, error) in if let error = error { // If an error has occurred, display an alert to the user. print(error.localizedDescription) // Show alert return } print("Stop screen recording") if let previewViewController = previewViewController { // Set delegate to handle view controller dismissal. previewViewController.previewControllerDelegate = self /* Keep a reference to the `previewViewController` to present when the user presses on preview button. */ self.previewViewController = previewViewController } } } 

Then create 2 extensions in the ScreenRecorder class that correspond to the ReplayKit delegates.

Preview Manager Delegate

  /// RPPreviewViewControllerDelegate extension ScreenRecorder: RPPreviewViewControllerDelegate { /// Preview controller did finish func previewControllerDidFinish(_ previewController: RPPreviewViewController) { previewController.dismiss(animated: true, completion: nil) } } 

and delegate entries

 extension ScreenRecorder: RPScreenRecorderDelegate { /// Screen recoder did stop with error func screenRecorder(_ screenRecorder: RPScreenRecorder, didStopRecordingWithError error: Error, previewViewController: RPPreviewViewController?) { // Display the error the user to alert them that the recording failed. let error = error as NSError if error.code != RPRecordingErrorCode.userDeclined.rawValue { print(message: error.localizedDescription) // show alert } // Hold onto a reference of the `previewViewController` if not nil. if let previewViewController = previewViewController { self.previewViewController = previewViewController } } /// Screen recoder did change availability func screenRecorderDidChangeAvailability(_ screenRecorder: RPScreenRecorder) { // eg update your button UI etc // you can use something like delegation to pass something to your SKScenes } } 

And finally, create a preview view method. You usually call this through a button in your game through the menu.

 func showPreview() { guard let previewViewController = previewViewController else { return } print("Showing screen recording preview") // `RPPreviewViewController` only supports full screen modal presentation. previewViewController.modalPresentationStyle = .fullScreen let rootViewController = UIApplication.shared.keyWindow?.rootViewController rootViewController?.present(previewViewController, animated: true, completion:nil) } 

Now you can call methods anywhere in your project

 ScreenRecorder.shared.start() ScreenRecorder.shared.stop() ScreenRecorder.shared.showPreview() // call stop before calling this 

This code is pretty much straight from DemoBots.

I think the best way to handle recording on the screen is to create an auto record button in the main menu. Use UserDefaults to save on / off status. If it is on, you call startRecording when your gameplay begins, and cause it to stop recording when the game ends. Then you show the preview button in your game through the menu to see the recording if the user wants.

Hope this helps

+3
source

DemoBots is tricky. Remember to import ReplayKit, RPPreviewViewControllerDelegate, RPScreenRecorderDelegate. Can you learn the final code on GitHub or something, crashoverride777. This is what I wrote and I cannot get a preview display:

 func startRecording() { let sharedRecorder = RPScreenRecorder.shared() // Do nothing if screen recording is not available guard sharedRecorder.isAvailable else { return } // Stop previous recording if necessary if sharedRecorder.isRecording { stopScreenRecording() } print("Starting screen recording") // Register as the recorder delegate to handle errors. sharedRecorder.delegate = self // Start recording sharedRecorder.isMicrophoneEnabled = true //sharedRecorder.isCameraEnabled = true // fixme sharedRecorder.startRecording { error in if let error = error as NSError?, error.code != RPRecordingErrorCode.userDeclined.rawValue { print(error.localizedDescription) // Show alert return } } } func stopScreenRecording() { let sharedRecorder = RPScreenRecorder.shared() // Do nothing if screen recording is not available guard sharedRecorder.isAvailable else { return } // Stop recording sharedRecorder.stopRecording { [unowned self] (previewViewController, error) in if let error = error { // If an error has occurred, display an alert to the user. print(error.localizedDescription) // Show alert return } print("Stop screen recording") if let previewViewController = previewViewController { // Set delegate to handle view controller dismissal. previewViewController.previewControllerDelegate = self /* Keep a reference to the `previewViewController` to present when the user presses on preview button. */ self.previewController = previewViewController } } } func previewControllerDidFinish(_ previewController: RPPreviewViewController) { view?.window?.rootViewController?.dismiss(animated: true) } 
0
source

Source: https://habr.com/ru/post/1257535/


All Articles