原文: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是非常出色非常強大的框架,是一款不可多得的應用程序的屏幕錄制及共享的工具!