なりゆき
チーム開発をしていて、レイアウトに関する考え方がバラバラだったのでみんなの意識を統一するために一定の方針を考えました。
ちなみにここで言っているレイアウトはデザイン的なことではなく、実際に開発する際にどうやって画面を作るかという意味でのレイアウトになります。
ちなみにどんな問題が起きてたかというと...
- あるViewの位置を変更したら他のViewもずれた
- ViewControllerに全てのViewに対するレイアウト処理がびっしり書かれていて辛い
方針
画面のレイアウトを考える時に以下の方針に従い設計していきます。
- View同士の階層を考える
- 階層化したViewを一つのカスタムViewとして扱う
- レイアウトを決めてよいのは1階層下のViewまで
1. View同士の階層を考える
『階層化したViewを一つのカスタムViewとして扱う』『レイアウトを決めてよいのは1階層下のViewまで』を考えるにはまず
『View同士の階層を考える』 必要があります。
View同士の階層を考えるためにViewをグルーピングしていきます。
例としてアキネーターのスクリーンショットを使わせてもらっています。
下記の画面を作る時と仮定して書いていきます。
(おじさんと右上のホームアイコンはないものとして説明していきます)
1.1 グルーピング1回目
画面全体のView(おそらくViewControllerのview)がまず大きなグループになります。
黒枠に囲まれた部分です。これを仮にBaseViewとします。
1.2 グルーピング2回目
次に緑色のViewと赤色のViewと青色のViewに大きく分けることができます。
それぞれ以下とします。
- 緑色→SerifView
- 赤色→CharacterTypeView
- 青色→StartView
1.3 グルーピング3回目
緑色のView(SerifView)の中をオレンジ色のViewとピンクのViewに分けることができます。
- オレンジ色→TextView
- ピンク色→ExpView
同じように赤色のView(CharacterTypeView)の中は以下のように分けることができます。
- 紫色→TitleView
- 水色→FamousLabelView
- 黄緑→FamousCheckView
- 黄色→MyWorldLabelView
- 白色→MyWorldCheckView
上記を繰り返してグルーピングできなくなるまで実施します。
例ではキャプチャに自分で色を付けてやっていますが、実際に開発している時はノートに絵を描いてやっています。
2. 階層化したViewを一つのカスタムViewとして扱う
基本的に子を持つViewはカスタムViewにすべきです。
今回の例で言うと、 SerifView と CharacterTypeView はそれぞれUIViewクラスを継承したカスタムクラスを作成します。
こうすることでBaseViewはSerifViewとCharacterTypeViewとStartViewだけを持てば(addSubviewすれば)よくなります。
つまりBaseViewはTextViewやTitleViewを知らなくて良くなります。
// TextViewやTitleViewを持つ必要がない
[self addSubView:serifView];
[self addSubView:characterTypeView];
[self addSubView:startView];
3. レイアウトを決めてよいのは1階層下のViewまで
ダメなこと
- 兄弟が別の兄弟のレイアウトを決める
// 兄弟が別の兄弟のレイアウトを決める
// そもそもStartViewを参照できるのもおかしいけど
self.startView.frame = (0,0,self.view.frame.width,self.view.frame.height);
- 子が親のレイアウトを決める
// 子が親のレイアウトを決める
// そもそも参照できるのもおかしいけど
self.baseView.frame = (0,0,self.view.frame.width,self.view.frame.height);
- 親が孫のレイアウトを決める
// 親が孫のレイアウトを決める
// そもそも参照できないように設計するべき
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);
していいこと
- 親が子のレイアウトを決める
// serifViewはBaseViewの子なのでこれはOK
self.serifView.frame = CGRectMake(0,0,self.view.frame.width,self.view.frame.height);
今回の例で言うとBaseViewはSerifViewとCharacterTypeViewとStartViewのみレイアウトを決めることができます。
方針に従うことによる利点
- 1階層下のViewのみのレイアウトを管理することで、孫のViewを変えたことで親や子のレイアウトが崩れることがなくなる
- 親のコードが完結になる(孫のレイアウトがなくなるので)
まとめ
チーム開発ではViewのレイアウトに関して今回のように階層を意識して設計することで、責務が階層ごとに分割されメンテしやすいコードになりました。