-
原文:iOS 9: An Introduction to ReplayKit(发表时间:2016.01.13)
-
作者:Davis Allie
-
译者:CocoaChina--guofei(CC论坛ID)
ReplayKit简介
在iOS 9中,ReplayKit 是一款全新的框架,可谓是游戏开发者(开发商)的福音。它可以让玩家更便捷地记录游戏进度或数据以及分享的功能。除此之外更强大的是:ReplayKit为用户(玩家)提供了一个全功能的交互界面,用户可用它来编辑或制作自己的视频剪辑!
ReplayKit不需要太大电量损耗和性能损耗就可以产出高清的视频记录。ReplayKit支持使用A7芯片以上,操作系统为iOS 9或更高版本的设备。
您需要准备什么
本教程要求您的Xcode版本为7.0以上,OS X为Yosemite(10.10.x)以上。倘若您还想在您的设备上体验一下这个简易的工程,请确保您的设备可以满足ReplayKit所需要的软硬件要求,当然您还需在GitHub上下载工程源码。
启动录制
ReplayKit框架提供了RPScreenRecorder类以及类单例方法sharedRecorder()供您进行游戏录制。这个实例对象负责检查设备的记录功能,包括启动、停止以及丢弃记录,并可以选择启动麦克风让玩家录制真人语音解说!
打开从GitHub下载的初始工程中GameViewController.swift文件。在文件顶部,导入ReplayKit框架。
import ReplayKit
接下来,在用户按下Start Recording按钮时调用GameViewController类中的startRecording(_:)这个方法开始录制。
func startRecording(sender: UIButton) { if RPScreenRecorder.sharedRecorder().available { RPScreenRecorder.sharedRecorder().startRecordingWithMicrophoneEnabled(true, handler: { (error: NSError?) -> Void in if error == nil { // Recording has started sender.removeTarget(self, action: "startRecording:", forControlEvents: .TouchUpInside) sender.addTarget(self, action: "stopRecording:", forControlEvents: .TouchUpInside) sender.setTitle("Stop Recording", forState: .Normal) sender.setTitleColor(UIColor.redColor(), forState: .Normal) } else { // Handle error } }) } else { // Display UI for recording being unavailable } }
跟着代码一步一步走。我们通过sharedRecorder()方法访问RPScreenRecorder实例以检查我们的设备录制功能是否可用。如果功能可用,我们便可以通过调用startRecordingWithMicrophone(_:handler:)方法启动一段记录。此方法的第一个参数为BOOL类型值,表示是否开启设备的麦克风,第二个参数则为完成后回调的代码块。如果出现一些错误,RepalyKit框架可以通过代码块返回给你并提示您错误的信息。如果一切准备就绪,我们改变按钮的式样告知用户录制已开始,再次点击可以停止录制。
编译运行你的应用程序并尝试按下绿色按钮,你会看到类似一团火焰的粒子效果,如果你点击Start Recording,你会看到这样的警告,如图:
注意,这个警告每次会在你开始录制时出现。然而,一旦用户选择了其中一种偏好设置,系统会在接下来的8分钟记住这个选择。
在你选择选项之后,Start Recording按钮变为了红色的Stop Recording按钮。
停止,丢弃和编辑记录
现在,我们的app可以开始ReplayKit的录制,是时候去了解在完成的时候编写怎样的代码了。在GameViewController类中实现stopRecording(_:)这个方法:
func stopRecording(sender: UIButton) { RPScreenRecorder.sharedRecorder().stopRecordingWithHandler { (previewController: RPPreviewViewController?, error: NSError?) -> Void in if previewController != nil { let alertController = UIAlertController(title: "Recording", message: "Do you wish to discard or view your gameplay recording?", preferredStyle: .Alert) let discardAction = UIAlertAction(title: "Discard", style: .Default) { (action: UIAlertAction) in RPScreenRecorder.sharedRecorder().discardRecordingWithHandler({ () -> Void in // Executed once recording has successfully been discarded }) } let viewAction = UIAlertAction(title: "View", style: .Default, handler: { (action: UIAlertAction) -> Void in self.presentViewController(previewController!, animated: true, completion: nil) }) alertController.addAction(discardAction) alertController.addAction(viewAction) self.presentViewController(alertController, animated: true, completion: nil) sender.removeTarget(self, action: "stopRecording:", forControlEvents: .TouchUpInside) sender.addTarget(self, action: "startRecording:", forControlEvents: .TouchUpInside) sender.setTitle("Start Recording", forState: .Normal) sender.setTitleColor(UIColor.blueColor(), forState: .Normal) } else { // Handle error } } }
继续一步一步地研究这个方法的实现。我们还是用RPScreenRecorder的实例对象调用stopRecordingWithHandler(_:)这个方法,这次在回调的块中,我们通过检查previewController存不存在来判断app完成录制的成功与否。
我们创建一个UIAlertController,它有两个action,一个为丢弃记录,另一个为回看记录。选择丢弃记录则调用discardRecordingWithHandler(_:)这个方法。要注意的是,这个方法只能在确保录制成功地完成后才可以调用,要是在录制进行的时候就调用的话,虽然系统不会抛出任何错误,但是也不会丢弃任何记录。
选择回看记录,我们就呈现previewController视图,它是RPPreviewController类的实例,从stopRecordingWithHandler(_:)方法回调块中返回给我们,用来回看、编辑或分享记录。这个previewController视图控制器实例是唯一能够访问到由ReplayKit生成的视频文件,它的职能就是负责保存/分享记录。
最后,别忘了恢复startRecording按钮以便再次另一段记录的开始!
编译和运行你的应用程序并点击开始录制。一旦按下Stop Recording按钮,你会看到如下的弹出窗:
如果你选择了view选项,会呈现如下的视图控制器:
在这里,你可以编辑你的录像并可以点击Save按钮选择转存到你的“照片”中。当然你也可以点击左下角的分享按钮分享你的录制视频。
需要注意的是,不管是由于设计的原因还是ReplayKit框架的bug,在保存到“照片”的时候没有确认的过程就直接进行了保存。
排除界面元素
你可能已经注意到了顶部和底部的按钮在应用录制的记录中都是可见的,它们包含在了最终的视频记录中。当RepalyKit录制你的应用时,它毫不遗漏地记录了应用程序在UIWindow中渲染的一切视图,任何细节都不掩饰地记录。还好,RepalyKit可以在来电话是或用户输入时的界面停止录制。
从记录中排除的用户界面元素,你需要把它们放置在单独的UIWindow实例中。让我们研究下它是如何工作的。在GameViewController类中添加一个属性buttonWindow,类型为UIWindow!
var buttonWindow: UIWindow!
下一步,用以下代码取代GameViewController类中的addButtons(_:)方法:
func addButtons(buttons: [UIButton]) { self.buttonWindow = UIWindow(frame: self.view.frame) self.buttonWindow.rootViewController = HiddenStatusBarViewController() for button in buttons { self.buttonWindow.rootViewController?.view.addSubview(button) } self.buttonWindow.makeKeyAndVisible() }
在 addButton(_:)方法中,我们新创建了一个新的UIWindow对象,并给它加了一些按钮然后让它可见。注意HiddenStatusBarViewController类是我们在初始工程中加入的一个自定义的视图控制器,它用来确保在新的窗口隐藏屏幕上方的状态栏。
最后,用以下的代码补全stopRecording(_:)方法的实现部分:
func stopRecording(sender: UIButton) { RPScreenRecorder.sharedRecorder().stopRecordingWithHandler { (previewController: RPPreviewViewController?, error: NSError?) -> Void in if previewController != nil { let alertController = UIAlertController(title: "Recording", message: "Do you wish to discard or view your gameplay recording?", preferredStyle: .Alert) let discardAction = UIAlertAction(title: "Discard", style: .Default) { (action: UIAlertAction) in RPScreenRecorder.sharedRecorder().discardRecordingWithHandler({ () -> Void in // Executed once recording has successfully been discarded }) } let viewAction = UIAlertAction(title: "View", style: .Default, handler: { (action: UIAlertAction) -> Void in self.buttonWindow.rootViewController?.presentViewController(previewController!, animated: true, completion: nil) }) alertController.addAction(discardAction) alertController.addAction(viewAction) print(self.buttonWindow.rootViewController) self.buttonWindow.rootViewController?.presentViewController(alertController, animated: true, completion: nil) sender.removeTarget(self, action: "stopRecording:", forControlEvents: .TouchUpInside) sender.addTarget(self, action: "startRecording:", forControlEvents: .TouchUpInside) sender.setTitle("Start Recording", forState: .Normal) sender.setTitleColor(UIColor.blueColor(), forState: .Normal) } else { // Handle error } } }
新的视图控制器唯一不同于之前的是窗口顶端的交互部分。它确保了交互控件正确的显示与用户交互的正确执行。
再次运行你的应用,并进行新的录制,你会发现界面按钮都隐藏了:
委托协议
还有与ReplayKit关联的两个协议共四个代理方法,不过本教程没有用到,但我们应该了解应当怎样使用它们。
RPScreenRecorderDelegate协议定义了以下两个方法:
-
screenRecorder(_:didStopRecordingWithError:previewViewController:) 不管何时,只要录制过程中出现了错误就会调用这个方法。当然如果ReplayKit能够从这个错误自我修复并完成录制,你仍可以选择将预览视图呈现给用户。
-
screenRecorderDidChangeAvailability(_:) 这个方法在触发另一个action引发录制状态改变时回调。举个例子,当你连接或断开Airplay时,就会触发此方法。
RPPreviewViewControllerDelegate协议定义了以下方法:
-
previewViewControllerDidFinlish(_:) 这个方法会在用户退出RPPreviewViewController视图实例的时候立刻调用。
-
previewViewController(_:didFinlishWithActivityTypes:) 这个方法会在previewViewControllerDidFinlish(_:)回调的同时触发,只不过这个方法携带了一个额外的UIActivity类型的参数。
需要注意的是,如果你实现了任意的RPPreviewViewControllerDelegate的方法,你就需要负责在适当的时候推出previewViewController视图控制器。
要点
在即将完成时,你还需要注意使用RepalyKit时的一些关键的要点。
-
每个应用程序在任何时间只可以存储一条记录。一旦你开始录制一条新的记录,如果之前的记录已存在,那么新的会自动覆盖掉之前的记录!
-
及时地丢弃不必要的记录。确保没有太多不必要的视频数据占用设备的本地存储空间,从用户的行为中一旦判断出一条记录没有价值时,及时地丢弃它。在本教程中,在用户不需要记录的情况下,教程也给出了最佳的实现逻辑那就是丢弃它。
-
显示录制指示器。正如本教程中,显示指示器可以提示用户视频是否在录制,尤其在同时使用设备的麦克风的情况下,尽可能地提高用户体验。
-
仔细选择要从用户的录制交互界面排除的元素,把选择界面的元素放置在单独的窗口中,包括用以选择的控件或者偏离游戏本身那些不重要的东西。如:记录指示器,虚拟控制或菜单按钮。
-
你不能直接访问最终的视频文件。通过用户回看视图控制器previewViewController你才可以使ReplayKit的记录存储的数据可见。但由于苹果对用户隐私保护,ReplayKit的记录数据都应由应用程序来访问。如果你想上传这些记录到你自己的服务器,你需要创建一个共享的扩展视图控制器来完成,并从previewViewController中呈现出来。
-
ReplayKit也支持家长控制功能。即使设备当前后台没有其他的进程在运行,录制功能是否可用仍然收到家长控制的授权。这就意味着你要保证在录制开始检查功能是否被允许使用。
-
最后要说的是,尽管苹果已经向游戏开发者(开发商)做好了用ReplayKit使用户分享他们的游戏的准备,但你可以在任何苹果应用中使用ReplayKit,只要满足Xcode 7+和iOS 9+的要求。我非常地支持使用ReplayKit这一优秀的框架,我很乐意看到更多开发者可以使用它,做出更多更好的苹果应用!
总结
你现在应该很乐意在你的app中集成ReplayKit框架完成屏幕录制功能了,它可以轻松编辑你的用户共享。总的来说,ReplayKit是非常出色非常强大的框架,是一款不可多得的应用程序的屏幕录制及共享的工具!