透過 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 的宣告如下

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)
}

結果

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com