16
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

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

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?