誰不愛在自己的 iPhone 上玩遊戲呢?我想我的朋友及家人會同意我花太多的時間在手機上玩遊戲。然而,最能夠表現出 iPhone 強大能力的東西之一就是這些聲光效果十足的遊戲了。
幾乎所有喜愛在 iPhone 上玩遊戲的人們都很喜歡向他們的朋友炫耀這些遊戲是多麽的好。而這就是為什麼 Apple 會在 WWDC 15 上發表 iOS 9 時,同時公布 ReplayKit 這支 API。 ReplayKit 是一支令人驚豔的 API,它可以讓開發者們以非常簡單的方式將螢幕錄製功能整合進 App 中。只要按下一個按鈕,玩家們就可以錄製遊戲畫面然後即時地分享給朋友們。先別急,我知道 iOS 11 新增了螢幕錄製功能讓你隨時隨地錄製螢幕畫面,但對玩家們來說使用上並不直覺,因為它需要開啟控制中心且要離開遊戲來編輯及傳送影片。而 ReplayKit 是專為玩家們所設計的 API。
在本次的教學中,我將會示範如何製作一個簡易螢幕錄製 App。這個 App 將會有一顆按鈕控制開始及停止錄製、一個 Label 用來顯示錄影的狀態以及一些可以操作的 UI 元件,因此你就可以錄製自己所做的事情。那麼,我們就開始吧!
App 概覽
我們這次要製作的 App 會是非常好撰寫以及使用。首先會有一顆綠色按鈕用來開始螢幕錄製。一旦點擊,按鈕會變成紅色,而在螢幕上方的 Label 會顯示現在正在錄製。App 同時含有一個有多種顏色選項的 Segmented View。點擊其中一種顏色,螢幕中間的方框的背景色會跟著變換成那個顏色,這是讓你可以紀錄下螢幕的變化。此外,有一個麥克風的開關讓你可以選擇是否想要錄下麥克風的聲音。
建立專案
那麼我們開始製作吧!開啟 Xcode 並建立專案,選擇 Single View Application 模板。我將這個專案命名為 ScreenRecord,但你可以填上你想要的名字。記得確認程式語言為 Swift。在這個 App 中我們不需要 Core Data,所以我們取消勾選。
一旦你已經建立好這個專案,進入 Deployment Info,然後取消勾選 Landscape Left
以及 Landscape Right
。這些在這個專案中並不需要。
製作 UI
因為這個 App 的 UI 相對簡單,所以我們不會花太多的時間在這上頭。前往 Main.Storyboard
,然後拖曳一個 Label
到 View 的上方並拉長一點,我自己將寬度設定為 165 px。接著將文字設定為 Ready to Record(別忘記這步驟唷,這非常重要)。然後添加一個 Segmented Control
在 Label 底下。設定 Segmented Control 有四個 不同顏色名字的 Segements,分別是:red, blue, orange, and green。記得確認將 Segmented Control 拉長到足夠的寬度好讓名字不會被切割。
接下來,新增一個 View
到 ViewController 的正中央。讓它成一個方形(我自己設定為 150×150)然後更換背景顏色為紅色。再新增一個 Switch
到方形下方。如果你想要的話,你可以放入一個 Microphone 字樣的 Label 在 Switch 的上面。最後,新增一個 Button
在 Switch 底下,讓它成方形(我設定為 65×65),刪除按鈕標題文字,然後將背景顏色更改為綠色。
現在你的 ViewController 應該長得像這樣:
設定 Outlets、Objects 以及 View
現在終於要開始撰寫程式碼囉!前往 ViewController.swift
,然後在 Class 的 import UIKit
底下引入 ReplayKit 程式庫。
import ReplayKit
現在,讓我們添加一些 Outlets 好讓我們的 UI 可以連結到程式碼。我們總共需要五個 Outlets:
@IBOutlet var statusLabel: UILabel!
@IBOutlet var colorPicker: UISegmentedControl!
@IBOutlet var colorDisplay: UIView!
@IBOutlet var recordButton: UIButton!
@IBOutlet var micToggle: UISwitch!
一個 Outlet 對應一個 UI 元件。你的程式碼現在應該看起來像這樣:
import UIKit
import ReplayKit
class ViewController: UIViewController {
@IBOutlet var statusLabel: UILabel!
@IBOutlet var colorPicker: UISegmentedControl!
@IBOutlet var colorDisplay: UIView!
@IBOutlet var recordButton: UIButton!
@IBOutlet var micToggle: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
現在,我們需要為我們的螢幕錄製器建立一個物件。在我們放置 Outlets 程式碼的下方宣告一些變數:
let recorder = RPScreenRecorder.shared()
private var isRecording = false
RPScreenRecorder 是一個共享的錄製器物件,它為 App 提供錄製聲音及影像的能力。isRecording
變數被用來追蹤錄製狀態,無論是否正在錄製。
接下來,讓我們把開始/結束按鈕變成圓形好讓它看起來就像一般的錄製按鈕。添加以下的程式碼到 viewDidLoad()
之中:
recordButton.layer.cornerRadius = 32.5
這行程式碼可以讓讓按鈕變成圓形。如果你把你的錄影按鈕的大小設定成跟我的一樣 (65×65),這會讓按鈕變成一個半徑 32.5(65的一半) 的完美圓形。
現在,我們要新增一個名為 viewReset()
的方法。當我們結束錄影並且希望重置所有在 ViewController 中的 UI 元件時會呼叫這個方法。將以下程式碼加入在 viewDidLoad()
之後,而不是在裡面:
func viewReset() {
micToggle.isEnabled = true
statusLabel.text = "Ready to Record"
statusLabel.textColor = UIColor.black
recordButton.backgroundColor = UIColor.green
}
這些程式碼所做的是將麥克風 Switch 設為 On、將 statusLabel
的文字顯示爲 App 已準備就序並將顏色設爲黑色、將 recordButton
的背景顏色變回綠色(按鈕在錄影時背景色會變為紅色)。
我們完成了基本設定,你的程式碼應該會看起來像:
import UIKit
import ReplayKit
class ViewController: UIViewController {
@IBOutlet var statusLabel: UILabel!
@IBOutlet var colorPicker: UISegmentedControl!
@IBOutlet var colorDisplay: UIView!
@IBOutlet var recordButton: UIButton!
@IBOutlet var micToggle: UISwitch!
let recorder = RPScreenRecorder.shared()
private var isRecording = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
recordButton.layer.cornerRadius = 32.5
}
func viewReset() {
micToggle.isEnabled = true
statusLabel.text = "Ready to Record"
statusLabel.textColor = UIColor.black
recordButton.backgroundColor = UIColor.green
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
加入 Actions
因為我們放入了一個 Segmentd Control 來切換 UIView
的背景顏色,所以我們可以錄製一些東西。而我們只需要一個簡單的 IBAction
函式來完成這個功能。在 viewReset()
底下加入以下函式:
@IBAction func colors(sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
colorDisplay.backgroundColor = UIColor.red
case 1:
colorDisplay.backgroundColor = UIColor.blue
case 2:
colorDisplay.backgroundColor = UIColor.orange
case 3:
colorDisplay.backgroundColor = UIColor.green
default:
colorDisplay.backgroundColor = UIColor.red
}
}
讓我來解釋一下上面的函式是做什麼用途。這個 IBAction 會被一個 UISegmentedControl
的 Sender 觸發。然後我們在之中加入一個 switch ,依據被點選的 Segment 來切換方形 UIView 的背景顏色。
現在加入一個 IBAction 讓它在錄製按鈕被按下時呼叫。將以下函式放在 color 函式底下:
@IBAction func recordButtonTapped() {
if !isRecording {
startRecording()
} else {
stopRecording()
}
}
這是一個非常簡單函式用來切換錄製模式。如果 isRecording
的狀態是 false
,表示我們還沒開始錄製影像,所以我們就會呼叫 startRecording()
函式。如果 isRecording
的值被設定爲 true
,那代表 App 正在進行錄製影像,所以它就會呼叫 stopRecording()
函式。因為我們還沒有撰寫這兩個函式,所以可以忽略現在發生的錯誤訊息。
連接 UI 元件
現在,我們已經完成了這個專案所需的所有 IBOutlet 以及 IBAction。回到 Main.storyboard
,我們可以開始將這些與合適的 UI 元件連接起來。點擊 View Controller ,然後在點擊 connections inspector(就是在右邊的 Utilities Panel 上的箭頭圖示)。現在拖曳所有的 Outlet 到各自的 SubView 上。
現在是時候連接所有的 IBAction 了。連結 recordButtonTapped
到視圖底部的錄製按鈕,然後選擇 Touch Up Inside 。接著連結 colorsWithSender
到 Segmented Control,然後確認選擇的是 Value Changed,這樣每當點選切換 Segment 時就會呼叫 IBAction。
現在所有的 IBOutlet 及 IBAction 都已經連結完畢了。在回頭到 ViewController.swift
吧。
開始錄製
現在終於到了本次教學的 ReplayKit 階段了!首先我們要添加 startRecording()
函式。將以下程式碼放在 recordButtonTapped
底下:
func startRecording() {
}
OK,我們要做的第一件事就是確認螢幕錄製器是真的可以用來錄製。實際上有可能因為一些原因而無法錄製影像,例如裝置不支援 ReplayKit 或是使用者正在使用 AirPlay 來投影到電視上。為了確認是否錄製器可以使用,我們會加入以下程式碼到 startRecording
中:
guard recorder.isAvailable else {
print("Recording is not available at this time.")
return
}
這東西十分簡單,guard 用來確認錄製器是否可以使用。如果無法使用,會印出錯誤訊息而函式會中斷執行。請確認函式其他的程式碼是置於 guard 底下。
還記得我之前加進到 UIView 裡的麥克風開關嗎?來判斷裡面的值吧。加入以下的程式碼到 guard 底下:
if micToggle.isOn {
recorder.isMicrophoneEnabled = true
} else {
recorder.isMicrophoneEnabled = false
}
如果開關是 On,我們就藉由設定 isMicrophoneEnabled
to true 來啟用麥克風。如果不是,我們就停用。就這麼簡單。
現在我們要真的開始撰寫錄製功能了,加入以下程式碼:
//1
recorder.startRecording{ [unowned self] (error) in
//2
guard error == nil else {
print("There was an error starting the recording.")
return
}
//3
print("Started Recording Successfully")
self.micToggle.isEnabled = false
self.recordButton.backgroundColor = UIColor.red
self.statusLabel.text = "Recording..."
self.statusLabel.textColor = UIColor.red
self.isRecording = true
}
讓我們來看看這是做什麼的:
1: 我們呼叫常數 recorder 裡的 startRecording
函式,這函式有一個 handler closure。
2: 在這個 hanlder block 中,我們加入一個 guard 來確保不會有錯誤發生。如果有錯誤發生,會印出錯誤訊息以及中斷函式。
3: 錄製功能已經正常地啟用。我們印出一段訊息以及改變一些 UI 元件來告訴使用者 App 正在錄製。為了達到這樣的目的,我們停用麥克風開關、將錄製按鈕的背景色更改爲紅色、將狀態 UILabel 的文字改為 recording 而文字顏色也換成紅色。
現在我們完成了 startRecording
函式!。完成的程式碼應該會看起來像這樣:
func startRecording() {
guard recorder.isAvailable else {
print("Recording is not available at this time.")
return
}
if micToggle.isOn {
recorder.isMicrophoneEnabled = true
} else {
recorder.isMicrophoneEnabled = false
}
recorder.startRecording{ [unowned self] (error) in
guard error == nil else {
print("There was an error starting the recording.")
return
}
print("Started Recording Successfully")
self.micToggle.isEnabled = false
self.recordButton.backgroundColor = UIColor.red
self.statusLabel.text = "Recording..."
self.statusLabel.textColor = UIColor.red
self.isRecording = true
}
}
停止、編輯及儲存
現在我們可以啟動錄影,但我們還需要停止錄影的功能。接著來完成 stopRecording()
功能吧。加入以下的程式碼到 startRecording()
函式底下:
func stopRecording() {
}
現在我們需要呼叫 stopRecording()
。加入以下程式碼到 stopRecording()
之中:
recorder.stopRecording { [unowned self] (preview, error) in
print("Stopped recording")
}
就這樣我們可以呼叫 stopRecording
來停止錄影。但是我們還可以在多加一些些東西…
如果我們可以加入一個很酷的編輯畫面讓使用者預覽他們的錄製影像、修剪影像而且不用離開 App 就可以傳送給好友,這樣的功能是不是棒呢?對我們來說很幸運的是,Apple 提供了 RPPreviewViewController
讓我們可以作到這些功能,而我們所需要做的就是呼叫它!
stopRecording
建有一個 preview 好讓我們可以在 handler closure 中呼叫。
首先我們想要確認 preview controller 是可以使用的。所以在 print("Stopped recording")
底下加入這個 guard:
guard preview != nil else {
print("Preview controller is not available.")
return
}
如果 preview controller 是無法使用的話,函式回中斷執行而免於 App 當掉!
接下來,我們想要添加一個 AlertView。如果玩家這回合玩的不好,不想給朋友們分享這段錄影的話,他們可以在停止錄影後直接刪掉錄影畫面,而不用進入預覽畫面看著剛剛慘烈的遊戲過程然後再按取消。
加入以下程式碼:
let alert = UIAlertController(title: "Recording Finished", message: "Would you like to edit or delete your recording?", preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { (action: UIAlertAction) in
self.recorder.discardRecording(handler: { () -> Void in
print("Recording suffessfully deleted.")
})
})
let editAction = UIAlertAction(title: "Edit", style: .default, handler: { (action: UIAlertAction) -> Void in
preview?.previewControllerDelegate = self
self.present(preview!, animated: true, completion: nil)
})
alert.addAction(editAction)
alert.addAction(deleteAction)
self.present(alert, animated: true, completion: nil)
self.isRecording = false
self.viewReset()
在你加入上面的程式碼後,我們回頭到 ViewController 宣告 Class 的地方。你需要添加 Preview Controller 的 delegate。在 UIViewController
的地方後面加個逗號(,) 然後輸入 RPPreviewViewControllerDelegate
那我們接下來要做什麼呢?
1: 我們要建立一個含有合適的標題、訊息、式樣的 UIAlertController。
2: 我們要建立一個刪除的 Action。如果呼叫了這個函式,它會丟棄錄製的影像,玩家就不必「重溫」那些慘烈的畫面() (我們做這個的真正好處!)
3: 我們要建立一個編輯 Action。如果被點擊,這個 Action 會開啟 Preview Controller,玩家就可以看到所錄製的內容,並且儲存到手機、編輯影像或者做任何他們想做的事情。
4: 我們為 UIAlertViewController 加入一個 Action 然後呈現這個 UIAlertView.
5: 我們要呼叫 viewReset()
函式,這樣視圖在 UIAlertView 消失後就會可以回復成原本的樣子。
stopRecording()
已經完成囉!整個函式應該會看起來像這樣:
func stopRecording() {
recorder.stopRecording { [unowned self] (preview, error) in
print("Stopped recording")
guard preview != nil else {
print("Preview controller is not available.")
return
}
let alert = UIAlertController(title: "Recording Finished", message: "Would you like to edit or delete your recording?", preferredStyle: .alert)
let deleteAction = UIAlertAction(title: "Delete", style: .destructive, handler: { (action: UIAlertAction) in
self.recorder.discardRecording(handler: { () -> Void in
print("Recording suffessfully deleted.")
})
})
let editAction = UIAlertAction(title: "Edit", style: .default, handler: { (action: UIAlertAction) -> Void in
preview?.previewControllerDelegate = self
self.present(preview!, animated: true, completion: nil)
})
alert.addAction(editAction)
alert.addAction(deleteAction)
self.present(alert, animated: true, completion: nil)
self.isRecording = false
self.viewReset()
}
}
我們差不多要完成這個專案了!我們只剩再加入一個函式。在 stopRecording()
之後輸入以下程式碼:
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
dismiss(animated: true)
}
這個簡短的 delegate 函式會在使用者點擊 cancel 按鈕時讓 Preview Controller 消失。
試試看吧!
現在是該把你的努力付諸實踐的時候了。在你的手機上執行 App,然後點擊綠色按鈕。你會注意到一個提示視窗彈跳出來要求你允許錄製螢幕或是允許錄製螢幕時開啟麥克風。這是 ReplayKit 中一個不錯的東西,因為 Apple 可以自動地讓 App 向使用者尋求授權,而我們不需要自己撰寫程式來完成這項功能,就像我們在 CoreLocation 及 Notification 中做的那樣。
點擊 Record Screen & Microphone。狀態 Label 應該會顯示 recording… 而且錄影按鈕應該也會變成紅色。你現在正在錄製你的螢幕!你可以操作 Segmented Control 來改變方塊的顏色。如果你開啟麥克風開關,然後說一些話,可能是唱首歌或任何你想說的東西 :)
當你玩的差不多的時候,再次點擊錄影按鈕來停止錄影。你現在應該會看到一個提示視窗詢問你希望對你的錄影做什麼。如果你唱了歌但覺得你的歌聲不好,或許你可以刪除錄影然後再錄製另外一部影像。然而,如果你想要炫耀你那出色的歌聲以及方塊的顏色變換,點擊 edit 按鈕。
現在你可以觀賞你的錄影畫面,也可以剪輯影像,或是儲存到你的相簿裡,或者向全世界分享這部影片。如果你不認為你的朋友們了解你錄製的是多麼驚奇的影像,那麼點擊左上的 Cancel 來離開畫面比且刪除影像。
小結
恭喜!你剛完成了一個棒極的螢幕錄影 App,而且這會讓你玩個好幾個小時…嗯,我想不會。雖然這個 App 做的東西不多,但他教會你所有對於用 ReplayKit 來添加一個螢幕錄影功能到你的遊戲之中需要用到的知識。現在就把螢幕錄影功能加進到你的遊戲裡吧。然後在底下留言,這樣我就會過去看看!
想知道更多關於 ReplayKit 的事情嗎?請繼續關注我的下一篇教學,它將會說明如何添加一個直播功能。一款能夠直播的遊戲,你怎能說它不棒呢?!
如果對這篇教學中有任何疑問或是有任何建議,儘管在底下留言而我會盡可能的幫你!
這篇教學的完整範例 App 可以在 GitHub 上取得。
更多關於 ReplayKit 程式庫的細節。可以參考 Apple 官方文件
你對 ReplayKit 看法是什麼呢?你是否想過加入一個螢幕錄影功能到你的 App 是如此簡單呢?歡迎留下你的意見。
原文:Introduction to ReplayKit: Building a Simple Screen Recording App