Swift4 UITableVIewで無限スクロールと下に引っ張って更新する機能の実装

UITableViewで無限スクロールと画面最上部で下に引っ張ってローディング画像を表示させ更新させる方法を探して調べた結果をメモ

準備するもの
jsonファイル複数、今回はuser1.json user2.jsonを用意。この辺は本来apiから取得する形でいいと思います。

json構造

[
    {
        "id": 1,
        "name": "aaao",
    },
    {
        "id": 2,
        "name": "bbb",
    },
    {
        "id": 3,
        "name": "ccc",
    },
    {
        "id": 4,
        "name": "ddd",
    },
    {
        "id": 5,
        "name": "eee",
    },
    {
        "id": 6,
        "name": "Bob",
    },
    {
        "id": 7,
        "name": "Alice",
    },
    {
        "id": 8,
        "name": "Bob",
    },
    {
        "id": 9,
        "name": "Alice",
    },
    {
        "id": 10,
        "name": "Bob",
    },
    {
        "id": 11,
        "name": "Alice",
    },
    {
        "id": 12,
        "name": "Bob",
    },
    {
        "id": 13,
        "name": "Alice",
    },
    {
        "id": 14,
        "name": "Bob",
    },
    {
        "id": 15,
        "name": "ee",
    },
    {
        "id": 16,
        "name": "aaa",
    },
    {
        "id": 17,
        "name": "hhh",
    },
    {
        "id": 18,
        "name": "ggg",
    },
    {
        "id": 19,
        "name": "fff",
    },
    {
        "id": 20,
        "name": "eeeee",
    }
]

LoaddingTableViewCell.swift

import UIKit

class LoaddingTableViewCell: UITableViewCell {

    
    @IBOutlet weak var IndicatorView: UIActivityIndicatorView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
    
    func startAnimating(){
        IndicatorView.startAnimating()
    }
}

LoaddingTableViewCell.xib はLoaddingTableViewCell.swiftと関連付け

xibの作り方については
https://qiita.com/naochi___/items/dcbf58acb925fc716af5
こちらを参考にした

xibのattributes inspectorのIdentifierにLoaddingTableViewCellと記述しLoaddingTableViewCellで呼び出せるようにする

ViewController.swift

import UIKit

struct User: Codable {
    var id: Int
    var name: String
}

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var users: [User] = [User]()
    var offset: Int = 1
    var isaddload:Bool = true
    let ViewFrame = UIScreen.main.bounds
    
    @IBOutlet weak var tableView: UITableView!
    
    fileprivate let refreshCtl = UIRefreshControl()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        users.removeAll()
        
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: #selector(ViewController.refreshControlValueChanged(sender:)), for: .valueChanged)
        self.tableView.addSubview(refreshControl)
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")

        
        self.createLoadingCell()
        loadJsonFile(1)

    }
    
    @objc func refreshControlValueChanged(sender: UIRefreshControl) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
            self.users.removeAll()
            
            self.createLoadingCell()
            
            self.loadJsonFile(1)
            self.isaddload = true
            self.tableView.reloadData()
            sender.endRefreshing()
        })
    }

    func createLoadingCell() -> Void {
        
        self.tableView.register(cellType: LoaddingTableViewCell.self) // extensionにて拡張
        let footerCell: UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "LoaddingTableViewCell")!
        
        (footerCell as! LoaddingTableViewCell).startAnimating()
        let footerView: UIView = footerCell.contentView
        self.tableView.tableFooterView = footerView
        
        self.tableView.frame = self.ViewFrame
        self.view.addSubview(self.tableView)
    }
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.users.count
    }
    


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // セルを取得する
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        // セルに表示する値を設定する
        cell.textLabel!.text = "\(self.users[indexPath.row].name)"
        return cell
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if (self.tableView.contentOffset.y + self.tableView.frame.size.height > self.tableView.contentSize.height && self.tableView.isDragging && isaddload == true){
            
            self.isaddload = false
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                self.loadJsonFile(2)
                self.tableView.tableFooterView = UIView()
                self.tableView.reloadData()
            }
        }
    }
    
    //データの読み込みは通常はapiから行われると思うのでその辺の書き換えが必要
    func loadJsonFile(_ offset: Int) {
        guard let path = Bundle.main.path(forResource: "users" + "\(offset)", ofType: "json") else {
            return
        }
        let url = URL(fileURLWithPath: path)

        do {
            let data = try Data(contentsOf: url)
            let users = try JSONDecoder().decode([User].self, from: data)
            
            if offset == 1 {
                self.users = users
            } else {
                self.users += users
            }
            
            
        } catch  {
            print(error)
        }
    }
}

Main.storyboardとデリゲートはこんな感じ

UITableViewExtension.swift

import UIKit

extension UITableView {
    func register(cellType: T.Type) {
        let className = cellType.className
        let nib = UINib(nibName: className, bundle: nil)
        register(nib, forCellReuseIdentifier: className)
    }
    
    func register(cellTypes: [T.Type]) {
        cellTypes.forEach { register(cellType: $0) }
    }
    
    func dequeueReusableCell(with type: T.Type, for indexPath: IndexPath) -> T {
        return self.dequeueReusableCell(withIdentifier: type.className, for: indexPath) as! T
    }
}

NSObjectExtension.swift

import UIKit

extension NSObject{
    class var className: String {
        return String(describing: self)
    }
    
    var className: String {
        return type(of: self).className
    }
    
}

参考リンク
https://qiita.com/sachiko-kame/items/99d240469294e0663c2d
https://qiita.com/ryo-ta/items/7e2fbedb6e8dc8eb217f
http://www.cl9.info/entry/2017/04/02/200440

シェアする

  • このエントリーをはてなブックマークに追加

フォローする