【Swift】TableViewCellにImageViewを追加してURLで指定された画像を表示する

# 概要

　この記事は初心者の自分がRESTful なAPIとswiftでiPhone向けのクーポン配信サービスを開発した手順を順番に記事にしています。技術要素を1つずつ調べながら実装したため、とても遠回りな実装となっています。

前回の []() でクーポンの画像も配信するAPIを作りました。
アプリ側もAPI側から取得した画像を画面に表示できるように改造します。


### 参考

* やさしくはじめる　iPhoneアプリ作りの教科書　森 巧尚 著　マイナビ
* [【Swift入門 文法編】型キャスト(as, as!, as?)をマスターしよう](https://fukatsu.tech/swift-cast)
* [【Swift4】UIImageでURLで画像を指定する](https://www.rasukarusan.com/entry/2019/01/07/233207)

### 環境

Mac OS 10.15
Swift5
Xcode11.1


# 手順

* TableViewCellにUIImageViewを追加する
* 追加したUIImageViewでとりあえず画像を表示させる
* jsonで取得したURLを使って画像を表示させる
* 動作確認


### TableViewCellにUIImageViewを追加する

　Xcodeを開いてMain.storyboardでTableVIewCellにUIImageViewを追加します。合わせてデザインを修正してこれまでのクーポンの文字情報の下に画像を表示するようにします。

TableViewCellの Row Height を200pxから400pxに変更して縦を長くします。そして長くした部分にUIImageViewを追加します。Autoresizingで四隅を固定します。そして追加したUIImageViewに tag を付けます（重要）。Labelで4番まで使っているので、5番を割り当てました。

<img width="556" alt="custum-tableviewcell.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526594/e00a8781-2ab6-0206-2245-254ef4fae13f.png">


　次にViewController.swiftを開いて、tableViewの高さを指定している`tableView.rowHeight = [数字]` の部分を Main.storyboardで指定したTableViewCellの Row Height の値に変更します。

```swift
tableView.rowHeight = 400 // Row Heightで指定した長さに変更
```


### 追加したUIImageViewでとりあえず画像を表示させる

　ViewController.swift の tableView にUIImageViewへ画像を表示する処理を追加します。下記の通り追加しました。基本はLabelに値を設定する方法を同じです。

　上で作った画像保存用のフォルダにサンプル画像を格納し、画像ファイルのフルパスをとりあえずベタ書きで指定しました。

```swift

// クーポン画像を表示
        let couponImageView = cell.viewWithTag(5) as! UIImageView
        let imageFileUrl = "/Users/XXXXX/workspace/amiApp/images/coupon-image-001.png"
        couponImageView.image = UIImage(named: imageFileUrl)
```

　変更を保存してXcodeでアプリを実行すると、クーポン情報の下に画像が表示されました！ 
<img width="395" alt="check-app-view-001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526594/c5bd3e97-08f0-f04a-003e-5403bd6581a5.png">

画像ファイルのパスをベタ書きしているので、全てのクーポンに同じ画像が表示されていると思います。また、少しレイアウトが崩れているので後で修正しようと思います。


### レスポンスのjsonで取得したURLを使って画像を表示させる

　TableViewCell や UIImageView の書き方は問題無いことが分かったので、ローカルフォルダのパスではなくAPIのレスポンスで取得した画像ファイルのURLを使って画像を取得するように変更します。

URLの取得から表示までのコードはこちらのようになります。

```swift
        // URLで指定されたクーポン画像を表示
        let couponImageView = cell.viewWithTag(5) as! UIImageView
        if let urlString = coupon["image"] as? String {
            let url = URL(string: urlString)

            do{
                let imageData = try Data(contentsOf: url!)
                couponImageView.image = UIImage(data: imageData)
            } catch {
                print("Error : Cat't get image")
            }
        } else {
            couponImageView.image = UIImage(named: "no-coupon-image.png") //nilの場合は固定画像表示
        }
        return cell
    }

```

　ポイントが幾つかあります。

　１つ目は`if let urlString = coupon["image"] as? String {　〜`の部分です。レスポンスに画像ファイルのURLが指定されていない場合に NULL参照でアプリが落ちるのを防ぐため、`as?`を使ったキャストとオプショナルバインディングを組み合わせて、画像ファイルのURLが指定されていない場合は代わりの画像を表示するようにしています。

`as?`はキャストする際、指定した型ならそのオプショナル型（nilが含まれるかもしれない）を返し、指定した型以外なら nilを返します。

オプショナルバインディングは下記のような書式となり、[オプショナル型の変数]が nilで無い場合に限り任意の処理を実行される事が出来ます。elseと組み合わせる事で、nilかnilで無いかで処理を分ける事も出来ます。

```
If let [任意の定数] = [オプショナル型の変数] {
	オプショナル型の変数がnilじゃなかった場合に実施したい処理
} 
```

　ポイントの2つ目は`let imageData = try Data(contentsOf: url!)`の部分です。URLを使って画像を取ってくる処理です。画像を取得出来た場合は`couponImageView.image = UIImage(data: imageData)`でImageViewに画像を表示させています。



### 動作確認

　変更を保存してXcodeでアプリを実行すると、クーポン情報の下に画像が表示されました！ URLを設定していない2枚目のクーポンからはアプリ側で持たせた仮の画像が表示されています。また、シミュレータを iPhone 11 Pro Max から iPhone 11 Pro にしたらレイアウトがピッタリはまりました。

<img width="388" alt="check-app-view-002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526594/0ccc23df-e3a5-7603-ec98-1c4959a4cb81.png">


これで一通りクーポンアプリの実装ができたので。次は作ったAPIをクラウドのサーバで公開させようと思います。