iOS
UITableView
UITableViewCell
SkeletonView
iOSDay 15

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

ちゃちゃっす!iOS Advent Calendar 2018の15日目担当、株式会社ゆめみ新卒iOSエンジニアの山田です!

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


できたもの

スクリーンショット 2018-12-15 6.07.12.png

スクリーンショット 2018-12-15 6.04.05.png

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


何を作ったか

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


作成期間

8時間!


ここがエモい!


スケルトン表示

このライブラリすっごい便利じゃないっすか!!??

入れて見せておくだけでオシャンティになります。

スクリーンショット 2018-12-15 7.06.32.png

(これが見せたいがためにTableViewのリロード三秒止めました😇)


離した時のアニメーション

dumpingアニメーション使っているので、バネっぽく元に戻ります!

気持ちよくないすか!!??

チームの先輩に教えてもらったのですが、すっかり虜になりました。

ScreenRecording_12-15-2018-07-08-38.gif


haptics

最近ハプティクスの使い所を探っているので、使ってみました。

プレスした状態から指を離した時にブルん!ってなります!

気持ちいい!!!

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


利用した技術/ハマりどころ


SkeletonView

Facebookが全く同じようなものを利用していますが、主に非同期通信時の読み込み中に表示しておくViewです。

基本的に簡単なのですが、微妙に使い方にクセがあるので慣れが必要です。

UIView, UITableView, UICollectionView のいずれも対応されています。


dumpingアニメーション

UIViewの標準アニメーションを利用して、以下のように実装しました。


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

こちら、実装は極めて簡単で10行程度で実現できてしまいます。

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

該当部分抜粋してGoogle翻訳先生にお聞きしました。


ジェネレーターを準備することで、フィードバックをトリガーするときの待ち時間を短縮できます。これは、音や映像の合図にフィードバックを一致させるときに特に重要です。

ジェネレータのprepare()メソッドを呼び出すと、Taptic Engineが準備された状態になります。パワーを維持するために、Taptic Engineは短時間(数秒のオーダー)の間、または次回のフィードバックをトリガーするまでこの状態にとどまります。

いつ、どこで発電機を準備するのが最も良いか考えてみましょう。 prepare()を呼び出してすぐにフィードバックをトリガーすると、システムはTaptic Engineを準備状態にするのに十分な時間がなくなり、待ち時間が短縮されることはありません。一方、prepare()をあまりに早く呼び出すと、フィードバックをトリガする前にTaptic Engineが再びアイドル状態になることがあります。



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を除く)という対応にしました。

スクリーンショット 2018-12-15 6.59.50.png


やりきれなかったところ

前半は勢いで書きましたが、本来hapticsの利用には最新の注意を払う必要があり、なにかユーザーに注意を促したい時や変化がある時以外は原則使わない方が良いです。

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

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


あとがき

ここまで読んでいただきありがとうございました!

レポジトリは公開しているので興味があれば実機で動かしてみてください!

(プルリクもお待ちしております!)

https://github.com/syatyo/FloatingSkeletonTableView