前提
Xcode9.2
Swift4
問題
列ごとに表示するセルを決めるデリゲートメソッドを書いて、その列にどのセルを使うかというのを決めると思います
extension ListViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(ListTableViewCell.self), for: indexPath) as? ListTableViewCell else {
fatalError("The dequeued cell is not instance of MealTableViewCell.")
}
///ここ以下省略
ここで落ちてしまう
guard let cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(ListTableViewCell.self), for: indexPath) as? ListTableViewCell
エラーメッセージ
reason: 'unable to dequeue a cell with identifier Todolist_firebase.MyCell -
must register a nib or a class for the identifier or connect a prototype cell in a storyboard'
原因と解決策
下記の入れ忘れ
tableView.register(ListTableViewCell.self, forCellReuseIdentifier: NSStringFromClass(ListTableViewCell.self))
register(_,identifier)のApple公式ドキュメントではここで紹介されています
func register(_ cellClass: AnyClass?,
forCellReuseIdentifier identifier: String)
セルを作成する前に、このメソッド(_:forCellReuseIdentifier :)を呼び出して、新しいセルをつくることをテーブルビューに伝えます。 指定されたタイプのセルが現在再利用キューにない場合、テーブルビューは提供された情報を使用して新しいセルオブジェクトを自動的に作成します。
つまり第一引数にはこのテーブルビューで使うcellのクラスを登録し、第二引数では再利用する際の名札的な立ち位置でString型の名前を入れてねということなのでしょう
dequeueReusableCell(withIdentifier:for:)のアップル公式のドキュメント
再利用可能なテーブルビューセルオブジェクトを返し、それをテーブルに追加する関数ですね
この関数は第一引数がString型のidentifierで第二引数がindexPathなので何番目のセルを対象としているのかという数字が返ってきます
第一引数に着目すると型はString型になっており、上記のregister関数でstringで登録したものを再利用できるようにしているのですね
func dequeueReusableCell(withIdentifier identifier: String,
for indexPath: IndexPath) -> UITableViewCell
公式ドキュメントではわざわざ黄色く囲まれたImportantのところで
重要
このメソッドを呼び出す前に、register(_:forCellReuseIdentifier :)またはregister(_:forCellReuseIdentifier :)メソッドを使用してクラスまたはnibファイルを登録する必要があります。
と書いてありました
コード全体
一部省略してます
ちなみに何故register
関数の第二引数でNSStringFromClass(ListTableViewCell.self)
このようにしているかというと、NSStringFromClass
関数は引数で入れたクラスをStringにして返してくれる関数だからです
例
NSStringFromClass(ListTableViewCell.self)
↓
"Todolist_firebase.ListTableViewCell"
class ListViewController: UIViewController {
var tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
//テーブルビュー
tableView = UITableView(frame: self.view.frame, style: .plain)
tableView.rowHeight = 100
tableView.delegate = self
tableView.dataSource = self
//NSStrignFromClassはクラスの名前をStringで返してくれる
tableView.register(ListTableViewCell.self, forCellReuseIdentifier: NSStringFromClass(ListTableViewCell.self))
self.view.addSubview(tableView)
}
}
extension ListViewController: UITableViewDelegate, UITableViewDataSource {
//セクションの数を設定
func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
//行数 セクションによって行数が違う場合はsectionで場合分けをするUITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
//列ごとに表示するセルを決めるデリゲートメソッド
//UITableViewDataSource は主に Table View が表示するデータを与えるものです。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: NSStringFromClass(ListTableViewCell.self), for: indexPath) as? ListTableViewCell else {
fatalError("The dequeued cell is not instance of MealTableViewCell.")
}
let item = items[indexPath.row]
cell.nameLabel.text = item.title
return cell
}
}
class ListTableViewCell: UITableViewCell {
//MARK: Properties
var nameLabel: UILabel!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
}
override func layoutSubviews() {
super.layoutSubviews()
self.addSubview(nameLabel)
nameLabel.frame = CGRect(x: 110, y: 0, width: frame.width - 100, height: frame.height)
}
//MARK : method
private func commonInit() {
self.createNameLabel()
}
private func createNameLabel() {
nameLabel = UILabel(frame: CGRect.zero)
nameLabel.textAlignment = .left
nameLabel.font = UIFont.systemFont(ofSize: 20)
}
}
おまけ
Appleのドキュメント曰く
指定した識別子のクラスを登録し、新しいセルを作成する必要がある場合、init(style:reuseIdentifier :)メソッドを呼び出してセルを初期化します。
既存のセルが再利用可能な場合、このメソッドは代わりにセルのprepareForReuse()メソッドを呼び出します。
とのことで、prepareForReuse()
関数がいつ使われるのかがドキュメントを確認することでわかりました