【初心者必見】UITableViewの実践デザイン分析

【初心者必見】UITableViewの実践デザイン分析

Table of Contents

UIKitの基本的な使い方を理解したが、App Storeにあるアプリを実際にどうやって作られているのか分析できない向けの記事です。

App Storeに登録されているアプリには様々なデザインがあります。 今回はUITableViewを使ってどのように実装されるのかをこの記事で説明します。

基本は覚えたけど、どう実装すればいいのか分からない人 にはとても価値ある記事になると思います。

この記事の読み方

  • よくあるレイアウトの確認
  • 分析フェイズ
  • 実装フェイズ

実装するサンプルの動画

解説に使ったサンプル実装の動かしたときの動画になります。

よくあるレイアウト

今回はApp Storeに上がっているいくつか複数のアプリをピックアップして、だいたい共通しているレイアウトなどを模様した架空のレイアウトを用意しました。

最近のアプリのレイアウト例

このレイアウトをUITableViewベースで実装していきたいと思います。 なお無理にUITableViewにこだわらずともUIStackViewやUICollectionViewでも実装は可能ですが、今回はUITableViewベースで話を進めます。

分析フェイズ

いきなり実装に入らず、まずはどのようにテーブルのセルやセクションを分けるのか考えます。

このレイアウトはテーブルで構築することができます。

次のイメージのようにセルやセクションを今回は分けます。

レイアウトをセルやセクションで分割

なぜ Section Header は使わないのか?

UITableViewはデフォルトだとSectionを表示中の間は、Section Headerが画面上部に残ります。
残したい場合は Section Header で実装でいいと思います。

しかし大体は残らないようにして欲しいとデザイナーからの要望は多いので、セルとして実装しています。
またSection Header内にボタンなどがあるので、セルの方が実装がシンプルになる場合もあります。

UITableViewではStyleをGroupだとSection Headerは残らないのですが、不要な余白が生まれるので私はセルで実装することが多いです。

セル数が可変する場合はセクションを分けるといい

今回はセクション内のセル数がデータなどで可変するレイアウトはありませんが、

例えば、最後の「おすすめ」↓ が可変の場合は、ヘッダーセルと商品セルを別セクションにすると、制御がしやすくなります。

可変セル時にセクションを分けた図

実装フェイズ

分析にて必要なセルのレイアウトや制御クラスは分かったと思います。次は分析結果を元に実装していきます。

なお今回の実装コードは量が多いためGitHubにあげてあります。

mothule/uitableview-base-layout

GitHubのコードを落としてXcodeと記事両方を開くことを推奨します。

storyboardでレイアウトに必要なセルを作成する

セルが用意されたStoryboard

こんな感じでセルを用意します。

ファイル構成

レイアウト実現のファイル構成

いくつか抜粋して説明します。

ViewModel.swift

ネットでよくあるRxSwiftを使ったものではありません。純SwiftによるViewModelになります

SectionType.swift

UITableViewのセクション定義になります。Rowの定義は用意していません。Rowの並び順が変わるなど、Section内がより複雑になるのであればあったほうがいいです。

DataSource.swift

UITableViewDataSourceUITableViewDelegate を採用したクラスです。

▼こちらに関しては次の記事で説明しています。
【初心者向け】UITableViewDataSourceを別クラス化する方法とメリット

Viewsフォルダ以下

UITableViewやUICollectionViewのセル制御クラスになります。

▼UITableViewの基本に関して分からない場合はこちらの記事に詳しく説明してあります。
【初心者向け】UITableViewの基本的な使い方を極める【入門】

▼またUICollectionViewに関してはこちらの記事で説明しています。
UITableView内にUICollectionViewでCarousel(カルーセル)を実装する

Item.swift

今回唯一のモデルクラスです。
と言っても今回はUIの話なので、特にロジックはなくただのデータ構造クラスです。

Nibable.swift

UITableViewやUICollectionViewのポイラープレートを収束させたクラスです。

▼詳しくはこちらの記事で説明しています。
StoryboardやXibのロード処理の作りを改善する

ApiClient.swift

API通信クライアントです。しかし今回は実際に通信はしておらず別スレッドで待機後にダミーデータを返すようにしてあります。
またここは力入れてないので、DataレイヤーでありながらItemクラスを直接使ってサボってます。

クラス構成

重要な部分のみ抜粋します。

レイアウト実現のクラス図

ViewController

おなじみ画面制御クラスです。 このクラスが ViewModelDataSource のスコープ管理をしています。

DataSource

ViewController によってスコープ管理されています。
ViewModelは参照用で、スコープ管理はしていません。
しかしViewModelのスコープはDataSourceと同じである保証がViewControllerによってスコープ保証されているので、弱参照にはしていません。

ViewModel

アクション処理とそれに関連するイベント発行をしています。 イベント発行は ViewModelDelegateを介して受け取ることができます。
ViewControllerViewModelDelegateを採用して、イベントを受け取るようにしています。

クラス間のメッセージ通信方法

クラス分離することによりクラス間のメッセージ通信について気になると思うので、まとめました。

ViewController <-> ViewModel

ViewController -> ViewModelViewModelのメソッド
ViewController <- ViewModel は前述したように ViewModelDelegate でメッセージやりとりしています。

DataSource <-> 各Views

DataSource -> Views は 各Viewsのsetupメソッド
DataSource <- Views は 各Viewsが持つクロージャによるコールバックでメッセージやりとりしています。

ViewController <-> DataSource

ViewController -> DataSourceDataSourceのメソッド
ViewController <- DataSourceViewModelを介しています。

DataSource <-> ViewModel

DataSource -> ViewModelViewModelのメソッド
DataSource <- ViewModel はありません。 ViewController 側に集約させています。
こうした理由は DataSourceはUITableViewの制御であって、イベント制御ではないためです。
「セルがタップされた」などユーザー発火イベントはViewControllerの責務になるので、こっちに集めるようにしています。

実は基本の組み合わせ

実はこのブログのUITableViewの記事を読んでいれば、今回の実装はほとんどが簡単な組み合わせになります。

難しいように見えて簡単です。ただ手間なだけですね。

このエントリーをはてなブックマークに追加