iOS| #17 | 利用Lorem Picsum API 回傳的 JSON製作照片牆App

Tommy
彼得潘的 Swift iOS / Flutter App 開發教室
11 min readDec 24, 2021

這一次將透過呼叫Lorem Picsum的API回傳JSON來做出照片牆App。

為了熟悉Call API、JSON解碼與CollectionView,這次要做個精簡版的照片牆App。畢竟人類的大腦是有極限的,往後日子萬一忘記了還可以回來參考這支範例程式與文章。

技術應用

  1. 呼叫API解析回傳的JSON檔
  2. 使用第三方套件Kingfisher
  3. Collection View

成果展示

Model設計

  • 根據JSON格式建立相對應的struct

格式在以下網站可得到:

建立struct PhotoResponse並且遵從Codable(為了要解析接回來的JSON檔案),再將需要的欄位宣告成屬性即可。

回傳圖片為多個,所以先準備好裝的容器。

功能解說&程式解說

  • Call API解析回傳的JSON資料

在viewDidLoad()時準備call api抓圖片

func fetchPhotos(){if let url = URL(string: "https://picsum.photos/v2/list?page=2&limit=100"){URLSession.shared.dataTask(with: url) { data, response, error inif let data = data,let httpResponse = response as? HTTPURLResponse,httpResponse.statusCode == 200{do {let decoder = JSONDecoder()self.photos = try decoder.decode([PhotoResponse].self, from: data)//丟到主執行緒作業DispatchQueue.main.async {self.collectionView.reloadData()}} catch  {print(error)}}else{print(error)}}.resume()}}

首先使用optional binding拿到url。

if let url = URL(string: "https://picsum.photos/v2/list?page=2&limit=100"){}

建立URLSession準備連網路抓資料,記得要使用resume()才會真的執行任務,否則不會有任何動靜。

URLSession.shared.dataTask(with: url).resume()

使用optional binding確認有拿到data、httpResponse.statusCode 為 200(代表連網路OK)、error 為nil(沒有錯誤)。這邊的寫法比較嚴謹一些,畢竟是作為範例程式,養成好習慣要從小地方開始著手。

if let data = data,let httpResponse = response as? HTTPURLResponse,httpResponse.statusCode == 200,error == nil{ }

使用do try catch將解碼的程式包起來,因為decode可能會拋出錯誤。

let decoder = JSONDecoder():負責解碼的物件。
self.photos = try decoder.decode([PhotoResponse].self, from: data):將data解碼,格式為[PhotoResponse],並且放入屬性self.photos中。
DispatchQueue.main.async { }:將大括號內的程式丟到主執行序裡面做事,通常跟UI畫面有關的事情都要丟到主執行序做。
self.collectionView.reloadData():刷新collectionView的資料與畫面。
print(error):如果try decoder.decode([PhotoResponse].self, from: data)失敗就會跑到catch裡面印出錯誤。

do {let decoder = JSONDecoder()self.photos = try decoder.decode([PhotoResponse].self, from: data)//丟到主執行緒作業DispatchQueue.main.async {self.collectionView.reloadData()}} catch  {print(error)}
  • CollectionView
    以 UICollectionViewFlowLayout itemSize 設定 cell 大小

參考Peter大的公式如下:

func configureCellSize() {let itemSpace: CGFloat = 3let columnCount: CGFloat = 3let flowLayout = collectionViewLayout as? UICollectionViewFlowLayoutlet width = floor((collectionView.bounds.width - itemSpace * (columnCount-1)) / columnCount)flowLayout?.itemSize = CGSize(width: width, height: width)flowLayout?.estimatedItemSize = .zeroflowLayout?.minimumInteritemSpacing = itemSpaceflowLayout?.minimumLineSpacing = itemSpace}

itemSpace:item之間的空格多寡
columnCount:一列的item有幾個(此例為3個)
collectionViewLayout as? UICollectionViewFlowLayout:在 Interface Builder將它設為 flow layout,因此我們可將它轉型為UICollectionViewFlowLayout,之後再透過它的 property 設定 cell 的大小和間距。
width:計算出一個item的寬度;公式:(螢幕寬度-(一列的圖片數 -1) * 空格size ) / 一列的圖片數。
flowLayout?.itemSize:item的尺寸。
flowLayout?.estimatedItemSize:在這邊將其設為零,Cell item才會依照itemSize顯示尺寸,否則會依照auto layout自動調整。
flowLayout?.minimumInteritemSpacing:設定Min Spacing For Cells。
flowLayout?.minimumLineSpacing:設定Min Spacing For Lines。

  • 使用第三方套件Kingfisher設定CollectionViewCell的ImageView
由於我們已經加進來了所以這邊會反灰
import Kingfisher

cell.photoImageView.kf.setImage:使用Kingfisher中的函式setImage將圖片放到photoImageView中。
with:圖片的url。
placeholder:暫時未載入的預設圖片。

補充:尚未載入的圖片也可以讓它動起來:

重點在於傳入placeholder的參數要設為可輪播的UIImage。

可以參考利用 UIImageView 實現多張圖片連續播放的動畫

程式碼如下:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "\(PhotoCollectionViewCell.self)", for: indexPath) as! PhotoCollectionViewCelllet photo = photos[indexPath.row]let image = UIImage.animatedImageNamed("dog-", duration: 3)cell.photoImageView.kf.setImage(with: photo.download_url,placeholder: image )return cell}
  • 點擊CollectionViewCell圖片顯示大圖

先拉好Segue。

給identifier。

在下一頁的Controller宣告好要承接上一頁的屬性與建構器。

在上一頁拉好@IBSegueAction並撰寫程式。

collectionView.indexPathsForSelectedItems?.first?.row:取得在collectionView中目前被選到的項目。

實作func collectionView(_ collectionView: , didSelectItemAt indexPath: )在點擊CollectionViewCell的圖片當下會自動Call此方法。

performSegue(withIdentifier: identifier, sender: nil):call此段會自動執行@IBSegueAction,將畫面帶到下一頁。

若內容有誤煩請指教,感謝收看。

--

--