I have implemented ReplayKit
in my SpriteKit
game, but since everything is done within the GameViewController
the record button appears too early. Please see my GameViewController
class below:
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
functions from a class that inherits from SKScene
like GameScene
class?
How can enable, disable and hide the videoRecButton
button from the GameScene
class?
UPDATE
Based on an answer from crashoverride777 a placed the following code in my SKScene
class but the screen records for just a few seconds before the navigation controller with a preview of the recorded video appears. The recorded video is just a black screen and the cancel and save buttons are unresponsive.
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()
}
}
}
ReplayKit is a framework that allows developers to add recording and live broadcasting capabilities to their apps. In addition, it allows users to annotate their recordings and broadcasts using the device's front-facing camera and microphone.
The ReplayKit namespace provides classes that allows screen recording of the developer's application. Additionally, it provides a standard RPPreviewViewController view controller that allows the user to preview, trim, and share the recording. Developers must use the SharedRecorder singleton to create replays.
You should not create your button in the GameViewController, create it directly in your SKScene. Its not good practice to use the GameViewController for UI in SpriteKit games.
There is plenty tutorials around on how to create buttons in SpriteKit.
In regards to ReplayKit, you can use it directly in the SKScene you want, just take the code you have already and move it to the relevant Scene.
To present the preview view controller in a SKScene you can say this
view?.window?.rootViewController?.present(unwrappedPreview, animated: true)
I also noticed you are presenting the View controller after you stop recording. You sure you want to do this? Usually you have a separate button in your game over menu where you can watch the recording.
Here is the general code. I also reccomend you check out apples Sample game DemoBots.
I personally use a Singleton class to manager recording, this way its easier to manager calling all the methods incase you need it for different scenes. To start of the class crate a new swift 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() { }
}
Than 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's 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 notice I am not actually showing the preview yet, I am simply storing 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
}
}
}
Than create 2 extensions in the ScreenRecorder class conforming to the ReplayKit delegates.
The Preview Controller Delegate
/// RPPreviewViewControllerDelegate
extension ScreenRecorder: RPPreviewViewControllerDelegate {
/// Preview controller did finish
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
previewController.dismiss(animated: true, completion: nil)
}
}
and the recording delegate
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) {
// e.g update your button UI etc
// you can use something like delegation to pass something to your SKScenes
}
}
And finally create a method to present the preview. Preferably you call this via a button in your game over 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 the methods anywhere you like in your project
ScreenRecorder.shared.start()
ScreenRecorder.shared.stop()
ScreenRecorder.shared.showPreview() // call stop before calling this
This code is pretty much straight out of DemoBots.
I think the nicest way to handle screen recording is by creating an auto-recording button in your main menu. Use UserDefaults to save the on/off state of it. If its turned on you call startRecording when your gameplay begins, and call stop recording when its game over. Than you show a preview button in your game over menu to watch the recording if the user wants to.
Hope this helps
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With