iOSアプリ開発入門#1 ~UITableView~

## 目的
- よく使うであろうTableViewを使用したアプリをStoryboardありとなしでサクッと作れるようになる
- ありなしの両方を試すことで、Storyboardが何をやっているのかなんとなくイメージできるようになる

## Step1. 爆速でTableViewアプリ作成
ひとまず5分でTableViewアプリを作る。
初心者がstoryboardを使うと色々とハマる気がするので、storyboardなしの方が手っ取り早い。

### 1.1. XCodeでProject作成
「Single View App」で「Next」
<img width="1512" alt="ios_prac1.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/12935510-3208-9479-3ab8-9de60173e234.png">

### 1.2. Project情報入力
適当にProject情報入力
<img width="1512" alt="ios_prac2.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/21e65b66-08f7-75fa-8246-82ca2083d248.png">

### 1.3. いきなりViewController編集
爆速でいきたいので他の設定やファイルには目もくれずViewControllerを編集する

```swift:ViewController.swift
import UIKit

class ViewController:
    UIViewController,
    UITableViewDelegate,
    UITableViewDataSource
{

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tableView: UITableView = UITableView(frame: view.frame, style: .plain)
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.delegate = self
        tableView.dataSource = self
        view.addSubview(tableView)
    }

    // UITableViewDataSource

    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int
    {
        return 10
    }
    
    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell",
                                                                  for: indexPath)
        cell.textLabel?.text = "\(indexPath[1] + 1)行目"
        return cell
    }
}
```
ポイントとしては

- インスタンス変数「view」にaddSubViewで表示したいTableViewを追加する
- 追加するTableViewに表示したいTableViewCellをregisterする
- 追加するTableViewのdelegateとdataSourceに必要なインタフェースの実装（この場合はself）を渡す
- 各行の表示にはcellForRowAtで返却されるTableViewCellが使用される
- その際、dequeueReusableCellを使用することでTableViewCellインスタンスを再利用しつつ表示に利用できる

### 1.4. 起動
これで完了。シミュレータでRunすれば以下のような10行だけ表示されるアプリの完成。
<img width="310" alt="スクリーンショット 2019-02-28 18.28.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/ebb2a094-f29d-4d4b-c506-5fd66436db4b.png">


## Step2. Cellのカスタマイズ+動的レコード
Tableっぽくdataをもとに行数を動的に変更しつつ、UICellViewのカスタマイズをコードだけでやってみる。

### 2.1. ViewControllerをもう少し弄ってみる
```swift:ViewController.swift
import UIKit

class ViewController:
    UIViewController,
    UITableViewDelegate,
    UITableViewDataSource
{
    private let data: [(String,String)] = [
        ("りんご", "Apple"),
        ("みかん", "Orange"),
        ("ぶどう", "Grape"),
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        let tableView: UITableView = UITableView(frame: view.frame, style: .plain)
        tableView.register(TableCellView.self, forCellReuseIdentifier: TableCellView.ID)
        tableView.delegate = self
        tableView.dataSource = self
        view.addSubview(tableView)
    }

    // UITableViewDataSource

    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int
    {
        return data.count
    }

    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell: TableCellView = tableView.dequeueReusableCell(withIdentifier: TableCellView.ID,
                                                                for: indexPath) as! TableCellView
        
        let rowIndex: Int = indexPath[1]
        cell.setCellInfo(title: data[rowIndex].0, detail: data[rowIndex].1)
        return cell
    }
}

class TableCellView: UITableViewCell
{
    static let ID: String = "cell"
    private let cellStyle: CellStyle = CellStyle.subtitle
    private let cellColor: UIColor = UIColor(red: 0.6, green: 0.8, blue: 0.9, alpha: 0.9)

    required init?(coder aDecoder: NSCoder) {
        fatalError("not implemented")
    }

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: cellStyle, reuseIdentifier: reuseIdentifier)
        self.backgroundColor = cellColor
    }

    // original methods

    func setCellInfo(title: String, detail: String) {
        self.textLabel?.text = title
        self.detailTextLabel?.text = detail
    }
}
```
ポイントとしては

- registerしていたUITableViewCellを自作クラスのTableViewCellに変更
- TableViewCellはUITableViewCellのサブクラス
- overrideしたinitで表示ロジックを実装

### 2.2. 起動
Runする
<img width="310" alt="スクリーンショット 2019-02-28 19.33.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/5e507444-d36d-6951-daaa-f9c3726efaee.png">


## Step3. Storyboardを使ってみる
コードで地道に書いているところをStoryboardにお任せしてみよう

### 3.1. TableViewをStoryboardへ
Main.storyboardを開き、View Controllerに対してTableViewを追加する。

画面右上のUIコンポネント追加ボタンから
<img width="480" alt="68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f313134342f36633462366565632d646466632d363935382d316434622d6136353166643064626365392e706e67.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/23163e4d-18db-6b51-8c05-e6dbf33d3556.png">



TableViewを選択
<img width="300" alt="スクリーンショット 2019-02-28 19.40.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/5607e3a5-30ff-afc5-17c1-1728294d3906.png">

Codeと並べて表示
<img width="1501" alt="68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f313134342f37636332356666612d396665332d666633622d623064662d6564326466383332353731382e706e67.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/69058454-c4b2-50f1-88d3-57733c97b70e.png">


ここでCtrlキーを押したまま、TableViewからコードの方へドラッグ&ドロップすると、該当のTableViewとバインドされた変数が定義される。
<img width="1522" alt="68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f313134342f31633335623930622d393232352d343265642d383533632d6431363434616566633566652e706e67.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/325c90c7-73bf-9fc3-3a90-ef19914e47f2.png">


さらにTableViewのOutletsについて、delegateとdataSourceをドラッグ&ドロップでView Controllerとバインドする（このあたり、慣れればどうってことないのだろうけど、初心者にやさしいかどうかは微妙な気がする）
<img width="1512" alt="68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f313134342f30303435356534612d666362322d303334662d393063352d3230306636323636373932312e706e67.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/2131998f-1140-fd78-b203-a5d3b1265afa.png">


これらの作業により、ViewController.swiftからは以下のコードが削減できる

```diff:
--- a/PracticeApp/ViewController.swift
+++ b/PracticeApp/ViewController.swift
@@ -13,6 +13,8 @@ class ViewController:
     UITableViewDelegate,
     UITableViewDataSource
 {
+    @IBOutlet weak var tableView: UITableView!
+
     private let data: [(String,String)] = [
         ("りんご", "Apple"),
         ("みかん", "Orange"),
@@ -22,11 +24,7 @@ class ViewController:
     override func viewDidLoad() {
         super.viewDidLoad()
 
-        let tableView: UITableView = UITableView(frame: view.frame, style: .plain)
         tableView.register(TableCellView.self, forCellReuseIdentifier: TableCellView.ID)
-        tableView.delegate = self
-        tableView.dataSource = self
-        view.addSubview(tableView)
     }
 
     // UITableViewDataSource
```
正直、この程度のtableViewであればあまりstoryboardのありがたさが実感できないが、おそらくlayout変更とかstyle変更とか諸々盛り込んでいく過程でありがたくなるはずだと信じる。

### 3.2. TableViewCellをStoryboardへ
引き続きTableViewCellもStoryboardへ移行してみよう

さっきのノリで今度はMain.storyboardのTableView内部に対してTableViewCellを追加する
<img width="300" alt="スクリーンショット 2019-02-28 20.10.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/5204868d-28a5-d07f-f807-4d2e065a1e6f.png">

このように**Prototype Cells**と表示される
<img width="1512" alt="68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f313134342f38363330393564642d303538652d393330362d663661392d3565646535633838633266622e706e67.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/65678701-ca8a-27a3-fdac-51b030adb0e5.png">

あとはStoryboard上でこのTableViewCellをさきほどと似せた感じで設定する。
具体的には

- Style: Subtitle
- Identifier: cell
- Background: Customから適当に色選択

といった具合。
<img width="1504" alt="68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f313134342f66383061643965312d623165372d373765322d623639382d3464396234336431393965332e706e67.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/1783751c-53ff-0e60-a207-eee7543f194d.png">

これによって、さらに以下のコードが削減できる。

```diff:
--- a/PracticeApp/ViewController.swift
+++ b/PracticeApp/ViewController.swift
@@ -23,8 +23,6 @@ class ViewController:
 
     override func viewDidLoad() {
         super.viewDidLoad()
-
-        tableView.register(TableCellView.self, forCellReuseIdentifier: TableCellView.ID)
     }
 
     // UITableViewDataSource
@@ -38,34 +36,13 @@ class ViewController:
     func tableView(_ tableView: UITableView,
                    cellForRowAt indexPath: IndexPath) -> UITableViewCell
     {
-        let cell: TableCellView = tableView.dequeueReusableCell(withIdentifier: TableCellView.ID,
-                                                                for: indexPath) as! TableCellView
+        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell",
+                                                                  for: indexPath)
 
         let rowIndex: Int = indexPath[1]
-        cell.setCellInfo(title: data[rowIndex].0, detail: data[rowIndex].1)
+        cell.textLabel?.text = data[rowIndex].0
+        cell.detailTextLabel?.text = data[rowIndex].1
         return cell
     }
 }
 
-class TableCellView: UITableViewCell
-{
-    static let ID: String = "cell"
-    private let cellStyle: CellStyle = CellStyle.subtitle
-    private let cellColor: UIColor = UIColor(red: 0.6, green: 0.8, blue: 0.9, alpha: 0.9)
-
-    required init?(coder aDecoder: NSCoder) {
-        fatalError("not implemented")
-    }
-
-    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
-        super.init(style: cellStyle, reuseIdentifier: reuseIdentifier)
-        self.backgroundColor = cellColor
-    }
-
-    // original methods
-
-    func setCellInfo(title: String, detail: String) {
-        self.textLabel?.text = title
-        self.detailTextLabel?.text = detail
-    }
-}
```

### 3.3. 起動
Runする
<img width="310" alt="スクリーンショット 2019-02-28 20.21.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/1144/ebd97b71-fede-ea47-ee36-484ebf3e7e9f.png">


## 4. まとめ
最終的には以下のように、よくTableViewのチュートリアルで見かける風なコードになっているはず。


```swift:ViewController.swift
import UIKit

class ViewController:
    UIViewController,
    UITableViewDelegate,
    UITableViewDataSource
{
    @IBOutlet weak var tableView: UITableView!

    private let data: [(String,String)] = [
        ("りんご", "Apple"),
        ("みかん", "Orange"),
        ("ぶどう", "Grape"),
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // UITableViewDataSource

    func tableView(_ tableView: UITableView,
                   numberOfRowsInSection section: Int) -> Int
    {
        return data.count
    }

    func tableView(_ tableView: UITableView,
                   cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell",
                                                                  for: indexPath)

        let rowIndex: Int = indexPath[1]
        cell.textLabel?.text = data[rowIndex].0
        cell.detailTextLabel?.text = data[rowIndex].1
        return cell
    }
}
```