LoginSignup
36
33

More than 5 years have passed since last update.

iOS開発でのViewのレイアウトの考え方

Last updated at Posted at 2015-06-21

なりゆき

チーム開発をしていて、レイアウトに関する考え方がバラバラだったのでみんなの意識を統一するために一定の方針を考えました。
ちなみにここで言っているレイアウトはデザイン的なことではなく、実際に開発する際にどうやって画面を作るかという意味でのレイアウトになります。

ちなみにどんな問題が起きてたかというと...

  • あるViewの位置を変更したら他のViewもずれた
  • ViewControllerに全てのViewに対するレイアウト処理がびっしり書かれていて辛い

方針

画面のレイアウトを考える時に以下の方針に従い設計していきます。

  1. View同士の階層を考える
  2. 階層化したViewを一つのカスタムViewとして扱う
  3. レイアウトを決めてよいのは1階層下のViewまで

1. View同士の階層を考える

『階層化したViewを一つのカスタムViewとして扱う』『レイアウトを決めてよいのは1階層下のViewまで』を考えるにはまず
『View同士の階層を考える』 必要があります。

View同士の階層を考えるためにViewをグルーピングしていきます。

例としてアキネーターのスクリーンショットを使わせてもらっています。

下記の画面を作る時と仮定して書いていきます。
(おじさんと右上のホームアイコンはないものとして説明していきます)
akinator_example01.png

1.1 グルーピング1回目

画面全体のView(おそらくViewControllerのview)がまず大きなグループになります。
黒枠に囲まれた部分です。これを仮にBaseViewとします。
akinator_example02.png

1.2 グルーピング2回目

次に緑色のViewと赤色のViewと青色のViewに大きく分けることができます。
それぞれ以下とします。

  • 緑色→SerifView
  • 赤色→CharacterTypeView
  • 青色→StartView

akinator_example03.png

この時点で以下のような階層があると考えられます。
hierarchy01.png

1.3 グルーピング3回目

緑色のView(SerifView)の中をオレンジ色のViewとピンクのViewに分けることができます。

  • オレンジ色→TextView
  • ピンク色→ExpView

同じように赤色のView(CharacterTypeView)の中は以下のように分けることができます。

  • 紫色→TitleView
  • 水色→FamousLabelView
  • 黄緑→FamousCheckView
  • 黄色→MyWorldLabelView
  • 白色→MyWorldCheckView

akinator_example04.png

この時点で親子関係は以下のようになります。
hierarchy02.png

上記を繰り返してグルーピングできなくなるまで実施します。
例ではキャプチャに自分で色を付けてやっていますが、実際に開発している時はノートに絵を描いてやっています。

こんな感じで。
note.JPG

2. 階層化したViewを一つのカスタムViewとして扱う

基本的に子を持つViewはカスタムViewにすべきです。

今回の例で言うと、 SerifViewCharacterTypeView はそれぞれUIViewクラスを継承したカスタムクラスを作成します。
こうすることでBaseViewはSerifViewとCharacterTypeViewとStartViewだけを持てば(addSubviewすれば)よくなります。
つまりBaseViewはTextViewやTitleViewを知らなくて良くなります。

BaseView.m
// TextViewやTitleViewを持つ必要がない
[self addSubView:serifView];
[self addSubView:characterTypeView];
[self addSubView:startView];

BaseViewが見える階層は以下
hierarchy01.png

3. レイアウトを決めてよいのは1階層下のViewまで

ダメなこと

  • 兄弟が別の兄弟のレイアウトを決める

brother_ng.png

SerifView.m
// 兄弟が別の兄弟のレイアウトを決める
// そもそもStartViewを参照できるのもおかしいけど
self.startView.frame = (0,0,self.view.frame.width,self.view.frame.height);
  • 子が親のレイアウトを決める

childToParent.png

CharacterTypeView.m
// 子が親のレイアウトを決める
// そもそも参照できるのもおかしいけど
self.baseView.frame = (0,0,self.view.frame.width,self.view.frame.height);
  • 親が孫のレイアウトを決める

skipChild.png

BaseView.m
// 親が孫のレイアウトを決める
// そもそも参照できないように設計するべき
self.textView.frame = (0,0,self.view.frame.width,self.view.frame.height);
// とか
self.serifView.textView.frame = (0,0,self.view.frame.width,self.view.frame.height);

していいこと

  • 親が子のレイアウトを決める
BaseView.m
// serifViewはBaseViewの子なのでこれはOK
self.serifView.frame = CGRectMake(0,0,self.view.frame.width,self.view.frame.height);

今回の例で言うとBaseViewはSerifViewとCharacterTypeViewとStartViewのみレイアウトを決めることができます。

下の図はBaseViewから見たレイアウトの可否の図です
hierarchy03.png

SerifViewから見たレイアウトの可否の図は以下
hierarchy04.png

方針に従うことによる利点

  • 1階層下のViewのみのレイアウトを管理することで、孫のViewを変えたことで親や子のレイアウトが崩れることがなくなる
  • 親のコードが完結になる(孫のレイアウトがなくなるので)

まとめ

チーム開発ではViewのレイアウトに関して今回のように階層を意識して設計することで、責務が階層ごとに分割されメンテしやすいコードになりました。

36
33
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
36
33