UIScrollViewのAutolayoutをコードオンリーで実装してみた

# はじめに
どうも@kaneko77です。
今回はコードオンリーでUIを実装するとき
割と苦労する**UIScrollViewのAutolayout**を共有していきたいと思います。
ちゃんと制約を組まないとスクロールできないとかありますよね。
今回の記事を見てなんとなーく分かるようになると思います。

# コード
まずはコードを貼り付けます。
コピペすれば動きます。

```swift

import UIKit

class TestViewController: UIViewController {

    var mainView: TestView!

    override func loadView() {
        super.loadView()
        self.mainView = TestView(frame: .zero)
        self.view = self.mainView
    }

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

}

class TestView: UIView {

    let scroll = UIScrollView()
    let outline = UIView()

    let testButton: UIButton = {
        let button = UIButton()
        button.backgroundColor = .red
        button.setTitleColor(.white, for: .normal)
        button.setTitle("テストテストテスト", for: .normal)
        return button
    }()
    let testText: UILabel = {
       let text = UILabel()
        text.text = "隣の柿はよく柿食う客だ隣の柿はよく柿食う客だ隣の柿はよく柿食う客だ隣の柿はよく柿食う客だ"
        text.textColor = .white
        text.numberOfLines = 0
        return text
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = .systemGray
        setComponent()
        scrollConstraint()
        setConstraint()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setComponent(){
        [scroll, outline, testButton, testText].forEach{
            $0.translatesAutoresizingMaskIntoConstraints = false
        }
        addSubview(scroll)
        scroll.addSubview(outline)
        [testButton, testText].forEach{
            outline.addSubview($0)
        }
    }

    private func scrollConstraint(){
        NSLayoutConstraint.activate([
            scroll.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor),
            scroll.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor),
            scroll.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor),
            scroll.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor),

            outline.topAnchor.constraint(equalTo: self.scroll.topAnchor),
            outline.bottomAnchor.constraint(equalTo: self.scroll.bottomAnchor),
            outline.widthAnchor.constraint(equalTo: self.scroll.widthAnchor),
            outline.leadingAnchor.constraint(equalTo: self.scroll.leadingAnchor),
            outline.trailingAnchor.constraint(equalTo: self.scroll.trailingAnchor),
        ])
    }

    private func setConstraint(){
        NSLayoutConstraint.activate([
            testButton.topAnchor.constraint(equalTo: self.outline.topAnchor, constant: 50),
            testButton.leadingAnchor.constraint(equalTo: self.outline.leadingAnchor, constant: 20),
            testButton.trailingAnchor.constraint(equalTo: self.outline.trailingAnchor, constant: -20),
            testButton.heightAnchor.constraint(equalToConstant: 40),

            testText.topAnchor.constraint(equalTo: self.testButton.bottomAnchor, constant: 400),
            testText.bottomAnchor.constraint(equalTo: self.outline.bottomAnchor, constant: -50),
            testText.leadingAnchor.constraint(equalTo: self.outline.leadingAnchor, constant: 20),
            testText.trailingAnchor.constraint(equalTo: self.outline.trailingAnchor, constant: -20),
        ])
    }

}
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397376/22dcb556-ad09-299e-f648-c621d6fa4d75.gif" width="300px">

# 説明

今回のメインディッシュはこちらですね。
　まず`UIScrollView(変数名はscroll)`は全方位に制約をかけます。
　`UIView(変数名はoutline)`は同じく全方位に制約をかけます。
今回は仮の縦画面のみスクロール画面を作ると定義してプラスして、横幅を固定値にします。

```swift
private func scrollConstraint(){
    NSLayoutConstraint.activate([
        scroll.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor),
        scroll.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor),
        scroll.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor),
        scroll.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor),
        
        outline.topAnchor.constraint(equalTo: self.scroll.topAnchor),
        outline.bottomAnchor.constraint(equalTo: self.scroll.bottomAnchor),
        outline.widthAnchor.constraint(equalTo: self.scroll.widthAnchor),
        outline.leadingAnchor.constraint(equalTo: self.scroll.leadingAnchor),
        outline.trailingAnchor.constraint(equalTo: self.scroll.trailingAnchor),
    ])
}

```

**ヒエラルキー**上だとこういう風になっています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397376/a15852de-0bbc-fea8-d263-16da6c01c1e9.png" width="700px">
ViewControllerのViewを消したバージョンです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397376/73becc66-c53d-43c9-4a31-ffe0a9642c1e.png" width="700px">

# 終わりに
そんな共有することはなかったなと思いますが、
しかしコードオンリーで実装すると
インターフェースビルダーで実装しているより
想像しずらく難しいなと感じる方もいらっしゃると思います。
今回のコードをコピぺしてコンテンツ中をいじる倒すのも良いと思います。
横もスクロールしたい場合は`outline`の横幅の固定値(以下コード)を消すことで実現できます。

```swift
outline.widthAnchor.constraint(equalTo: self.scroll.widthAnchor)
```

ここまで読んでいただきありがとうございます。
皆さんのお役に少しでも立てればなと思います。