エモいTableViewを作るチャレンジ

ちゃちゃっす！[iOS Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ios)の15日目担当、株式会社ゆめみ新卒iOSエンジニアの山田です！

入社９ヶ月ほど経って色々経験したので、今まで使ったライブラリや技術を利用して出来るだけエモいTableViewを作ってみようチャレンジでした！

# できたもの

<img width="324" alt="スクリーンショット 2018-12-15 6.07.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/114709/cdea1fc0-0632-d879-4a45-97cf99a55034.png">

<img width="336" alt="スクリーンショット 2018-12-15 6.04.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/114709/96b9a60f-9152-ad2c-42a9-8fe25c6f315c.png">

ぜひ、実機で動かしてみてください！

# 何を作ったか
iTunesからトップ映画のxmlを取得してきてリスト表示

# 作成期間
8時間！

# ここがエモい！
## スケルトン表示
このライブラリすっごい便利じゃないっすか！！？？
入れて見せておくだけでオシャンティになります。

<img width="291" alt="スクリーンショット 2018-12-15 7.06.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/114709/7fa5a631-0e00-5a54-44b4-f96f075a0599.png">

（これが見せたいがためにTableViewのリロード三秒止めました😇）
## 離した時のアニメーション
dumpingアニメーション使っているので、**バネっぽく**元に戻ります！
気持ちよくないすか！！？？
チームの先輩に教えてもらったのですが、すっかり虜になりました。

![ScreenRecording_12-15-2018-07-08-38.gif](https://qiita-image-store.s3.amazonaws.com/0/114709/7b263837-136d-a2ca-99c1-c9167748379d.gif)

## haptics
最近ハプティクスの使い所を探っているので、使ってみました。
プレスした状態から指を離した時に**ブルん！**ってなります！

気持ちいい！！！
(こちらは触ってみないと分からないので見せられなかったです...)

# 利用した技術/ハマりどころ
## [SkeletonView](https://github.com/Juanpe/SkeletonView)
Facebookが全く同じようなものを利用していますが、主に非同期通信時の読み込み中に表示しておくViewです。

基本的に簡単なのですが、微妙に使い方にクセがあるので慣れが必要です。
`UIView`, `UITableView`, `UICollectionView` のいずれも対応されています。

# [dumpingアニメーション](https://developer.apple.com/documentation/uikit/uiview/1622594-animate)
UIViewの標準アニメーションを利用して、以下のように実装しました。

```swift:animation.swift
private func shrink() {
    let animationScale: CGFloat = 0.90
    UIView.animate(withDuration: 0.1) { [unowned self] in
        self.transform = .init(scaleX: animationScale,
                               y: animationScale)
    }
}
    
private func expand() {
    let animationScale: CGFloat = 1.10
    UIView.animate(withDuration: 0.1) { [unowned self] in
        self.transform = .init(scaleX: animationScale,
                               y: animationScale)
    }
}
    
private func restore() {
    UIView.animate(withDuration: 0.1,
                   delay: 0,
                   usingSpringWithDamping: 0.7,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: { [unowned self] in
                    self.transform = CGAffineTransform.identity
    })
}
```

# [haptics](https://developer.apple.com/documentation/uikit/uifeedbackgenerator)
こちら、実装は極めて簡単で10行程度で実現できてしまいます。

注意点というのが一つだけあって、フィードバックの発生に遅延が出てしまう可能性があるらしく、処理の数秒前に　`feedbackGenerator.prepare()` を呼ぶのをAppleが推奨しています。

該当部分抜粋してGoogle翻訳先生にお聞きしました。
>ジェネレーターを準備することで、フィードバックをトリガーするときの待ち時間を短縮できます。これは、音や映像の合図にフィードバックを一致させるときに特に重要です。
ジェネレータのprepare（）メソッドを呼び出すと、Taptic Engineが準備された状態になります。パワーを維持するために、Taptic Engineは短時間（数秒のオーダー）の間、または次回のフィードバックをトリガーするまでこの状態にとどまります。
いつ、どこで発電機を準備するのが最も良いか考えてみましょう。 prepare（）を呼び出してすぐにフィードバックをトリガーすると、システムはTaptic Engineを準備状態にするのに十分な時間がなくなり、待ち時間が短縮されることはありません。一方、prepare（）をあまりに早く呼び出すと、フィードバックをトリガする前にTaptic Engineが再びアイドル状態になることがあります。


 ```swift:haptics.swift
    private let feedbackGenerator = UIImpactFeedbackGenerator(style: .light)

...

extension FloatingSkeletonTableViewController: FloatingSkeletonTableViewCellDelegate {
    
    func cellWillStartAnimating() {
        feedbackGenerator.prepare()
    }
    
    func cellWillEndAnimating() {
        feedbackGenerator.impactOccurred()
    }
    
}

```

# UITableViewのセルの間のすきま
こんな感じのハックみたいな解決法をよく見かけたのですが、セクションを使いたくなった瞬間死ぬので使いませんでした。
https://stackoverflow.com/questions/6216839/how-to-add-spacing-between-uitableviewcell

- UITableViewCellのContentViewの上にさらに `floatingView` というのを乗せて上下左右10ptずつ内側に制約を付ける。
- `floatingView` は白背景に、その他背面ViewのbackgroundColorは全て `.clear` にする。(青色のViewを除く)という対応にしました。

<img width="415" alt="スクリーンショット 2018-12-15 6.59.50.png" src="https://qiita-image-store.s3.amazonaws.com/0/114709/af3040c4-2608-0a74-7b07-94c40cc0c54d.png">

# やりきれなかったところ
前半は勢いで書きましたが、[本来hapticsの利用には最新の注意を払う必要があり](https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/feedback/)、なにかユーザーに注意を促したい時や変化がある時以外は原則使わない方が良いです。

アニメーション+haptics feedbackで状態の変化を表現しようと思ったのですが（トップ画像が入れ替わるなど)残念ながらタイムオーバー。

TableViewCellが自分でリサイズとか、細かい詰めるところは全部すっ飛ばしました。

# あとがき
ここまで読んでいただきありがとうございました！
レポジトリは公開しているので興味があれば実機で動かしてみてください！
（プルリクもお待ちしております！）

https://github.com/syatyo/FloatingSkeletonTableView