Kekeの日記

エンジニア、読書なんでも

FlexBoxと比較しながらUIStackViewマスターになる

f:id:bobchan1915:20180901234655p:plain

f:id:bobchan1915:20180901235551p:plain

1. はじめに

僕はよくCSSを書き、それなりにできるので今回は比較しながら書ければいいなと思います。

今回は以下のようなフォームの一番下にあるテキストとリンクが混じったものをUIStackViewというものを使って実装していきます。

2. UIStackViewとは

iOS9から導入されたレイアウトのために使うクラスです。

UIKitのモジュールであり、Auto LayoutをsubViewの管理を自動で行ってくれます

2.1 対応するCSSのプロパティ

Flexboxが非常にUIStackViewのスタックという考え方に似ています。

display: flex

2.2 メリット

以下のようなメリットがあります。

  • StackViewでコンポーネントを分けることができる
  • subViewのレイアウトを簡単に決められる
  • スタックの中にスタックと重ねることができる
  • 少ない制限で使える
  • サイズクラスをサポートする
  • などなど

2.3 デメリット

  • すでに配置されたサブビュー取り除いてしまう:addArrangedSubviewsとあるが、実際に行っているのはaddSubViewである。
  • 制約を壊すかもしれない: UIStackViewではどれが制約を壊すかを知ることは難しいです。
  • 細かい制約を課すには逆に使いにくいかもしれない。
  • などなど

3. UIStackViewのプロパティ

3.1 Axis

axisはsubViewをどのような軸でレイアウトするかというもの。 水平方向のhorizontalと、垂直方向のverticalがあります。

flex-flow: /* row か column */

に対応します。

3.2 Distribution

distributionaxisで指定した主軸の方向にどのように配置をするかを指定します。 これはcssのjustify-contentで指定できるものです。

Mysteries of Auto Layout, Part 1 - WWDC 2015 - Videos - Apple Developer

から引用すると

f:id:bobchan1915:20180909215636p:plain

のように指定できます。

CSSとの対応表は以下にまとめました。

UIStackView(iOS) Flexbox(CSS)
fill なし
fillEqually space-around
fillProportionally なし。しかし別途flexプロパティが設定された状態
equalSpacing space-between
equalCentering なし。

では、これ以外にはどのようにしたらいいでしょうか。

Flexboxでいう、、、

justify-content: flix-start

これはStackViewの幅を設定しなければ実現することができます。

というのも、同じdistributionをfillにしても幅を変更することによって対応できます。

widthAnchorを指定したり、centerXAnchorを指定すると以下のようになります。

f:id:bobchan1915:20180909082025p:plain

それをleadingAnchorにすると以下のようになります。

f:id:bobchan1915:20180909082115p:plain

基準を変えることで実現できます。

3.3 Spacing

subViewの間での余白を指定します。

CSSで言う所のmarginを指定するようなものでしょうか。

.parent
    .child
         p hoge
    .child
         p fuga
    .child
         p copi

だとすると

.parent {
    display: flex,
    flex-flow: row,
    justify-content: space-between,

    & > div {
        margin: 0px {
             right: 10px,
             left: 10px,
        }
    }
}

のような感じではないでしょうか。

3.4 Alignment

axishorizontalのときはtop, center, bottom, fillを指定することができて、verticalの時はleading,center ,trailing ,fillを指定することができます。

Horizontal

f:id:bobchan1915:20180909215257p:plain

Vertical

f:id:bobchan1915:20180909215302p:plain

これはcssでいうalign-itemのことです。

項目が多いので割愛するが、詳しく知りたい人は参考文献をご覧ください。

3.5 DistributionとAlignmentがどっちがどっちか忘れる

CSSでも「あれ、justify-contentsalign-itemsかどっちを指定すればよかったっけ?」と最初のころはなっていました。

distributionjustify-contents主軸の方向に定めるものです。 行として並べていたら縦にどのように配置するか、列としてして並べていたら横にどのように配置するか**を指定することになります。

alignmentalign-itemsは、主軸方向と垂直にどのように並べるかを決めます。 配置したものを、どこ基準で配置するかを主軸と垂直方向で考えるのです。

4 実装パート

4.1 UIStackViewを使って定義

以下のように定義します。 また、ここでのbuttonはStackView`の上にあるボタンのことです。

private let signupStackView = UIStackView()
self.signupStack.axis = .horizontal
self.signupStack.alignment = .center
self.signupStack.distribution = .fill

self.view.addSubView(signupStackView)
signupStackView.addSubView(self.label)
signupStackView.addSubView(self.button)

self.signupStack.topAnchor.constraint(equalTo: self.button.bottomAnchor, constant: 50.0).isActive = true
self.signupStack.widthAnchor.constraint(equalToConstant: formWidth).isActive = true
self.signupStack.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor).isActive = true

すると以下のようになりました。

f:id:bobchan1915:20180902015200p:plain

まとめ

だんだん制約がきつくなって、テストを書かないと不安になってきました。

またここらへんから、どこを基準に制約を掛けるかもチームなどの運用で大事になってくるのではと感じました。 あくまでも相対的なものなので、

参考文献

公式ドキュメント

UIStackView - UIKit | Apple Developer Documentation

全般的にこれ一つの記事ですべてがわかるもの

qiita.com

Distributionについて詳細に書かれてあるもの

qiita.com

同様にDistributionについてわかりやすい

blog.personal-factory.com

よくあるUIStackViewの間違いを指摘

medium.com

CSSのFlexboxについて

coliss.com