如何在SpriteKit SKScene中开启ReplayKit录屏class
How to start ReplayKit screen recording in SpriteKit SKScene class
我在我的 SpriteKit
游戏中实现了 ReplayKit
,但是由于一切都在 GameViewController
中完成,因此记录按钮出现得太早了。请看下面我的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
}
}
如何从继承自 SKScene
的 class 调用 startRecording
和 stopRecording
函数,例如 GameScene
class?
如何启用、禁用和隐藏 GameScene
class 中的 videoRecButton
按钮?
更新
根据 crashoverride777 的回答,a 在我的 SKScene
class 中放置了以下代码,但在带有录制视频预览的导航控制器出现之前,屏幕只录制了几秒钟。录制的视频只是黑屏,取消和保存按钮没有反应。
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)
}
我创建了一个记录按钮:
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()
}
}
}
你不应该在 GameViewController 中创建你的按钮,直接在你的 SKScene 中创建它。在 SpriteKit 游戏中 UI 使用 GameViewController 不是好的做法。
有很多关于如何在 SpriteKit 中创建按钮的教程。
关于ReplayKit,你可以直接在你想要的SKScene中使用,把你已有的代码移到相关的Scene中即可。
要在 SKScene 中显示预览视图控制器,您可以这样说
view?.window?.rootViewController?.present(unwrappedPreview, animated: true)
我还注意到您在停止录制后显示了视图控制器。你确定要这样做吗?通常你的游戏结束菜单中有一个单独的按钮,你可以在其中观看录制。
这是通用代码。我还建议您查看 apples 示例游戏 DemoBots。
我个人使用 Singleton class 来管理录音,这样可以更轻松地管理调用所有方法,以防您在不同的场景中需要它。要开始 class 创建一个新的 swift 文件并添加此代码。
class ScreenRecoder: NSObject {
/// Shared instance
static let shared = ScreenRecorder()
/// Preview controller
var previewController: RPPreviewViewController?
/// Private singleton init
private override init() { }
}
然后开始录制将此方法添加到 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
}
}
}
}
要停止录音,请调用此。您注意到我实际上并没有显示预览,我只是将其存储以备后用。这是你通常的做法。
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
}
}
}
比在 ScreenRecorder 中创建 2 个扩展 class 符合 ReplayKit 委托。
预览控制器委托
/// RPPreviewViewControllerDelegate
extension ScreenRecorder: RPPreviewViewControllerDelegate {
/// Preview controller did finish
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
previewController.dismiss(animated: true, completion: nil)
}
}
和录音代表
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
}
}
最后创建一个方法来呈现预览。最好通过游戏结束菜单中的按钮来调用它。
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)
}
现在您可以在项目的任何地方调用这些方法
ScreenRecorder.shared.start()
ScreenRecorder.shared.stop()
ScreenRecorder.shared.showPreview() // call stop before calling this
此代码几乎直接来自 DemoBots。
我认为处理屏幕录制的最好方法是在主菜单中创建一个自动录制按钮。使用 UserDefaults 保存它的 on/off 状态。如果它打开,你会在游戏开始时调用 startRecording,并在游戏结束时调用停止录制。如果用户愿意,您可以在游戏结束菜单中显示一个预览按钮来观看录音。
希望对您有所帮助
DemoBots 很难。记得导入 ReplayKit、RPPreviewViewControllerDelegate、RPScreenRecorderDelegate。你可以 post 你的最终代码 GitHub 什么的,crashoverride777。这是我写的,我无法得到预览显示:
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's 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)
}
我在我的 SpriteKit
游戏中实现了 ReplayKit
,但是由于一切都在 GameViewController
中完成,因此记录按钮出现得太早了。请看下面我的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
}
}
如何从继承自
SKScene
的 class 调用startRecording
和stopRecording
函数,例如GameScene
class?如何启用、禁用和隐藏
GameScene
class 中的videoRecButton
按钮?
更新
根据 crashoverride777 的回答,a 在我的 SKScene
class 中放置了以下代码,但在带有录制视频预览的导航控制器出现之前,屏幕只录制了几秒钟。录制的视频只是黑屏,取消和保存按钮没有反应。
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)
}
我创建了一个记录按钮:
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()
}
}
}
你不应该在 GameViewController 中创建你的按钮,直接在你的 SKScene 中创建它。在 SpriteKit 游戏中 UI 使用 GameViewController 不是好的做法。
有很多关于如何在 SpriteKit 中创建按钮的教程。
关于ReplayKit,你可以直接在你想要的SKScene中使用,把你已有的代码移到相关的Scene中即可。
要在 SKScene 中显示预览视图控制器,您可以这样说
view?.window?.rootViewController?.present(unwrappedPreview, animated: true)
我还注意到您在停止录制后显示了视图控制器。你确定要这样做吗?通常你的游戏结束菜单中有一个单独的按钮,你可以在其中观看录制。
这是通用代码。我还建议您查看 apples 示例游戏 DemoBots。
我个人使用 Singleton class 来管理录音,这样可以更轻松地管理调用所有方法,以防您在不同的场景中需要它。要开始 class 创建一个新的 swift 文件并添加此代码。
class ScreenRecoder: NSObject {
/// Shared instance
static let shared = ScreenRecorder()
/// Preview controller
var previewController: RPPreviewViewController?
/// Private singleton init
private override init() { }
}
然后开始录制将此方法添加到 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
}
}
}
}
要停止录音,请调用此。您注意到我实际上并没有显示预览,我只是将其存储以备后用。这是你通常的做法。
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
}
}
}
比在 ScreenRecorder 中创建 2 个扩展 class 符合 ReplayKit 委托。
预览控制器委托
/// RPPreviewViewControllerDelegate
extension ScreenRecorder: RPPreviewViewControllerDelegate {
/// Preview controller did finish
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
previewController.dismiss(animated: true, completion: nil)
}
}
和录音代表
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
}
}
最后创建一个方法来呈现预览。最好通过游戏结束菜单中的按钮来调用它。
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)
}
现在您可以在项目的任何地方调用这些方法
ScreenRecorder.shared.start()
ScreenRecorder.shared.stop()
ScreenRecorder.shared.showPreview() // call stop before calling this
此代码几乎直接来自 DemoBots。
我认为处理屏幕录制的最好方法是在主菜单中创建一个自动录制按钮。使用 UserDefaults 保存它的 on/off 状态。如果它打开,你会在游戏开始时调用 startRecording,并在游戏结束时调用停止录制。如果用户愿意,您可以在游戏结束菜单中显示一个预览按钮来观看录音。
希望对您有所帮助
DemoBots 很难。记得导入 ReplayKit、RPPreviewViewControllerDelegate、RPScreenRecorderDelegate。你可以 post 你的最终代码 GitHub 什么的,crashoverride777。这是我写的,我无法得到预览显示:
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's 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)
}