#37 隨機圖片API &進階版 UIPickerVier

繼上一回的作業練習了串接文字API及UIPickerView基礎練習;

這次練習串接圖片API,共有兩種方式:

1. 用一般 coding 顯示API圖片
2. 用第三方套件 kingfisher 顯示API圖片

另外,也包含了進階題UIPickerView

1. 點選畫面後顯示 Picker View

2. Picker View 的上方加入包含完成 & 取消 button 的 bar,按下完成時更新畫面的內容

3. 顯示多欄的 Picker View,第二欄的 picker view 隨著第一欄而變化

其他功能

等待畫面時,顯示 UIActivityIndicatorView

等待畫面時,顯示預設圖片

APP畫面:

左:起始畫面;中:loading畫面;右:呈現API影像

UIPikerView

基礎版UIPickerView可以參考上一篇:

這裡說明進階版不同的部分:

點選畫面後,UIPickerView才會出現,並搭配著按鈕取消選取

新增UIToolbar在畫面外(放在exit底下),並拉好 IBOutlet;
UIPickerView 也是一樣的方式先新增跟拉好

UIToolbar
放在exit底下

再新增一個TextField放在View裡,在 func viewDidLoad() 裡利用程式碼

optionTextField.inputView = optionPickerView
optionTextField.inputAccessoryView = optionToolBar

讓UIPickerView跟UIToolbar取代觸發時會跳出的鍵盤

用 .becomeFirstResponder( ) 去觸發 TextField

optionTextField.becomeFirstResponder()

這裡是用點兩下畫面觸發顯示UIPickerView跟UIToolbar,所以寫在 UITapGestureRecognizer 的 function 裡

    func tapToSelect() {
let tap = UITapGestureRecognizer()
tap.numberOfTapsRequired = 2
tap.addTarget(self, action: #selector(showPickerVier))
pictureImageView.isUserInteractionEnabled = true
pictureImageView.addGestureRecognizer(tap)

}

@objc func showPickerVier() {
optionTextField.becomeFirstResponder()
optionTextField.isHidden = true
}

關於 UITapGestureRecognizer 也可以參考上一篇

仔細觀察,Toolbar 中間其實有三個 item

中間的item,Fixed Space Bar Button Item 是固定兩個item之間的距離

記得 Cancel Button 跟 Get Image Button 也要拉 IBAction,去定義按下時要做的事。例如:收鍵盤

view.endEditing(true)

UIPickerView 裡的資料設定

第一個欄位(component = 0)選取的資料不會出現在第二個欄位(component = 1),也就是不會選到一樣的選項

PickerView不重複選取

撰寫程式的概念是:
在 ViewController 裡宣告兩組 array,其中一組包含所有資料,另一組是空 array,搭配移除 array 資料( .remove(at: ) ),與存入資料去改變第二組欄位

        if component == 0 {
secondOptions = firstOptions
let firstSelected = pickerView.selectedRow(inComponent: 0)
secondOptions.remove(at: firstSelected)
pickerView.selectRow(2, inComponent: 1, animated: true)
pickerView.reloadComponent(1)
} else if component == 1 {
pickerView.reloadComponent(1)
}

特別注意,因為第二組欄位會因第一組欄位而改變,所以也必須被 reload,不然第二組欄位選取的資料會不正確。

串接API,顯示選取PickerView後的畫面

試了兩種方式串接API來顯示圖片

使用一般coding
使用第三方套件(kingfisher)

APP GIF呈現比較:

左圖:一般coding;右圖:kingfisher

使用起來幾乎一樣,但用一般 coding 撰寫程式的可以一直按 GetImage 去取得同樣條件下不同的圖片;但用kingfisher的,在同樣條件下,會一直呈現同樣的照片,仔細翻閱文件可以發現原因

from kingfisher documents
from kingfisher documents

它抓過的資料都會暫存起來,讓程式更有效率

一般coding的串接API程式碼:

    func fetchImage() {
processIndicator.startAnimating()
optionTextField.isHidden = true
pictureImageView.image = UIImage(systemName: "sailboat")
if let randomURL = URL(string: "https://loremflickr.com/350/400/\(firstOption),\(secondOption)/all") {
print(randomURL.description)
URLSession.shared.dataTask(with: randomURL) { data, response, error in
if let data {
let image = UIImage(data: data)
DispatchQueue.main.async {

self.pictureImageView.image = image
self.pictureImageView.contentMode = .scaleAspectFill
self.processIndicator.stopAnimating()
}
}
}.resume()
}
}

用kingfisher串接API的程式碼:

    func fetchImage() {
optionTextField.isHidden = true
if let randomURL = URL(string: "https://loremflickr.com/350/400/\(firstOption),\(secondOption)/all") {
print(randomURL.description)

//顯示未知進度
pictureImageView.kf.indicatorType = .activity
//等待載入影像時,顯示placeholder設定的圖示
let processorRoundCorner = RoundCornerImageProcessor(cornerRadius: 15)
//在影像上覆蓋一層自訂顏色的透明色調
let processorOverLay = OverlayImageProcessor(overlay: .systemOrange)

pictureImageView.kf.setImage(
with: randomURL,
placeholder: UIImage(systemName: "sailboat"),
options: [
.processor(processorRoundCorner),
.processor(processorOverLay)
])
}
}

kingfisher的文件寫得非常清楚,還搭配範例,很適合初學者練習第三方程式與閱讀其文件。

UIActivityIndicatorView

有時網路比較慢或是在載入較大檔案時會花上幾秒時間,可以顯示 UIActivityIndicatorView 去讓使用者知道現在APP的狀態。

關鍵的三行程式碼:

開始運作:

processIndicator.startAnimating()

停止運作:

processIndicator.stopAnimating()

停止運作時自動隱藏:

processIndicator.hidesWhenStopped = true

--

--