透過 delegate or data source 實現 App 功能的步驟說明
開發 iOS App 時,我們時常透過 delegate or data source,定義某個 protocol 的相關 function 實現功能。相關的動作可以簡單分為三個步驟:
- 定義型別 A 遵從 protocol。
- 在型別 A 裡定義和某個功能有關的 protocol function。
- 指定 data source or delegate 是型別 A 生成的東西。
只要熟練以上三個步驟,即可靈活運用 iOS SDK 裡各種 delegate 和 data source,實現我們想要的功能。接下來我們就以 UITextViewDelegate & QLPreviewControllerDataSource 的例子說明吧。
例子 1: 彼得潘因為小時候李白的詩背不出來被老師打,所以不喜歡李白,因此決定讓使用者無法在 App 的 text view 輸入李白。
為了實現此功能,一般我們會聯想是否有 function 能在使用者輸入每個字時觸發,然後在此 function 控制輸入的文字。經由 google 大神的幫忙,我們查到使用者輸入文字時, text view 將找 delegate 幫忙,觸發 protocol UITextViewDelegate 的 function textView(_:shouldChangeTextIn:replacementText:),因此我們可透過它控制使用者是否能輸入李白。接下來我們就照剛剛提到的三步驟實現吧。
1 讓 ViewController 遵從 protocol UITextViewDelegate。
我們時常用 view controller 當 delegate or data source,為了讓以下畫面的 controller 成為 text view 的 delegate,我們讓 class ViewController 遵從 protocol UITextViewDelegate。
class ViewController: UIViewController, UITextViewDelegate
遵從 protocol 也有另一種寫法,利用 extension 讓 ViewController 遵從 UITextViewDelegate,此方法可讓程式切得比較乾淨,讓 class ViewController { } 裡的程式碼不會太多。
extension ViewController: UITextViewDelegate {
}
2 定義 protocol UITextViewDelegate 的 function textView(_:shouldChangeTextIn:replacementText:)
此 function 在 protocol 的宣告如下。
optional func textView(_ textView: UITextView,
shouldChangeTextIn range: NSRange,
replacementText text: String) -> Bool
從以下連結的說明文字,我們明白此 function 將在使用者輸入或刪除文字時觸發,回傳 true 或 false 將控制文字是否能順利輸入或刪除。
了解此 function 的用途後,我們在 function 裡判斷加入使用者新輸入的文字 text 後,newText 是否包含可惡的李白,若有則回傳 false,不讓它輸入,其它則回傳 true。
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
var result = true
if let originalText = textView.text,
let range = Range(range, in: originalText) {
let newText = originalText.replacingCharacters(in: range, with: text)
if newText.contains("李白") {
result = false
}
}
return result
}
3 將 controller 指定為 text view 的 delegate。
我們可在 storyboard 從 text view 連到 view controller,然後將 controller 設為 text view 的 delegate。
想從程式指定 delegate 也可以。我們可連結 text view 的 outlet,然後在 class ViewController 的 viewDidLoad 設定。
class ViewController: UIViewController, UITextViewDelegate {
@IBOutlet var lyricsTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
lyricsTextView.delegate = self
}
結果
要輸入白時,因為會跟前面的字合成李白,所以不行 !
想當李壞倒是可以。
例子 2: 利用 QLPreviewController 顯示 PDF。
QLPreviewController 可以預覽 pdf,word,圖片等檔案,不過必須找 data source 幫忙,透過protocol QLPreviewControllerDataSource 的 function numberOfPreviewItems(in:) & previewController(_:previewItemAt:) 設定顯示的內容。
1 加入預覽的檔案。
為了學習 iOS App 開發,我們決定好好閱讀 Stanford 的 iOS 課程投影片,以下我們先將 pdf 檔 Lecture-1-Slides.pdf 加入專案。
2 讓 ViewController 遵從 protocol QLPreviewControllerDataSource。
protocol QLPreviewControllerDataSource 裡有些一定要定義的 function,沒定義會出現紅色錯誤,does not conform to protocol。
此時我們只要點選 Fix,Xcode 即可幫我們輸入。
3 定義 protocol QLPreviewControllerDataSource 的 function numberOfPreviewItems(in:) & previewController(_:previewItemAt:)
這兩個 function 控制 QLPreviewController 顯示的檔案數量和內容,相關說明可參考以下連結:
(1) 定義 function function numberOfPreviewItems(in:) 設定預覽的檔案數量。
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
我們只要預覽一份 pdf 檔,所以回傳 1。
(2) 定義 function previewController(_:previewItemAt:) 設定預覽的檔案。
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
if let url = Bundle.main.url(forResource: "Lecture-1-Slides", withExtension: "pdf") {
return url as QLPreviewItem
} else {
fatalError()
}
將 pdf 檔的 URL 回傳,表示此 pdf 檔為預覽的檔案。因為 URL 型別本來就遵從 protocol QLPreviewItem,所以我們可以利用 as 轉型回傳。(因為轉型一定成功,所以寫 as,而不是 as!)
對於 URL 遵從 protocol QLPreviewItem 有興趣的朋友,可參考以下連結的說明。
4 顯示 QLPreviewController, 將 view controller 指定為 QLPreviewController 的 dataSource。
@IBAction func showPdf(_ sender: Any) {
let controller = QLPreviewController()
controller.dataSource = self
present(controller, animated: true, completion: nil)
}
結果