#31 Delegate practice — Photo Editor app

難產的第 31 篇 zzz,今天來記錄一下 Delegate 的觀念,並且用一個簡單的 Photo Editor app 來實際練習一下。為什麼說難產,因為真的好抽象,看了好多人分享 Delegate 的觀念,起初真的是,有看沒有懂。先來看看這次的 App 畫面吧!

Target

  1. 練習使用 UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIColorPickerViewControllerDelegate。
  2. 熟悉 Delegate 使用規則,並使用 Extension 讓 code 變得比較易讀。
  3. 將 View 輸出成 Image 並儲存在 iPhone。

Process

這次要記錄的有以下三個重點,其他改圖片比例或是增加文字就不另外做紀錄,大家如果有需要可以直接拉到文末的 Git hub 連結去下載整包的 code ~

  1. 如何叫出相簿,且如何確認使用者所選的照片,並將照片傳回原本主頁的 ImageView 顯示呢?
  2. 同上,如何叫出顏色選單,且如何確認使用者所選的顏色,並將顏色傳回主頁上更改 View 的 back ground 以及 button 的 back ground 呢?
  3. 如何將 View 轉換成 Image,並儲存照片到 iPhone?

UIImagePickerControllerDelegate & UINavigationControllerDelegate

這次使用到的功能需要靠上面兩個 Protocal 來完成,在講什麼是 Delegate 之前我們要先知道 Protocol 。

Delegate,中文就是代理的意思,意思是我請某個人(元件) 來替我做事情,當然,不是隨便的人(元件) 都可以當我的代理人,這個代理人必須要 follow 我的規則 (Protocol) 來替我做這件事情。

比較詳細的解說可以參考底下彼得潘以及法蘭克兩位大大的教學文。

彼得潘說過,在使用 Delegate 時,基本上是 follow 底下三個步驟:

  1. 定義型別 A 遵從 protocol. (請 A Follow 被代理人的原則)
  2. 在型別 A 裡定義 protocol 的 function. (被代理人要求代理人要做什麼事)
  3. 指定 data source or delegate 是型別 A 生成的東⻄,也就是指定型別 A 的東⻄是執行 function 的人. (指定誰來當代理人)

我們來看看底下實際例子:

上面這段 code 是我 app 裡面,中間的 add button ,也就是點擊後叫出 Alert ,再由 Alert 叫出 imagePickerController,我要利用 imagePickerController 的 sourceType 中的 .photoLibrary 叫出相簿來。

再指定代理人 imagePickerController.delegate = self 來將使用者所選取的照片傳回主頁當作 imageView 的 image,這裡的 self 指的就是 ViewController。

但當你照著打的時候就會發現紅色驚嘆號跑了出來

Error: Cannot assign value of type ‘ViewController’ to type ‘(UIImagePickerControllerDelegate & UINavigationControllerDelegate)?’

請回去看看彼得潘的三大口訣,再指定代理人之前,我們要先要求代理人 follow 被代理人的原則,也就是之前提到的 Protocol,所以我們要請 ViewController 遵循它所指定的 protocol,也就是 UIImagePickerControllerDelegate 和 UINavigationControllerDelegate。

該怎麼做呢?你可以直接寫在 ViewController 裡面的,但這裡要特別提一下,如果以後我們要遵循的 protocol 越來越多,然後我們還要在裡面寫要請代理人做的事 (function),那我們的 class ViewController 就會變得越來越肥也越來越凌亂。這時候先跳 tone 來介紹一下 extension。

Extension

Extension 的概念很簡單,就是擴充,我要我的 class ViewController 遵循其他的 protocol,可以另開一個 swift file 來寫,並且把需要代理人做的事情都寫在裡面,這樣就不會跟原本的 code 混雜在一起。

回到 delegate,當我們讓 ViewController 遵循 UIImagePickerControllerDelegate, UINavigationControllerDelegate 後還不夠,再來我們要指定這位代理人應該要做哪些事情。但這裡一定會有個疑問,我怎麼知道成為代理人可以做哪些事情?

我們可以按住 cmd 點選 UIImagePickerControllerDelegate,進入 Jump to Definition。

可以看到 protocol 裡有兩個 optional func,這邊也特別記錄一下,成為代理人當然勢必得負起一些責任來,也就是代理人一定要做的事情(不然我請一個代理人幹嘛?),所以 protocol 裡面的 func 是必須要寫出來執行的,但當然也有例外,上面就是個例子。

在 protocol 裡面的 func,只要是前面加上 optional ,就是不一定要執行的,有需要再寫即可。

我們需要代理人幫我們將挑選後的圖片設定在主頁的 imageView,看起來符合第一個 optional func imagePickerController 中紅框的敘述。

在紅框中,其中有一段看起來有點陌生,就是 info: [UIImagePickerController.InfoKey : Any] ,這裡是一個 dictionary 的寫法。

如果看不懂或不知道該怎麼用的時候,一樣 cmd 按住點選這個 InfoKey,再點選 Jump to Definition。

我們要的是使用者所選的照片,看起來應該是 .originalImage ,所以 func 裡面我是這樣寫的。

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {photoImageView.image = info[.originalImage] as? UIImagedismiss(animated: true, completion: nil)}

這邊也再多紀錄一下,as? 是轉型的用法,info[.originalImage] 所生出來的東西型別是 Any,我們要將它轉為 UIImage 才可以使用在我們主頁的 photoImageView.image。到這邊基本上已經完成彼得潘所說的三大原則了。

至於 UIColorPickerViewController 也就是依樣畫葫蘆了我把 swift file 裡的extension code 和 changeViewBackgroundColor 的 button code 另外貼在下方供大家參考。

再來就是將 View 轉成 Image 並儲存了,這邊請直接參考彼得潘底下的教學文,寫得非常詳盡。

最後的最後,當你成功照著彼得大大的方式做出來後,別忘了在 project info 頁上新增權限,這樣你的 App 才不會在儲存照片的時候閃退喔~

文末最後提供幾位學長姐的作品,感謝各位巨人前輩~

Free talk

老實說,Delegate 的觀念真的對初學者來說不太友善,只能多看看前輩們的作品多多學習,畢竟,學一次不會?就學兩次吧!

你說學兩次還是不會?那就學三四五六七八次,天底下哪有學不會的事?😂

--

--