Swift 程式語言

初探 ReplayKit:製作一個簡易螢幕錄製 App

初探 ReplayKit:製作一個簡易螢幕錄製 App
初探 ReplayKit:製作一個簡易螢幕錄製 App
In: Swift 程式語言

誰不愛在自己的 iPhone 上玩遊戲呢?我想我的朋友及家人會同意我花太多的時間在手機上玩遊戲。然而,最能夠表現出 iPhone 強大能力的東西之一就是這些聲光效果十足的遊戲了。

幾乎所有喜愛在 iPhone 上玩遊戲的人們都很喜歡向他們的朋友炫耀這些遊戲是多麽的好。而這就是為什麼 Apple 會在 WWDC 15 上發表 iOS 9 時,同時公布 ReplayKit 這支 API。 ReplayKit 是一支令人驚豔的 API,它可以讓開發者們以非常簡單的方式將螢幕錄製功能整合進 App 中。只要按下一個按鈕,玩家們就可以錄製遊戲畫面然後即時地分享給朋友們。先別急,我知道 iOS 11 新增了螢幕錄製功能讓你隨時隨地錄製螢幕畫面,但對玩家們來說使用上並不直覺,因為它需要開啟控制中心且要離開遊戲來編輯及傳送影片。而 ReplayKit 是專為玩家們所設計的 API。

在本次的教學中,我將會示範如何製作一個簡易螢幕錄製 App。這個 App 將會有一顆按鈕控制開始及停止錄製、一個 Label 用來顯示錄影的狀態以及一些可以操作的 UI 元件,因此你就可以錄製自己所做的事情。那麼,我們就開始吧!

註: 你將會在 Xcode 8 中使用 Swift3 來完成這篇教學。不過,這個範例 App 的程式碼在 Swift 3 及 Swift 4 中是相同的,所以應該可以在 Xcode 9 中運作。此外,你需要一台實體裝置來執行這個 App,因為螢幕錄製功能無法在模擬器上運作。

App 概覽

我們這次要製作的 App 會是非常好撰寫以及使用。首先會有一顆綠色按鈕用來開始螢幕錄製。一旦點擊,按鈕會變成紅色,而在螢幕上方的 Label 會顯示現在正在錄製。App 同時含有一個有多種顏色選項的 Segmented View。點擊其中一種顏色,螢幕中間的方框的背景色會跟著變換成那個顏色,這是讓你可以紀錄下螢幕的變化。此外,有一個麥克風的開關讓你可以選擇是否想要錄下麥克風的聲音。

Demo app for ReplayKit

建立專案

那麼我們開始製作吧!開啟 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 是如此簡單呢?歡迎留下你的意見。

譯者簡介:楊敦凱-目前於科技公司擔任 iOS Developer,工作之餘開發自有 iOS App同時關注網路上有趣的新玩意、話題及科技資訊。平時的興趣則是與自身專業無關的歷史、地理、棒球。來信請寄到:[email protected]

原文Introduction to ReplayKit: Building a Simple Screen Recording App

作者
Mitchell Sweet
擁有多年 iOS 程式開發經驗,自2013年開始,已開發了 12 個不同類型的 App。他曾獲 2016 及 2017 年度的 WWDC Scholarship 。Mitchell 現於羅切斯特理工學院(Rochester Institute of Technology)就讀,除熱愛研究 UI 設計外,也非常喜歡將自己的iOS開發經驗和大家分享。
評論
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。