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