Last active
January 30, 2018 04:13
-
-
Save takashi1975/7eea1235666a49b7a394887e03ad502d to your computer and use it in GitHub Desktop.
Swift4 TableView Sample (備忘録)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ViewController.swift | |
// Sample | |
// | |
import UIKit | |
// MARK: - | |
extension ViewController { | |
internal struct TableDef { | |
//~~~ 各セクション (今回は共通で使い回す) ~~~~ | |
//項目名: 各セクション | |
static let Section1Rows = [ | |
"cell A", | |
"cell B", | |
"cell C", | |
] | |
enum Section1RowIndex: Int, Codable { | |
case | |
cell_A = 0, | |
cell_B = 1, | |
cell_C = 2 | |
} | |
//~~~ セクション (以下の3つの配列の順序を合わせる) ~~~ | |
//セクション名 | |
static let Section = [ | |
"section 0", | |
"section 1", | |
"section 2", | |
] | |
enum SectionRowIndex: Int, Codable { | |
case | |
section0 = 0, | |
section1 = 1, | |
section2 = 2 | |
} | |
//項目名 (各セクションをまとめる) | |
static let Rows = [ | |
TableDef.Section1Rows, | |
TableDef.Section1Rows, | |
TableDef.Section1Rows, | |
] | |
//項目名を返す | |
static func row(_ indexPath: IndexPath) -> String? { | |
guard (0 <= indexPath.section), (indexPath.section < Rows.count) else { | |
return nil | |
} | |
let lists = Rows[indexPath.section] | |
guard (0 <= indexPath.row), (indexPath.row < lists.count) else { | |
return nil | |
} | |
//正常終了 | |
return lists[indexPath.row] | |
} | |
} | |
} | |
// MARK: - | |
class ViewController: UIViewController { | |
@IBOutlet weak var tableView: UITableView! | |
//TableView の開閉用 | |
fileprivate var openedSections = Set<Int>() | |
//KVO | |
fileprivate var keyValueObservations = [NSKeyValueObservation]() | |
//破棄 | |
deinit { | |
self.removeKVO() | |
} | |
} | |
// MARK: - | |
extension ViewController { | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
// Do any additional setup after loading the view. | |
self.initKVO() | |
} | |
override func didReceiveMemoryWarning() { | |
super.didReceiveMemoryWarning() | |
// Dispose of any resources that can be recreated. | |
} | |
} | |
// MARK: - KVO (KeyValueObserver) | |
extension ViewController { | |
//KVO解除 | |
fileprivate func removeKVO() { | |
for keyValueObservation in self.keyValueObservations { | |
keyValueObservation.invalidate() | |
} | |
self.keyValueObservations.removeAll() | |
} | |
//KVO登録 | |
fileprivate func initKVO() { | |
// //KVO: ... https://qiita.com/BlueEventHorizon/items/bf37428b54b937728dc7 | |
// if let status = xxx.status { | |
// | |
// let kvo = status.observe(\.xxx) { (status, change) in | |
// //change is Always nil. (⇨ Objective-Cで表現できるプロパティではない...ということ。) | |
// print(change) | |
// | |
// //TableViewを更新 | |
// self.updateSection(xxx: status.xxx) | |
// } | |
// | |
// self.keyValueObservations.append(kvo) | |
// } | |
} | |
} | |
// MARK: - | |
extension ViewController: UITableViewDataSource, UITableViewDelegate { | |
// MARK: - Table view data source | |
// UITableViewController の時は override | |
func numberOfSections(in tableView: UITableView) -> Int { | |
// #warning Incomplete implementation, return the number of sections | |
return TableDef.Section.count | |
} | |
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
// #warning Incomplete implementation, return the number of rows | |
//開いているセクション | |
if self.openedSections.contains(section) { | |
// セクションに入るセルの数を返す | |
let count = TableDef.Rows[section].count | |
return count | |
} | |
return 0 | |
} | |
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { | |
let text = TableDef.Section[section] | |
return text | |
} | |
//セクションヘッダを表示 | |
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { | |
//TEST: たたみたくないセクション | |
if (false) { | |
let list: [Int] = [ | |
] | |
if (list.contains(section)) { | |
return nil | |
} | |
} | |
//ジェスチャーを追加 | |
if (true) { | |
let view = UITableViewHeaderFooterView() | |
let gesture = UITapGestureRecognizer(target: self, action: #selector(self.tapSectionHeader(_:))) | |
view.addGestureRecognizer(gesture) | |
view.tag = section | |
return view | |
} | |
} | |
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
//セルの作成 | |
let cell = self.createCell(tableView, indexPath) | |
// //通常例 | |
// let cell = UITableViewCell(style: .value1, reuseIdentifier: "cell") | |
// //let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) | |
// | |
// //Configure the cell... | |
return cell | |
} | |
//セルをタップ | |
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? { | |
if (self.isTouchCellIndexPath(indexPath)) { | |
return indexPath | |
} | |
return nil | |
} | |
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | |
//print(indexPath) | |
//セルから指を離したときにコマンドを実行 | |
self.onClickCell(indexPath) | |
} | |
} | |
// MARK: - TableView (Private Method) | |
extension ViewController { | |
//セクションヘッダをタップしたとき(for ジェスチャー) | |
@objc fileprivate func tapSectionHeader(_ sender: UIGestureRecognizer) { | |
if let section = sender.view?.tag { | |
//MEMO .fade がアコーディオンみたいで一番いいけど、たまに崩れる... | |
var animation = UITableViewRowAnimation.automatic | |
if self.openedSections.contains(section) { | |
self.openedSections.remove(section) | |
animation = .left | |
} else { | |
self.openedSections.insert(section) | |
animation = .right | |
} | |
//セクションを更新 (アニメーションあり) | |
self.tableView.reloadSections(IndexSet(integer: section), with: animation) | |
} | |
} | |
//セルをタップ | |
@objc fileprivate func tapCell(_ sendor: UILongPressGestureRecognizer) { | |
switch sendor.state { | |
case .began, .ended: | |
//print(sendor.view) | |
if let cell = sendor.view as? UITableViewCell { | |
if let indexPath = self.tableView.indexPath(for: cell) { | |
if (sendor.state == .began) { | |
self.tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none) | |
} else { | |
self.tableView.deselectRow(at: indexPath, animated: true) | |
self.tableView(self.tableView, didSelectRowAt: indexPath) | |
} | |
} | |
} | |
break | |
default: | |
break | |
} | |
} | |
//セルの作成 | |
internal func createCell(_ tableView: UITableView, _ indexPath: IndexPath) -> UITableViewCell { | |
//セルの作成 | |
let cell = UITableViewCell(style: .value1, reuseIdentifier: "cell") | |
if (true) { | |
//デバッグ用 | |
cell.textLabel?.text = TableDef.row(indexPath) | |
cell.detailTextLabel?.text = self.getString(indexPath) | |
//cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator | |
} | |
//選択状態 (セルによっては有効にしたい) | |
cell.selectionStyle = self.isTouchCellIndexPath(indexPath) ? (.default) : (.none) | |
//指を離したときに .selectionStyle を元に戻したい | |
if (cell.selectionStyle == .default) { | |
// https://qiita.com/taketomato/items/e0e45f0d768109abef2f | |
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(tapCell(_:))) | |
gesture.minimumPressDuration = 0 | |
cell.addGestureRecognizer(gesture) | |
} | |
return cell | |
} | |
//セルの更新 | |
internal func updateCell(_ indexPath: IndexPath, _ text: String) { | |
//self.tableView.reloadRows(at: [indexPath], with: .automatic) | |
if let cell = self.tableView.cellForRow(at: indexPath) /* as? UITableViewCell */ { | |
cell.detailTextLabel?.text = text | |
} | |
} | |
} | |
// MARK: - セルのタップ (セルをボタンのように扱う) | |
extension ViewController { | |
//タップ可能なセル番号かどうか? | |
fileprivate func isTouchCellIndexPath(_ indexPath: IndexPath) -> Bool { | |
//タップしたい項目 だけを登録 | |
let list: [Int : [Int]] = [ | |
TableDef.SectionRowIndex.section0.rawValue : | |
[ | |
TableDef.Section1RowIndex.cell_A.rawValue, | |
TableDef.Section1RowIndex.cell_B.rawValue, | |
TableDef.Section1RowIndex.cell_C.rawValue, | |
], | |
TableDef.SectionRowIndex.section2.rawValue : | |
[ | |
TableDef.Section1RowIndex.cell_B.rawValue, | |
], | |
] | |
return list[indexPath.section]?.contains(indexPath.row) ?? false | |
} | |
//セルをタップ | |
fileprivate func onClickCell(_ indexPath: IndexPath) { | |
//TODO: タップした時のコマンドを記述 | |
switch indexPath.section { | |
case TableDef.SectionRowIndex.section0.rawValue: | |
self.testCommand(section0: indexPath.row) | |
break | |
case TableDef.SectionRowIndex.section1.rawValue: | |
self.testCommand(section1: indexPath.row) | |
break | |
case TableDef.SectionRowIndex.section2.rawValue: | |
self.testCommand(section2: indexPath.row) | |
break | |
default: | |
break | |
} | |
} | |
//テスト | |
fileprivate func testCommand(section0 row: Int) { | |
let data = TableDef.SectionRowIndex.section0.rawValue | |
self.testCommand(section: data, row: row) | |
} | |
fileprivate func testCommand(section1 row: Int) { | |
let data = TableDef.SectionRowIndex.section1.rawValue | |
self.testCommand(section: data, row: row) | |
} | |
fileprivate func testCommand(section2 row: Int) { | |
let data = TableDef.SectionRowIndex.section2.rawValue | |
self.testCommand(section: data, row: row) | |
} | |
//テスト (今回は共通) | |
fileprivate func testCommand(section: Int, row: Int) { | |
switch row { | |
case TableDef.Section1RowIndex.cell_A.rawValue: | |
print("tap A ... ", "section:", section, ", row:", row) | |
break | |
case TableDef.Section1RowIndex.cell_B.rawValue: | |
print("tap B ... ", "section:", section, ", row:", row) | |
break | |
case TableDef.Section1RowIndex.cell_C.rawValue: | |
print("tap C ... ", "section:", section, ", row:", row) | |
break | |
default: | |
break | |
} | |
} | |
} | |
// MARK: - TableView (Private Method ... Update Section) | |
extension ViewController { | |
//各セクションの文言を更新 (今回は共通メソッド ... 実際はセクションごとで用意) | |
fileprivate func updateSection(dummyData: Int) { | |
//TODO: セクションの指定 (今回は引数を仮で使用) | |
//let section = TableDef.SectionRowIndex.section0.rawValue | |
let section = dummyData | |
//TODO: 更新する行を指定 | |
let rows: [TableDef.Section1RowIndex] = [ | |
.cell_A, | |
.cell_B, | |
.cell_C, | |
] | |
for row in rows { | |
//TODO: セルのデータ設定 | |
let text = self.getString(data: dummyData, row: row.rawValue) | |
let indexPath = IndexPath(row: row.rawValue, section: section) | |
self.updateCell(indexPath, text) | |
} | |
} | |
} | |
// MARK: - TableView で表示する文言 (KVO などのデータを String にしたり...) | |
extension ViewController { | |
fileprivate func getString(_ indexPath: IndexPath) -> String { | |
var result: String = "---" | |
//TODO: 各セクションごとでセルに表示する文言を調整するメソッドを用意 (今回は共通) | |
switch indexPath.section { | |
case TableDef.SectionRowIndex.section0.rawValue, | |
TableDef.SectionRowIndex.section1.rawValue, | |
TableDef.SectionRowIndex.section2.rawValue: | |
result = self.getString(data:indexPath.section, row:indexPath.row) | |
break; | |
default: | |
break; | |
} | |
return result | |
} | |
//セクションごとで セルに表示する文言を調整する (今回は共通) | |
//引数は各セクションごとのデータになるようにしてます | |
fileprivate func getString(data: Int, row: Int) -> String { | |
var result: String = "(nil)" | |
switch row { | |
case TableDef.Section1RowIndex.cell_A.rawValue: | |
result = String(format:"section:%d - Cell A", data) | |
break | |
case TableDef.Section1RowIndex.cell_B.rawValue: | |
result = String(format:"section:%d - Cell B", data) | |
break | |
case TableDef.Section1RowIndex.cell_C.rawValue: | |
result = String(format:"section:%d - Cell C", data) | |
break | |
default: | |
break | |
} | |
return result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment