13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-12-14

ちゃちゃっす!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が自分でリサイズとか、細かい詰めるところは全部すっ飛ばしました。

あとがき

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

13
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?