Feel Physics Backyard

HoloLensの出張授業をする会社で、教材を開発しています

アプリの画面を動画として保存する 罠だらけ(iOS9)

f:id:weed_7777:20160930172541p:plain

ゲームのプレイ動画を保存するなどの目的でiOS9からはReplayKitというフレームワークが加わっています。一見、これを使えば簡単そうですが、やってみると実際にはスレッド制御などが必要など、罠だらけです。

ReplayKit Framework has added from iOS9 for the purpose of saving the game play video. At first glance, it is simple enough, but if you use this, in fact, be full of traps, such as the need of thread control.

海外も含めて、ReplayKitの紹介している記事はたいてい以下のように書いています:

Including overseas, articles introducing ReplayKit is usually written as follows.

以下のように手軽にプレイ動画を保存することができます

You can easily save the play video as follows:

import ReplayKit

// start recording
RPScreenRecorder.sharedRecorder()
.startRecordingWithMicrophoneEnabled(false, handler: { error in })

// stop recording
RPScreenRecorder.sharedRecorder()
.stopRecordingWithHandler { viewController, error in
    self.presentViewController(viewController!, animated: true, completion: nil)
}

おー、これは簡単そう。しかし実際にはいくつもの罠が…

Oh, this is simple enough. In practice, however there was a trap of a number:

  • start recordingしてから実際にrecording has startedまでに3秒くらいかかる
  • stop recordingすると保存画面が表示されるが、「save」「cancel」共に押しても何も起こらない
  • プロトコルを指定すると保存できるようになるが、「保存」を押してから保存画面が消えるまでに時間がかかり、しかもそのあいだ何回も(同じ動画を)保存できてしまう
  • 最終的にはメインスレッドを呼んで保存画面を消さなければならない

  • It takes about 3 seconds among "start recording" and "recording has started".
  • When stop recording, save screen is displayed. But nothing happens when I press the "save", "cancel" button.
  • When I specified the protocol, I've got to be able to save, but it takes a long time before save screen disappears after pressing the "Save", yet I can save it (the same video) for many times during the time.
  • After all, I had to call the main thread to erase the save screen.

最終的には以下のようなコードになりました。どこが手軽だ!

Eventually it became the code, such as the following. Where is the easy!

import ReplayKit

// プロトコルを指定する
// specify protocol
class ViewController: UIViewController, RPPreviewViewControllerDelegate {
    var isRecording = false
    var previewController: RPPreviewViewController!

    @IBAction func RecordPressed(sender: AnyObject) {
        if isRecording {
            isRecording = false
            print("record stopped")
            RPScreenRecorder.sharedRecorder().stopRecordingWithHandler { previewController, error in
                // 保存画面のボタンを押すとメッセージがselfに刺さるようにする
                // let message hit self when I push the button on save screen
                previewController?.previewControllerDelegate = self
                self.previewController = previewController;
                self.presentViewController(previewController!, animated: true, completion: nil)
            }
        } else {
            isRecording = true
            print("prepare to record")
            RPScreenRecorder.sharedRecorder().startRecordingWithMicrophoneEnabled(true) { error in
                print("recording started")
            }
        }
    }

    // プロトコルを指定して、保存画面の「保存」「キャンセル」を受け取るようにする
    // specify protocol and recieve "save" "cancel" from save screen
    func previewControllerDidFinish(previewController: RPPreviewViewController) {
        // そのままだと5秒くらい保存画面が残り、多重保存もできてしまうので、メインスレッドで実行
        // save screen remains about 5 seconds and you can save the same video many times, so run on main thread
        dispatch_async(dispatch_get_main_queue()) { [unowned previewController] in
            // close preview window
            previewController.dismissViewControllerAnimated(true, completion: nil)
        }
    }

あれこれ探した結果、以下の記事のコードでようやくちゃんと動くものができました。ありがとうございました!

With the code of article below I've made code runs correctly. Thanks!

iOS 9 の新機能のサンプルコード集『iOS-9-Sampler』を公開しました - Over&Out その後