- 某勉強会 で 教わった内容 の 復習用まとめ
- 講師
- 馬谷 さん
- 自分で定義した TableViewCell クラス (
SimpleTableViewCell
) を TableView 上に 表示させる- (前回:2018-08-25 は 既存の
UITableViewCell
クラス を 利用した)
- (前回:2018-08-25 は 既存の
- 自分で定義した TableViewCell クラス (
SimpleTableViewCell
) 用の Delegate を 実装する - Controller 内に 配列 で準備した データ(
[UserModel]
) を 各 Cell に 表示させる - TableView で 選択した Cell に 応じて 違う処理 を 振り分ける
- Class:
SimpleTableViewCell
と 【入力】 - Subclass of:
UITableViewCell
と 【入力】 Also create XIB file
に チェック- 自動生成された 【メソッド を 全て削除】
awakeFromNib()
setSelected()
import UIKit
// MARK: - Property
class SimpleTableViewCell: UITableViewCell {
}
// MARK: - Life Cycle
extension SimpleTableViewCell {
}
// MARK: - Protocol
extension SimpleTableViewCell {
}
// MARK: - Method
extension SimpleTableViewCell {
}
import UIKit
+// MARK: - Property
class SimpleTableViewCell: UITableViewCell {
-
- override func awakeFromNib() {
- super.awakeFromNib()
- // Initialization code
- }
-
- override func setSelected(_ selected: Bool, animated: Bool) {
- super.setSelected(selected, animated: animated)
-
- // Configure the view for the selected state
- }
-
}
+// MARK: - Life Cycle
+extension SimpleTableViewCell {
+}
+// MARK: - Protocol
+extension SimpleTableViewCell {
+}
+// MARK: - Method
+extension SimpleTableViewCell {
+}
SimpleTableViewCell.xib
を 【ファイル 選択】- 部品 を 配置
- Label 【追加】
Outlet
を コード上に 【追加】- 変数名:
titleLabel
- 変数名:
- Button 【追加】
Action
を コード上に 【追加】- メソッド名:
touchButton
- メソッド名:
- Label 【追加】
// MARK: - Property
class SimpleTableViewCell: UITableViewCell {
+ @IBOutlet weak var titleLabel: UILabel!
+ @IBAction func touchButton(_ sender: UIButton) {
+ }
}
// (以下略)
- tableView を配置している ViewController(
NewViewController.swift
) を 【ファイル選択】 viewDidLoad()
内で- 【 新規 TableViewCell を 登録(
register
) 】 するnibName
- =
xib
ファイルの名前 (="SimpleTableViewCell"
)
- =
forCellReuseIdentifier
=- = Cell 生成(or 使い回し)時(
dequeueReusableCell()
) の 引数withIdentifier
で 指定する ID 文字列- ここでは
nibName
と 同じ(="SimpleTableViewCell"
) にする
- ここでは
- = Cell 生成(or 使い回し)時(
- 【 新規 TableViewCell を 登録(
let xib: UINib = UINib.init(nibName: "SimpleTableViewCell", bundle: nil)
tableView.register(xib, forCellReuseIdentifier: "SimpleTableViewCell")
- 生成(or 使い回し) して返す クラス を 【変更】
UITableViewCell
->SimpleTableViewCell
に 【変更】
UITableView
のdequeueReusableCell()
メソッド を利用- 100 行あったら 100 行分の View を作るわけではなくて
- 表示される分だけの View を用意して、それを使い回す
withIdentifier
- =
register
時にforCellReuseIdentifier
に 指定した ID 文字列 (="SimpleTableViewCell"
)
- =
- 100 行あったら 100 行分の View を作るわけではなくて
let cell: SimpleTableViewCell =
tableView.dequeueReusableCell(withIdentifier: "SimpleTableViewCell", for: indexPath)
as! SimpleTableViewCell
NewViewController.swift
import UIKit
// MARK: - Property
class NewViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
}
// MARK: - Life Cycle
extension NewViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
+ let xib: UINib = UINib.init(nibName: "SimpleTableViewCell", bundle: nil)
+ tableView.register(xib, forCellReuseIdentifier: "SimpleTableViewCell")
}
}
// MARK: - Protocol: UITableViewDataSource
extension NewViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let cell = UITableViewCell()
- cell.textLabel?.text = String(indexPath.row)
+ let cell: SimpleTableViewCell =
+ tableView.dequeueReusableCell(withIdentifier: "SimpleTableViewCell", for: indexPath)
+ as! SimpleTableViewCell
return cell
}
}
// MARK: - Protocol: UITableViewDelegate
extension NewViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let nextVC = TopViewController()
transitionViewController(to: nextVC)
}
}
// MARK: - Method
extension NewViewController {
func transitionViewController(to: UIViewController) {
navigationController?.pushViewController(to, animated: true)
}
}
Cmd + R
で 動作確認 (TableView の 各 Cell に Label と Button が 表示される 事の確認)
- ID を 文字列 で 指定すると 綴り間違い する可能性あるので
String(describing:)
と型.self
を 利用して 型 から 指定 する方が良いかも ?
let cellTypeName = String(describing: SimpleTableViewCell.self) // "SimpleTableViewCell"
let xib: UINib = UINib.init(nibName: cellTypeName, bundle: nil)
tableView.register(xib, forCellReuseIdentifier: cellTypeName)
let cell: SimpleTableViewCell =
tableView.dequeueReusableCell(withIdentifier: cellTypeName, for: indexPath)
as! SimpleTableViewCell
SimpleTableViewCell.swift
内に 【 新規 Protocol を 定義 】NSObjectProtocol
を 継承
UIButton
を 引数 に 取る メソッド を 【実装】touchButton(_ sender: UIButton)
@IBAction func touchButton(_ sender: UIButton)
から 呼び出す メソッド
// ★ 新規追加 ここから -------------------------------
// MARK: - Delegate
protocol SimpleTableViewCellDelegate: NSObjectProtocol {
func touchButton(_ sender: UIButton)
}
// ★ 新規追加 ここまで -------------------------------
// MARK: - Property
class SimpleTableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBAction func touchButton(_ sender: UIButton) {
}
}
// 以下略
- ↑で新規作成した Delegate を
SimpleTableViewCell
の Property として 【実装】
weak var delegate: SimpleTableViewCellDelegate? = nil
@IBAction func touchButton(_ sender: UIButton)
内に- Delegate を 呼び出す処理を 【実装】
@IBAction func touchButton(_ sender: UIButton) {
delegate?.touchButton(sender) // ★ 追加
}
SimpleTableViewCell.swift
import UIKit
+// MARK: - Delegate
+protocol SimpleTableViewCellDelegate: NSObjectProtocol {
+ func touchButton(_ sender: UIButton)
+}
// MARK: - Property
class SimpleTableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBAction func touchButton(_ sender: UIButton) {
+ delegate?.touchButton(sender)
}
+ weak var delegate: SimpleTableViewCellDelegate? = nil
}
// MARK: - Life Cycle
extension SimpleTableViewCell {
}
// MARK: - Protocol
extension SimpleTableViewCell {
}
// MARK: - Method
extension SimpleTableViewCell {
}
Cmd + R
で 動作確認 (動作結果 は 前と変わらず. エラーが発生しない事の確認)
NewViewController.swift
を 【ファイル選択】- 元々の Delegate (=
UITableViewDelegate
) の 実装 を 全て 【削除】
-// MARK: - Protocol: UITableViewDelegate
-extension NewViewController: UITableViewDelegate {
- func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
- let nextVC = TopViewController()
- transitionVC(to: nextVC)
- }
-}
viewDidLoad()
内のtableView.delegate = self
が コンパイルエラー になるので 【削除】- ↑ で
NewViewController
からUITableViewDelegate
の 実装 を 消したため- 代入先/代入元 の 型が異なる
- 左辺: UITableView 型の
delegate
はUITableViewDelegate?
Protocol 型 を要求 - 右辺:
self
(=NewViewController
型)UITableViewDelegate
Protocol は 未実装
- 左辺: UITableView 型の
- 代入先/代入元 の 型が異なる
- tableView.delegate = self
- 新規 Delegate (=
SimpleTableViewCellDelegate
) を 【実装】
// ★ 新規追加
// MARK: - Protocol: SimpleTableViewCellDelegate
extension NewViewController: SimpleTableViewCellDelegate {
func touchButton(_ sender: UIButton) {
let nextVC = TopViewController()
transitionVC(to: nextVC)
}
}
- Cell(
SimpleTableViewCell
) 生成(or 使い回し)時 にdelegate
を指定する処理を 【実装】
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: SimpleTableViewCell =
tableView.dequeueReusableCell(withIdentifier: "SimpleTableViewCell", for: indexPath)
as! SimpleTableViewCell
cell.delegate = self // ★ 追加
return cell
}
NewViewController.swift
import UIKit
// MARK: - Property
class NewViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
}
// MARK: - Life Cycle
extension NewViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
- tableView.delegate = self
let xib: UINib = UINib.init(nibName: "SimpleTableViewCell", bundle: nil)
tableView.register(xib, forCellReuseIdentifier: "SimpleTableViewCell")
}
}
// MARK: - Protocol: UITableViewDataSource
extension NewViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: SimpleTableViewCell =
tableView.dequeueReusableCell(withIdentifier: "SimpleTableViewCell", for: indexPath)
as! SimpleTableViewCell
+ cell.delegate = self
return cell
}
}
-// MARK: - Protocol: UITableViewDelegate
-extension NewViewController: UITableViewDelegate {
- func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
- let nextVC = TopViewController()
- transitionVC(to: nextVC)
- }
-}
+// MARK: - Protocol: SimpleTableViewCellDelegate
+extension NewViewController: SimpleTableViewCellDelegate {
+ func touchButton(_ sender: UIButton) {
+ let nextVC = TopViewController()
+ transitionVC(to: nextVC)
+ }
+}
// MARK: - Method
extension NewViewController {
func transitionVC(to: UIViewController) {
navigationController?.pushViewController(to, animated: true)
}
}
Cmd + R
で 動作確認 (TableView の 各 Cell の Button を選択すると 画面遷移 される 事の確認)
NewViewController
内でUserModel
の 配列 を Property で 保持して- 各
UserModel
の 情報をSimpleTableViewCell
に 表示 させる
- 各
- 自分自身 を 変更する メソッド を 【実装】
- Cell の 生成(or 使い回し)時 に
delegate
先 から 呼び出される
- Cell の 生成(or 使い回し)時 に
func updateCell(userModel: UserModel) {
titleLabel.text = userModel.name
}
SimpleTableViewCell.swift
import UIKit
// MARK: - Delegate
protocol SimpleTableViewCellDelegate: NSObjectProtocol {
func touchButton(_ sender: UIButton)
}
// MARK: - Property
class SimpleTableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBAction func touchButton(_ sender: UIButton) {
delegate?.touchButton(sender)
}
weak var delegate: SimpleTableViewCellDelegate? = nil
}
// MARK: - Life Cycle
extension SimpleTableViewCell {
}
// MARK: - Protocol
extension SimpleTableViewCell {
}
// MARK: - Method
extension SimpleTableViewCell {
+ func updateCell(userModel: UserModel) {
+ titleLabel.text = userModel.name
+ }
}
UserModel
の 配列 を Property として 【実装】
var userModels: [UserModel] = []
UserModel
の 配列 を 初期化 する メソッド を 【実装】- ダミーデータとして 3件 用意
func createUserModel(name: String, age: Int?) -> UserModel {
let u = UserModel()
u.name = name
u.age = age
return u
}
func setUserModels() {
let u1 = createUserModel(name: "user1", age: 20)
let u2 = createUserModel(name: "user2", age: nil)
let u3 = createUserModel(name: "user3", age: 60)
userModels += [u1]
userModels += [u2]
userModels += [u3]
}
- Cell の 行数 を 返す メソッド で
UserModel
の 配列 の 件数 を 返す 処理に 【変更】
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userModels.count // ★ 修正
}
- 各 Cell(
SimpleTableViewCell
) を 生成(or 使い回し) する メソッド で- 各
UserModel
の 情報を Cell (SimpleTableViewCell
) に 渡す 処理を 【実装】
- 各
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: SimpleTableViewCell =
tableView.dequeueReusableCell(withIdentifier: "SimpleTableViewCell", for: indexPath)
as! SimpleTableViewCell
cell.delegate = self
// ★ 追加 ここから -----------------------
let userModel = userModels[indexPath.row]
cell.updateCell(userModel: userModel)
// ★ 追加 ここまで -----------------------
return cell
}
viewDidLoad()
でUserModel
の 配列 の 初期化 を 【呼び出し】UITableView
のreloadData()
も 【呼び出し】- TableView 全体を更新
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
let xib: UINib = UINib.init(nibName: "SimpleTableViewCell", bundle: nil)
tableView.register(xib, forCellReuseIdentifier: "SimpleTableViewCell")
// ★ 追加 ここから -----------------------
setUserModels()
tableView.reloadData()
// ★ 追加 ここまで -----------------------
}
NewViewController.swift
import UIKit
// MARK: - Property
class NewViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
+ var userModels: [UserModel] = []
}
// MARK: - Life Cycle
extension NewViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
let xib: UINib = UINib.init(nibName: "SimpleTableViewCell", bundle: nil)
tableView.register(xib, forCellReuseIdentifier: "SimpleTableViewCell")
+ setUserModels()
+ tableView.reloadData()
}
}
// MARK: - Protocol: UITableViewDataSource
extension NewViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- return 100
+ return userModels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: SimpleTableViewCell =
tableView.dequeueReusableCell(withIdentifier: "SimpleTableViewCell", for: indexPath)
as! SimpleTableViewCell
cell.delegate = self
+ let userModel = userModels[indexPath.row]
+ cell.updateCell(userModel: userModel)
return cell
}
}
// MARK: - Protocol: SimpleTableViewCellDelegate
extension NewViewController: SimpleTableViewCellDelegate {
func touchButton(_ sender: UIButton) {
let nextVC = TopViewController()
transitionVC(to: nextVC)
}
}
// MARK: - Method
extension NewViewController {
func transitionVC(to: UIViewController) {
navigationController?.pushViewController(to, animated: true)
}
+ func createUserModel(name: String, age: Int?) -> UserModel {
+ let u = UserModel()
+ u.name = name
+ u.age = age
+ return u
+ }
+ func setUserModels() {
+ let u1 = createUserModel(name: "user1", age: 20)
+ let u2 = createUserModel(name: "user2", age: nil)
+ let u3 = createUserModel(name: "user3", age: 60)
+ userModels += [u1]
+ userModels += [u2]
+ userModels += [u3]
+ }
}
Cmd + R
で 動作確認 (TableView の Cell の 行数が 3行, 各 Cell の Label に UserModel.name
の 中身 が 表示 される 事の確認)
- 仕様
id
にはindexPath.row
の 値 を入れる- 3件のデータのうち
- 1件目 は
TopViewController
に 遷移 - 2件目 は
SecondViewController
に 遷移 - 3件目 は 何もしない
- 1件目 は
- Objective C では
id
は 予約語 のため 使用できない らしい- Objective C と swift の 混合 Project だと コンパイルエラー になる ??
UserModel.swift
import UIKit
class UserModel: NSObject {
+ var id: Int?
var name: String?
var age: Int?
}
UserModel
を Property として 【実装】
var userModel: UserModel = UserModel()
- Cell の 生成(or 使い回し)時 に
delegate
先 から 呼び出される メソッド 内でUserModel
を Property に セット
func updateCell(userModel: UserModel) {
self.userModel = userModel // ★ 追加
titleLabel.text = userModel.name
}
- ボタン の 選択時 に Delegate 先 に
UserModel
を 返す ように 【 Protocol を 改修 】
protocol SimpleTableViewCellDelegate: NSObjectProtocol {
func touchButton(_ sender: UIButton, userModel: UserModel) // ★ 第2引数 追加
}
@IBAction func touchButton(_ sender: UIButton) {
delegate?.touchButton(sender, userModel: userModel) // ★ 第2引数 追加
}
SimpleTableViewCell.swift
import UIKit
// MARK: - Delegate
protocol SimpleTableViewCellDelegate: NSObjectProtocol {
- func touchButton(_ sender: UIButton)
+ func touchButton(_ sender: UIButton, userModel: UserModel)
}
// MARK: - Property
class SimpleTableViewCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBAction func touchButton(_ sender: UIButton) {
- delegate?.touchButton(sender)
+ delegate?.touchButton(sender, userModel: userModel)
}
weak var delegate: SimpleTableViewCellDelegate? = nil
+ var userModel: UserModel = UserModel()
}
// MARK: - Life Cycle
extension SimpleTableViewCell {
}
// MARK: - Protocol
extension SimpleTableViewCell {
}
// MARK: - Method
extension SimpleTableViewCell {
func updateCell(userModel: UserModel) {
+ self.userModel = userModel
titleLabel.text = userModel.name
}
}
- この時点で
NewViewController.swift
に 以下の コンパイルエラー 発生Type 'NewViewController' does not conform to protocol 'SimpleTableViewCellDelegate'
- 【引数を追加】
func touchButton(_ sender: UIButton, userModel: UserModel) { // ★ 第2引数 追加
let nextVC = TopViewController()
transitionVC(to: nextVC)
}
- ↑ の メソッド の 内容 を 【変更】
func touchButton(_ sender: UIButton, userModel: UserModel) {
// 変数:nextVC は UIViewController 型
var nextVC = UIViewController()
switch userModel.id {
case 0:
nextVC = TopViewController()
case 1:
// 変数:nextVC は UIViewController 型
// (実体は SecondViewController 型の instance)
nextVC = SecondViewController()
// ダウンキャスト する方法(1) if let as? 使用
if let vc = nextVC as? SecondViewController {
// 変数:vc は SecondViewController 型
vc.userModel.name = "userX"
}
// ダウンキャスト する方法(2) 別の変数 使用
// ( ViewController は class で参照型のため
// `nextVC` と `vc2` は同じ instance を指し示す)
let vc2 = nextVC as! SecondViewController
vc2.userModel.age = 99
default:
return
}
transitionVC(to: nextVC)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: SimpleTableViewCell =
tableView.dequeueReusableCell(withIdentifier: "SimpleTableViewCell", for: indexPath)
as! SimpleTableViewCell
cell.delegate = self
let userModel = userModels[indexPath.row]
userModel.id = indexPath.row // ★ 追加
cell.updateCell(userModel: userModel)
return cell
}
NewViewController.swift
import UIKit
// MARK: - Property
class NewViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var userModels: [UserModel] = []
}
// MARK: - Life Cycle
extension NewViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
let xib: UINib = UINib.init(nibName: "SimpleTableViewCell", bundle: nil)
tableView.register(xib, forCellReuseIdentifier: "SimpleTableViewCell")
setUsers()
tableView.reloadData()
}
}
// MARK: - Protocol: UITableViewDataSource
extension NewViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userModels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: SimpleTableViewCell =
tableView.dequeueReusableCell(withIdentifier: "SimpleTableViewCell", for: indexPath)
as! SimpleTableViewCell
cell.delegate = self
let userModel = userModels[indexPath.row]
+ userModel.id = indexPath.row
cell.updateCell(userModel: userModel)
return cell
}
}
// MARK: - Protocol: SimpleTableViewCellDelegate
extension NewViewController: SimpleTableViewCellDelegate {
- func touchButton(_ sender: UIButton) {
+ func touchButton(_ sender: UIButton, userModel: UserModel) {
- let nextVC = TopViewController()
+ var nextVC = UIViewController()
+
+ switch userModel.id {
+ case 0:
+ nextVC = TopViewController()
+ case 1:
+ nextVC = SecondViewController()
+
+ if let vc = nextVC as? SecondViewController {
+ vc.userModel.name = "userX"
+ }
+
+ let vc2 = nextVC as! SecondViewController
+ vc2.userModel.age = 99
+
+ default:
+ return
+ }
transitionVC(to: nextVC)
}
}
// MARK: - Method
extension NewViewController {
func transitionVC(to: UIViewController) {
navigationController?.pushViewController(to, animated: true)
}
func createUser(name: String, age: Int?) -> UserModel {
let u = UserModel()
u.name = name
u.age = age
return u
}
func setUsers() {
let u1 = createUser(name: "user1", age: 20)
let u2 = createUser(name: "user2", age: nil)
let u3 = createUser(name: "user3", age: 60)
userModels += [u1]
userModels += [u2]
userModels += [u3]
}
}
Cmd + R
で 動作確認 (TableView の 1行目, 2行目 の Cell の Button を 選択すると 別々の画面に 遷移 される, 3行目 は 何も起きない 事の確認)