LoginSignup
15
9

More than 3 years have passed since last update.

コードでUIScrollView(とその子どもたち)をAutoLayout配置

Last updated at Posted at 2019-10-16

UIScrollViewのAutoLayout制御はいろいろな人がハマるらしくていくつかの記事が見つかるが、自分が欲しい感じで解説しているものがなかったので書いてみる。

前提

  • コードで書く。ストーリーボードは使わない。

概念的なものの解説

UIScrollViewは重要な長方形を2つ持っている。

  • 画面上で占める領域、俗に言うUIScrollView.frame
  • スクロールすることによって見ることができる領域、俗に言うUIScrollView.contentSize

まあこれらはおなじみの知識である。んで、

UIScrollViewをAutoLayout制御するときは

  • 外側のUIViewと関連つけたときはUIScrollView.frameのほうに結びつく
  • 内側の(UIScrollViewにaddされている)UIViewと関連つけたときはUIScrollView.contentSizeのほうに結びつく

という特殊なルールがある。
また、結構くせものなのが

  • UIScrollView.contentSizeは内側の部品のサイズをもとにして決まる。

ということ。なので内部部品のサイズを先に決めてやる。これは文の説明が難しいのでコードを見て下さい。

以上をまとめてコードで書きました。外、スクロールビュー、内、の3つを、それぞれ50づつ間を開けています。

class ViewController: UIViewController {

    let outView = UIView()
    let scrollView = UIScrollView()
    let inView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        outView.backgroundColor = UIColor.red
        scrollView.backgroundColor = UIColor.orange
        inView.backgroundColor = UIColor.yellow

        scrollView.addSubview(inView)
        outView.addSubview(scrollView)
        view.addSubview(outView)

        outView.translatesAutoresizingMaskIntoConstraints = false
        outView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 50.0).isActive = true
        outView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -50.0).isActive = true
        outView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
        outView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50.0).isActive = true

        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.leftAnchor.constraint(equalTo: outView.leftAnchor, constant: 50.0).isActive = true
        scrollView.rightAnchor.constraint(equalTo: outView.rightAnchor, constant: -50.0).isActive = true
        scrollView.topAnchor.constraint(equalTo: outView.topAnchor, constant: 50.0).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: outView.bottomAnchor, constant: -50.0).isActive = true

        inView.translatesAutoresizingMaskIntoConstraints = false
        inView.widthAnchor.constraint(equalToConstant: 1000.0).isActive = true
        inView.heightAnchor.constraint(equalToConstant: 1000.0).isActive = true
        inView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 50.0).isActive = true
        inView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -50.0).isActive = true
        inView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 50.0).isActive = true
        inView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -50.0).isActive = true
    }
}

Simulator Screen Shot - iPhone 11 - 2019-10-16 at 09.12.55.png

UIScrollViewに外側から制約をつけたときと、内側から制約をつけたときで、挙動が変わっているのがわかるかと思います。inViewにはwidth、heightと上下左右の6個の制約がついているが、これは上で書いたようにinViewのサイズを決定させて、それをもとにscrollViewのcontentSizeを決定させるため。(書く順番は前後しても大丈夫だと思いますが)

また、どうしても内部部品のサイズをUIScrollViewのframeのwidth(or height)に合わせたいときはあるよね、というのもわかる。そういうときは、UIScrollViewの下(奥?)に同サイズのUIViewを敷いてそれのwidth(or height)に合わせる手法をとる。コードは以下のとおり。

class ViewController: UIViewController {

    let outView = UIView()
    let scrollView = UIScrollView()
    let inView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        outView.backgroundColor = UIColor.red
        scrollView.backgroundColor = UIColor.orange
        inView.backgroundColor = UIColor.yellow

        scrollView.addSubview(inView)
        outView.addSubview(scrollView)
        view.addSubview(outView)

        outView.translatesAutoresizingMaskIntoConstraints = false
        outView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 50.0).isActive = true
        outView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -50.0).isActive = true
        outView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
        outView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50.0).isActive = true

        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.leftAnchor.constraint(equalTo: outView.leftAnchor).isActive = true
        scrollView.rightAnchor.constraint(equalTo: outView.rightAnchor).isActive = true
        scrollView.topAnchor.constraint(equalTo: outView.topAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: outView.bottomAnchor).isActive = true

        inView.translatesAutoresizingMaskIntoConstraints = false
        //↓↓↓これが味噌↓↓↓
        inView.widthAnchor.constraint(equalTo: outView.widthAnchor).isActive = true
        inView.heightAnchor.constraint(equalToConstant: 1000.0).isActive = true
        inView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
        inView.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
        inView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        inView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
    }
}

Simulator Screen Shot - iPhone 11 - 2019-10-16 at 09.14.19.png

最後に

簡単な仕様の自分のアプリでしか検証していないコードなので、別のパターンのときには通用しなかったり、ここに書いてある説明が不適切だったりするかもしれません。

15
9
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
15
9