Skip to content

Instantly share code, notes, and snippets.

@takashi1975
Last active January 30, 2018 04:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takashi1975/7eea1235666a49b7a394887e03ad502d to your computer and use it in GitHub Desktop.
Save takashi1975/7eea1235666a49b7a394887e03ad502d to your computer and use it in GitHub Desktop.
Swift4 TableView Sample (備忘録)
//
// 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