【问题标题】:How to start ReplayKit screen recording in SpriteKit SKScene class如何在 SpriteKit SKScene 类中启动 ReplayKit 屏幕录制
【发布时间】:2016-09-30 14:30:41
【问题描述】:

我已经在我的SpriteKit 游戏中实现了ReplayKit,但由于一切都在GameViewController 内完成,因此录制按钮出现得太早了。请在下面查看我的GameViewController 课程:

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
}
}
  1. 如何从继承自 SKScene 的类(如 GameScene 类)调用 startRecordingstopRecording 函数?

  2. 如何在GameScene 类中启用、禁用和隐藏videoRecButton 按钮?

更新

根据 crashoverride777 的回答,a 在我的 SKScene 类中放置了以下代码,但屏幕会记录几秒钟,然后导航控制器会显示已录制视频的预览。录制的视频只是黑屏,取消和保存按钮没有响应。

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

【问题讨论】:

    标签: ios swift uiviewcontroller sprite-kit replaykit


    【解决方案1】:

    你不应该在 GameViewController 中创建你的按钮,直接在你的 SKScene 中创建它。在 SpriteKit 游戏中将 GameViewController 用于 UI 并不是一个好习惯。

    有很多关于如何在 SpriteKit 中创建按钮的教程。

    关于 ReplayKit,你可以直接在你想要的 SKScene 中使用它,只需将你已有的代码移动到相关的场景中即可。

    要在 SKScene 中显示预览视图控制器,您可以这样说

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

    我还注意到您在停止录制后会显示视图控制器。你确定要这样做吗?通常,您的游戏结束菜单中有一个单独的按钮,您可以在其中观看录制。

    这是通用代码。我还建议您查看苹果示例游戏 DemoBots。

    我个人使用 Singleton 类来管理录制,这样管理器更容易调用所有方法,以防您在不同场景中需要它。要开始课程,请创建一个新的 swift 文件并添加此代码。

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

    要开始录制,请将此方法添加到 ScreenRecorder 类。

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

    比在符合 ReplayKit 委托的 ScreenRecorder 类中创建 2 个扩展。

    预览控制器委托

     /// 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 保存它的开/关状态。如果它打开了,您在游戏开始时调用 startRecording,并在游戏结束时调用停止录制。如果用户愿意,您可以在游戏结束菜单中显示预览按钮以观看录制。

    希望对你有帮助

    【讨论】:

    • 你为什么要创建一个导航控制器来显示屏幕录像?只需直接呈现回放套件视图控制器即可。你真的不应该在 SpriteKit 中使用 UIKit。我会更新我的答案,给我大概 2-3 分钟
    • 用完整的屏幕录制代码示例更新了我的答案。希望这可以帮助。随时通知我
    • 它正在工作,但是当我选择保存或取消时预览不会关闭。另外,我如何在不创建实例的情况下在另一个不同的SKScene 中调用showScreenRecordingPreview()
    • 您需要按照我的回答中的描述实现 RPPreviewViewControllerDelegate 否则它不会关闭。关于在另一个场景中显示该代码,这超出了您的问题范围。我个人在一个单例类中拥有所有这些代码。我将再次更新我的答案,向您展示单例方式。给我 3-4 分钟。
    • 使用单例方法更新。告诉我进展如何。
    【解决方案2】:

    DemoBots 很难。记得导入 ReplayKit、RPPreviewViewControllerDelegate、RPScreenRecorderDelegate。你能在 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)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多