iOS| #17 | 利用Lorem Picsum API 回傳的 JSON製作照片牆App
這一次將透過呼叫Lorem Picsum的API回傳JSON來做出照片牆App。
為了熟悉Call API、JSON解碼與CollectionView,這次要做個精簡版的照片牆App。畢竟人類的大腦是有極限的,往後日子萬一忘記了還可以回來參考這支範例程式與文章。
技術應用
- 呼叫API解析回傳的JSON檔
- 使用第三方套件Kingfisher
- 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,將畫面帶到下一頁。
若內容有誤煩請指教,感謝收看。