Swift・iOS

Swiftを中心に学んだことを記録に残すブログです。技術に関係ない記事もたまに書いています。

【Swift】ページネーションを実装する(UICollectionView/API)

【Swift】UICollectionView/APIから画像URLを取得して画像を表示(https://www.hfoasi8fje3.work/entry/2019/08/22/224038)でAPIから画像取得後UICollectionViewに表示する実装についてまとめました。

今回は上記記事の続きとして、ページネーションを実装してみました。

 

※今回もPixabay APIを使用しています。

https://pixabay.com/ja/

 

■開発環境

Xcode 10.3

・Swift 5.0.1

 

■コード

import UIKit
import Nuke

struct Item: Codable {
    var hits: [Hits]
    struct Hits: Codable {
        var previewURL: String
        var tags: String
        var user: String
        var webformatURL: String
    }
}

class ViewController: UIViewController {
    
    @IBOutlet weak var pixabayCollectionView: UICollectionView!
    
    private var items: [Item.Hits] = []
    private let preheater = ImagePreheater()
    
    private var pageNo = 1
    private var isLoadingList = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.pixabayCollectionView.register(UINib(nibName: "pixabayCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "pixabayCollectionViewCell")
        
        self.pixabayCollectionView.delegate = self
        self.pixabayCollectionView.dataSource = self
        self.pixabayCollectionView.prefetchDataSource = self
        
        self.setUpPixabayItems()
        
        let refreshControl = UIRefreshControl()
        self.pixabayCollectionView.refreshControl = refreshControl
        refreshControl.addTarget(self, action: #selector(ViewController.refresh(sender:)), for: .valueChanged)
    }
    
    @objc func refresh(sender: UIRefreshControl) {
        // ページ番号を元に戻す
        self.pageNo = 1
        self.items = []
        self.setUpPixabayItems()
        sender.endRefreshing()
    }
    
    private func setUpPixabayItems() {
        self.getPixabayItems(pageNo: self.pageNo, completion: { (item) in
            self.pageNo += 1
            self.items.append(contentsOf: item.hits)
            DispatchQueue.main.async {
                self.isLoadingList = false
                self.pixabayCollectionView.reloadData()
            }
        })
    }
    
    private func getPixabayItems(pageNo: Int, completion: @escaping (Item) -> ()) {
        let url = URL(string: "https://pixabay.com/api/")!
        var components = URLComponents(url: url, resolvingAgainstBaseURL: false)
        components?.queryItems = [URLQueryItem(name: "key", value: "{APIKey}")] + [URLQueryItem(name: "page", value: "\(pageNo)")] + [URLQueryItem(name: "per_page", value: "100")] + [URLQueryItem(name: "q", value: "sea")] + [URLQueryItem(name: "image_type", value: "photo")]
        let queryStringAddedUrl = components?.url
        
        if let url = queryStringAddedUrl {
            let task = URLSession.shared.dataTask(with: url) { data, response, error in
                if let data = data {
                    
                    do {
                        let decoder = JSONDecoder()
                        let items = try decoder.decode(Item.self, from: data)
                        completion(items)
                    } catch {
                        print("Serialize Error")
                    }
                } else {
                    print(error ?? "Error")
                }
            }
            task.resume()
        }
    }
}

extension ViewController: UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        // isLoadingListがfalseの時だけsetUpPixabayItemsを実行する
        if (((scrollView.contentOffset.y + scrollView.frame.size.height) > scrollView.contentSize.height) && !self.isLoadingList) {
            // setUpPixabayItemsを実行している間に追加でsetUpPixabayItemsを実行しないようにisLoadingListをtrueに変更
            self.isLoadingList = true
            self.setUpPixabayItems()
        }
    }
}

// セル選択時の処理
extension ViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if let url = URL(string: self.items[indexPath.row].webformatURL) {
            UIApplication.shared.open(url)
        }
    }
}

// セルの大きさ
extension ViewController:  UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        let numberOfCell: CGFloat = 3
        let cellWidth = UIScreen.main.bounds.size.width  / numberOfCell - 6
        return CGSize(width: cellWidth, height: 160)
    }
}

extension ViewController: UICollectionViewDataSource {
    // セルの数
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.items.count
    }
    
    // セルの設定
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "pixabayCollectionViewCell", for: indexPath) as! pixabayCollectionViewCell
        
        let item = self.items[indexPath.row]
        let previewUrl = URL(string: item.previewURL)!
        Nuke.loadImage(with: previewUrl, into: cell.pixabayImageView)
        cell.imageTagLabel.text = item.tags
        cell.imageCreatorNameLabel.text = item.user
        
        return cell
    }
}

extension ViewController: UICollectionViewDataSourcePrefetching {
    func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
        let urls = indexPaths.map { URL(string: self.items[$0.row].previewURL) }
        self.preheater.startPreheating(with: urls as! [URL])
    }
    
    func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) {
        let urls = indexPaths.map { URL(string: self.items[$0.row].previewURL) }
        self.preheater.stopPreheating(with: urls as! [URL])
    }
}

 

■参考リンク

https://pixabay.com/api/docs/

https://stackoverflow.com/questions/35367496/swift-tableview-pagination

https://qiita.com/abouch/items/aca979b71ad792478687