環境: Swift3
よく使うのでコピペ用にメモしておく。
ViewController
import UIKit
class ViewController: UIViewController {
var items: [String] = ["foo", "bar", "hoge"]
@IBOutlet weak var tableView: UITableView?
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
cell.item = self.items[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 60
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}
final class MyCell: UITableViewCell {
var item: String? {
didSet {
self.nameLabel?.text = self.item
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
@IBOutlet weak var nameLabel: UILabel?
}
カスタムセルMyCell
を定義する。
Storyboard 上でカスタムセルを貼り付けて定義している場合は以下のような register メソッドは不要。
逆にregister メソッドで登録してしまうとカスタムセルが表示されなくなってしまうので注意。
カスタムセルをコードのみで定義、または別途 nib ファイルで定義した場合は viewDidLoad などで以下のように register メソッドで登録する。
self.tableView?.register(MyCell.self, forCellReuseIdentifier: "MyCell")
self.tableView?.register(UINib(nibName: "MyCell", bundle: nil), forCellReuseIdentifier: "MyCell")
また、普通やらないと思うが、カスタムセルを使う場合に UITableViewCell にデフォルトで用意されているプロパティ(textLabel
など)を使うと表示がおかしくなるのでやらないこと。
Storyboard
UIViewController に UITableView と UITableViewCell を貼り付ける。
UITableView の datasource と delegate を UIViewController に接続する。
UITableViewCell のクラス名を設定、Identifier を設定。
UITableViewCell 上にラベルなどがあればそれも接続する。
Extension
Cell・HeaderFooterViewのregisterやdequeueのIdentifierは文字列で扱うが、大抵はクラス名をそのまま使用するので、文字列ではなく、クラスを使って扱えるようにExtensionを定義する。
定義
extension UITableView {
func dequeueReusableCell<T: UITableViewCell>(withClass type: T.Type, for indexPath: IndexPath) -> T {
return self.dequeueReusableCell(withIdentifier: String(describing: type), for: indexPath) as! T
}
func dequeueReusableHeaderFooterView<T: UITableViewHeaderFooterView>(withClass type: T.Type) -> T {
return self.dequeueReusableHeaderFooterView(withIdentifier: String(describing: type)) as! T
}
func register(tableViewCellClass cellClass: AnyClass) {
let className = String(describing: cellClass)
if UINib.fileExists(nibName: className) {
self.register(UINib.cachedNib(nibName: className), forCellReuseIdentifier: className)
} else {
self.register(cellClass, forCellReuseIdentifier: className)
}
}
func register(headerFooterViewClass aClass: AnyClass) {
let className = String(describing: aClass)
if UINib.fileExists(nibName: className) {
self.register(UINib.cachedNib(nibName: className), forHeaderFooterViewReuseIdentifier: className)
} else {
self.register(aClass, forHeaderFooterViewReuseIdentifier: className)
}
}
}
extension UINib {
static let nibCache = NSCache<NSString, UINib>()
static func fileExists(nibName: String) -> Bool {
return Bundle.main.path(forResource: nibName, ofType: "nib") != nil
}
static func cachedNib(nibName: String) -> UINib {
if let nib = self.nibCache.object(forKey: nibName as NSString) {
return nib
} else {
let nib = UINib(nibName: nibName, bundle: nil)
self.nibCache.setObject(nib, forKey: nibName as NSString)
return nib
}
}
}
使用時
tableView.register(tableViewCellClass: MyCell.self)
let cell = tableView.dequeueReusableCell(withClass: MyCell.self, for: indexPath)
tableView.register(headerFooterViewClass: MyHeaderView.self)
let view = tableView.dequeueReusableHeaderFooterView(withClass: MyHeaderView.self)