Xcode11の新機能でUIScrollViewのAutoLayoutを攻略
久しぶりにUIScrollViewダンジョンに入ったら攻略できなかったので攻略法の備忘録。
背景
たくさんの人が利用しているだろうSnapKit
をうちでも使用していて、ScrollView
にAutoLayoutかけようと思ったら難しかったです。
ポップアップのようなビューを表示していたんですが、SE端末だけ下のタブバー上のナビバーの間の表示領域を飛び出してしまうので、上限がきたらスクロールに切り替えるという実装をすることになりました。
満たしたい条件
- SnapKit を使用(以下ちょいちょいこれ使用の前提で話します)
- ボタンやらラベルやら詰め込まれているViewを使用(スクロールさせたいContentView)
- Viewの指定
- width最大値指定あり(iPad様の限界値を作るため)
- height最大値指定あり(限界まできたらマージン分で止める)
- ContentViewのheightは内部パーツで決まる可変
- 基本中央表示
- height限界値に到達した場合スクロールさせる、かつContentViewのtopはScrollViewのtopに合わせる
色々条件がありますがXcode11からScrollViewの仕様が少し変わったこともあり、基本形と条件指定形をまとめやり方を定着させたい。参考になる方がいれば。
ScrollViewに制約をかけるときの基本
Xcode11からScrollViewは
- FrameLayoutGuide
- ContentLayoutGuide
というのを持つようです。
登場人物
- ScrollView = ScrollView自体、ContentViewを覗くフレームに直結
- FrameLayoutGuide = 上記ScrollViewフレームのガイド
- ContentLayoutGuide = ContentViewを合わせるガイド
- ContentView = スクロールさせたいView
基本的にはこの子たちに制約かけていく形になります。
ざっと、ScrollView
FrameLayoutGuide
で窓👓ContentView
ContentLayoutGuide
で中身🌃を窓👓にどう表示したいか
を決める感じかと思います。
ContentView.frame.height
などが可変で設定したScrollViewの高さ未満の場合、スクロールされません。
いきなり最初にあげたミッション達成条件を満たすのは難易度が高いので、順番に下の階層からダンジョンを上がっていく。
SnapKit
の使用前提です。
1階層
ミッション達成条件
⭐️ 単一方向スクロール(縦or横スクロール)
⭐️ ScrollViewは全画面
でミッションコンプリートです。簡単です。
ちなみに横スクロールしたい場合はmake.height.equalTo(scrollView.contentLayoutGuide)
で高さを固定すればおkです。
両方固定するとスクロールしないので注意です。
備考
低難易度のうちにどうなっているか整理したい、うやむやの中先に進むと崩壊します。
make.edges.equalToSuperView()
は窓👓を作るので、作りたい窓👓の大きさを設定します。
全画面にしたいとか、superView
の下半分にScrollを作りたいとか、ここでsuperView
基準で作ります。
make.width.equalTo(scrollView.frameLayoutGuide)
は、contentView
(表示させたい中身🌃)の横幅をscrollView
(frameLayoutGuide)に収めたいので、中身🌃.width = 窓👓.width
とします。縦幅(height)は設定していないので垂れ流し = frameから外れる = 外れた分がスクロール対象領域
という認識です。
make.edges.equalTo(scrollView.contentLayoutGuide)
は、個人的には立ち位置の理解が少し難しかったのですが、中身🌃をScrollViewに対してどう表示させたいかを担う部分ということで今は落ち着いています。
という部分を頭に入れた上で階層を上がっていく。
2階層
ミッション達成条件
⭐️ 単一方向スクロール(縦or横スクロール)
⭐️ ScrollViewの大きさは指定条件でレイアウト
⭐️ ScrollViewはsuperViewの下半分、左右下に指定のマージン
基準はそれぞれですが、scrollView
のtopをsuperView
の高さ/2の数値にイコールとすることでsuperView
の下半分の領域をScrollView
として確保します。
この様に窓👓自体のレイアウト指定がある場合はScrollView
に制約をかけてレイアウトを決めます。
次から少し🤔となり始める、というか、Autolayout
の正しい知識がないと討伐が大変になる。
3階層
ミッション達成条件
⭐️ 単一方向スクロール(縦or横スクロール)
⭐️ ScrollViewの大きさは指定条件でレイアウト
⭐️ ScrollViewは左右に指定のマージン
⭐️ ContentViewの高さ表示限界以内なら中央表示(超えたらスクロール)
補足しますが、この条件が意図しているところは
- 上下は決まったマージンをとらず、遊ばせる(画面サイズまたはsuperViewによって可変)
ということなので、superView
に収まる範囲ならスクロールさせる必要はありません。
ですが例えばsuperView
の高さが小さい時など、限界突破してcontentView
の高さがsuperView
の縦幅をぶち抜いてしまった時にはスクロールを適用して差し上げます。
scrollView
make.left.right.equalToSuperView().inset(10)
make.center.equalToSuperview()
窓👓の左右マージンを固定で設置、中央にくる様に設定。
make.height.lessThanOrEqualToSuperview()
窓👓の縦幅最大値をsuperView
にして限界突破しないようにする。
contentView
make.width.equalTo(scrollView.frameLayoutGuide)
お馴染み縦スクロール設定。
限界前は中央表示、限界きたらスクロールという設定は以下でバランスとって設定しないといけない。
ただ中央表示するだけではなく、限界にきたら中央表示ではなくcontentView
のtopは上揃えというふうに
表示条件を分けなければいけない。
make.left.right.bottom.equalTo(scrollView.contentLayoutGuide)
top以外の三方向はcontentLayoutGuide
に揃える。topは条件で分けるので指定しない。
make.top.bottom.greaterThanOrEqualTo(scrollView.contentLayoutGuide)
限界にきたらtop bottomをcontentLayoutGuide
に合わせる。
make.center.lessThanOrEqualToSuperview().priority(.required - 1)
中央表示はcontentView
がsuperView
に到達するまでに設定。優先度は下げておく。
(この辺自信がないので指摘頂けると嬉しい)
4階層
ミッション達成条件
⭐️ 単一方向スクロール(縦or横スクロール)
⭐️ ScrollViewの大きさは指定条件でレイアウト
⭐️ ScrollViewは左右に指定のマージン
⭐️ ContentViewの高さ表示限界以内なら中央表示(超えたらスクロール)
⭐️ ScrollViewの高さ表示限界に指定のマージンをいれる
ラストダンジョンは最初にやりたかった条件達成でコンプリート。
3階層が終わればここはマージン指定するだけなので難しくない。
make.height.lessThanOrEqualToSuperview().inset(5)
scrollView
の高さ最大値はsuperview
に設定していたのでインセットをかける。
これで目標条件達成できました。
上記のように、端末サイズ指定や条件分岐を入れなくてもlessThanOrEqualTo
やgreaterThanOrEqualTo
、priority
を使用することで調整できる。
という認識。
終わりに
Scrollダンジョン難しい。
だがFrameLayoutGuide
ContentLayoutGuide
というガイドがあるのでレイアウトを作りやすくなりました。
あとSnapKitとAutolayoutの勉強になりました。
お疲れっした!
アプリリリースしました!
持ち歌とそのキーを登録できるアプリです!
自分のキーをよく忘れる方は使ってみてください!
ディスカッション
コメント一覧
まだ、コメントがありません