去年の自分にマサカリを投げる

# まえがき
こんにちはrenです。
世間は12月。すっかりアドカレの季節ですね。

アドカレといえば去年のアドカレではこんな記事を書きました。
[iOSアプリでよく見るチュートリアル画面を作成する](https://qiita.com/renchild8/items/8fd870d5bfed0f0b360b)

え、なにこれ。
読みづらすぎる。

ということで、去年の自分にマサカリを投げることにしました。

# 本編
まず、前回のコードを見返してみましょう。

## ViewController.swift

```Swift
// ViewController.swift
import UIKit

class ViewController: UIViewController{
    
    var tutorialCollectionView: UICollectionView!
    var layout: UICollectionViewFlowLayout!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height
        
        let collectionViewFrame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight)
        
        // CollectionViewのレイアウトを生成
        layout = UICollectionViewFlowLayout()
        
        // Cell一つ一つの大きさを設定
        layout.itemSize = CGSize(width: viewWidth, height: viewHeight)
        
        // Cellの行間隔を設定
        layout.minimumLineSpacing = 0
        
        // Cellの列間隔を設定
        layout.minimumInteritemSpacing = 0
        
        // CollectionViewのスクロールの方向を横にする
        layout.scrollDirection = .horizontal
        
        // CollectionViewを生成
        tutorialCollectionView = UICollectionView(frame: collectionViewFrame, collectionViewLayout: layout)
        
        // Cellに使われるクラスを登録
        tutorialCollectionView.register(CustomUICollectionViewCell.self, forCellWithReuseIdentifier: "CustomCell")
        
        // dataSourceを自身に設定
        tutorialCollectionView.dataSource = self
        
        // ページングさせる
        tutorialCollectionView.isPagingEnabled = true
        
        // ScrollIndicatorを非表示にする
        tutorialCollectionView.showsHorizontalScrollIndicator = false
        
        // CollectionViewをViewに追加する
        self.view.addSubview(tutorialCollectionView)
        
    }
    
    override func viewDidAppear(_ animated: Bool) {
        // ViewDidLoadではSafeAreaが取得できないのでここでリサイズ
        let safeArea = self.view.safeAreaInsets
        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height
        let collectionViewFrame = CGRect (x: safeArea.left, y: safeArea.top, width: viewWidth - safeArea.left, height: viewHeight - safeArea.top - safeArea.bottom)
        
        layout.itemSize = CGSize(width: viewWidth - safeArea.left, height: viewHeight - safeArea.top - safeArea.bottom)
        
        tutorialCollectionView.frame = collectionViewFrame
        
    }
    
}

extension ViewController: UICollectionViewDataSource {
    
    // Cellの数を設定
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    
    // Cellに値を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell : CustomUICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath as IndexPath) as! CustomUICollectionViewCell
        
        // Cellに応じてbackgroundColorを変更
        switch indexPath.row {
        case 0:
            cell.backgroundColor = UIColor.blue
        case 1:
            cell.backgroundColor = UIColor.orange
        case 2:
            cell.backgroundColor = UIColor.yellow
        case 3:
            cell.backgroundColor = UIColor.green
        case 4:
            cell.backgroundColor = UIColor.red
        default:
            break
        }
        
        cell.textLabel?.text = "\(indexPath.row + 1)ページ目"
        
        return cell
    }
    
}
```

## CustomUICollectionViewCell.swift

```swift

// CustomUICollectionViewCell
import UIKit

class CustomUICollectionViewCell : UICollectionViewCell{
    
    var textLabel : UILabel?
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // UILabelを生成.
        textLabel = UILabel(frame: CGRect(x:0, y:0, width:frame.width, height:frame.height))
        textLabel?.text = "nil"
        textLabel?.textAlignment = NSTextAlignment.center
        
        // Cellに追加.
        self.contentView.addSubview(textLabel!)
    }
    
}

```

うん。読みづらい。
どんなところが読みづらいのか、説明していきます。

### `viewDidLoad`に処理書きすぎ

```Swift
    override func viewDidLoad() {
        super.viewDidLoad()

        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height

        let collectionViewFrame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight)

        // CollectionViewのレイアウトを生成
        layout = UICollectionViewFlowLayout()

        // Cell一つ一つの大きさを設定
        layout.itemSize = CGSize(width: viewWidth, height: viewHeight)

        // Cellの行間隔を設定
        layout.minimumLineSpacing = 0

        // Cellの列間隔を設定
        layout.minimumInteritemSpacing = 0

        // CollectionViewのスクロールの方向を横にする
        layout.scrollDirection = .horizontal

        // CollectionViewを生成
        tutorialCollectionView = UICollectionView(frame: collectionViewFrame, collectionViewLayout: layout)

        // Cellに使われるクラスを登録
        tutorialCollectionView.register(CustomUICollectionViewCell.self, forCellWithReuseIdentifier: "CustomCell")

        // dataSourceを自身に設定
        tutorialCollectionView.dataSource = self

        // ページングさせる
        tutorialCollectionView.isPagingEnabled = true

        // ScrollIndicatorを非表示にする
        tutorialCollectionView.showsHorizontalScrollIndicator = false

        // CollectionViewをViewに追加する
        self.view.addSubview(tutorialCollectionView)

    }
```

処理を分割してみましょう

```Swift
    override func viewDidLoad() {
        super.viewDidLoad()

        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height

        let collectionViewFrame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight)

        // CollectionViewのレイアウトをセット
        layout = setLayout(width: viewWidth, height: viewHeight)

        // CollectionViewを生成
        tutorialCollectionView = UICollectionView(frame: collectionViewFrame, collectionViewLayout: layout)

        // CollectionViewを設定
        setupTutorialCollectionView()

        // CollectionViewをViewに追加する
        self.view.addSubview(tutorialCollectionView)

    }

    func setLayout(width: CGFloat, height: CGFloat) -> UICollectionViewFlowLayout {
        // CollectionViewのレイアウトを生成
        let layout = UICollectionViewFlowLayout()
        
        // Cell一つ一つの大きさを設定
        layout.itemSize = CGSize(width: width, height: height)
        
        // Cellの行間隔を設定
        layout.minimumLineSpacing = 0
        
        // Cellの列間隔を設定
        layout.minimumInteritemSpacing = 0
        
        // CollectionViewのスクロールの方向を横にする
        layout.scrollDirection = .horizontal
        
        return layout
    }

    func setupTutorialCollectionView() {
        // Cellに使われるクラスを登録
        tutorialCollectionView.register(CustomUICollectionViewCell.self, forCellWithReuseIdentifier: "CustomCell")

        // dataSourceを自身に設定
        tutorialCollectionView.dataSource = self

        // ページングさせる
        tutorialCollectionView.isPagingEnabled = true

        // ScrollIndicatorを非表示にする
        tutorialCollectionView.showsHorizontalScrollIndicator = false
    }
```

だいぶマシにはなりましたね。

### `UICollectionViewDataSource` を直接 `ViewController` に準拠させている
```Swift
extension ViewController: UICollectionViewDataSource {
    
    // Cellの数を設定
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    
    // Cellに値を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell : CustomUICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath as IndexPath) as! CustomUICollectionViewCell
        
        // Cellに応じてbackgroundColorを変更
        switch indexPath.row {
        case 0:
            cell.backgroundColor = UIColor.blue
        case 1:
            cell.backgroundColor = UIColor.orange
        case 2:
            cell.backgroundColor = UIColor.yellow
        case 3:
            cell.backgroundColor = UIColor.green
        case 4:
            cell.backgroundColor = UIColor.red
        default:
            break
        }
        
        cell.textLabel?.text = "\(indexPath.row + 1)ページ目"
        
        return cell
    }
    
}
```
これでは `ViewController` の仕事が増えすぎてしまいます。  
こういう時は `UICollectionView` のカスタムクラスを作りましょう。  

```Swift

import UIKit

class TutorialCollectionView: UICollectionView {
    
    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension TutorialCollectionView: UICollectionViewDataSource {
    // Cellの数を設定
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    
    // Cellに値を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell : CustomUICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath as IndexPath) as! CustomUICollectionViewCell
        
        // Cellに応じてbackgroundColorを変更
        switch indexPath.row {
        case 0:
            cell.backgroundColor = UIColor.blue
        case 1:
            cell.backgroundColor = UIColor.orange
        case 2:
            cell.backgroundColor = UIColor.yellow
        case 3:
            cell.backgroundColor = UIColor.green
        case 4:
            cell.backgroundColor = UIColor.red
        default:
            break
        }
        
        cell.textLabel?.text = "\(indexPath.row + 1)ページ目"
        
        return cell
    }
    
}

```

ここに先程`viewDidLoad` から抜き出した `setupTutorialCollectionView` を移動させます。

```Swift

import UIKit

class TutorialCollectionView: UICollectionView {
    
    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        setup()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setup() {
        // Cellに使われるクラスを登録
        tutorialCollectionView.register(CustomUICollectionViewCell.self, forCellWithReuseIdentifier: "CustomCell")
        
        // dataSourceを自身に設定
        self.dataSource = self
        
        // ページングさせる
        self.isPagingEnabled = true
        
        // ScrollIndicatorを非表示にする
        self.showsHorizontalScrollIndicator = false
        
        // BackgroundColorを白にする。
        self.backgroundColor = .white
    }
}

extension TutorialCollectionView: UICollectionViewDataSource {
    // Cellの数を設定
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    
    // Cellに値を設定する
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell : CustomUICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath as IndexPath) as! CustomUICollectionViewCell
        
        // Cellに応じてbackgroundColorを変更
        switch indexPath.row {
        case 0:
            cell.backgroundColor = UIColor.blue
        case 1:
            cell.backgroundColor = UIColor.orange
        case 2:
            cell.backgroundColor = UIColor.yellow
        case 3:
            cell.backgroundColor = UIColor.green
        case 4:
            cell.backgroundColor = UIColor.red
        default:
            break
        }
        
        cell.textLabel?.text = "\(indexPath.row + 1)ページ目"
        
        return cell
    }
    
}

```

`UICollectionView` を `TutorialCollectionView` に変更

```Swift
    override func viewDidLoad() {
        super.viewDidLoad()

        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height

        let collectionViewFrame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight)

        // CollectionViewのレイアウトをセット
        layout = setLayout(width: viewWidth, height: viewHeight)

        // TutorialCollectionViewを生成
        tutorialCollectionView = TutorialCollectionView(frame: collectionViewFrame, collectionViewLayout: layout)

        // CollectionViewをViewに追加する
        self.view.addSubview(tutorialCollectionView)

    }

    func setLayout(width: CGFloat, height: CGFloat) -> UICollectionViewFlowLayout {
        // CollectionViewのレイアウトを生成
        let layout = UICollectionViewFlowLayout()
        
        // Cell一つ一つの大きさを設定
        layout.itemSize = CGSize(width: width, height: height)
        
        // Cellの行間隔を設定
        layout.minimumLineSpacing = 0
        
        // Cellの列間隔を設定
        layout.minimumInteritemSpacing = 0
        
        // CollectionViewのスクロールの方向を横にする
        layout.scrollDirection = .horizontal
        
        return layout
    }

```

### `cellForItemAt` で `Cell` の設定をしている

```Swift
    // Cellに値を設定する
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell : CustomUICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath as IndexPath) as! CustomUICollectionViewCell
        
        // Cellに応じてbackgroundColorを変更
        switch indexPath.row {
        case 0:
            cell.backgroundColor = UIColor.blue
        case 1:
            cell.backgroundColor = UIColor.orange
        case 2:
            cell.backgroundColor = UIColor.yellow
        case 3:
            cell.backgroundColor = UIColor.green
        case 4:
            cell.backgroundColor = UIColor.red
        default:
            break
        }
        
        cell.textLabel?.text = "\(indexPath.row + 1)ページ目"
        
        return cell
    }
```

せっかくカスタムセルを作っているので、セルの設定もセル側にやってもらったほうが処理がスッキリします。

```swift
import UIKit

class CustomUICollectionViewCell : UICollectionViewCell{
    
    var textLabel : UILabel?
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // UILabelを生成.
        textLabel = UILabel(frame: CGRect(x:0, y:0, width:frame.width, height:frame.height))
        textLabel?.text = "nil"
        textLabel?.textAlignment = NSTextAlignment.center
        
        // Cellに追加.
        self.contentView.addSubview(textLabel!)
    }

    func setCell(indexPath: IndexPath) {
        // Cellに応じてbackgroundColorを変更
        switch indexPath.row {
        case 0:
            self.backgroundColor = UIColor.blue
        case 1:
            self.backgroundColor = UIColor.orange
        case 2:
            self.backgroundColor = UIColor.yellow
        case 3:
            self.backgroundColor = UIColor.green
        case 4:
            self.backgroundColor = UIColor.red
        default:
            break
        }
        
        pageNumberLabel.text = "\(indexPath.row + 1)ページ目"
    }
    
}
```

```Swift
    // Cellに値を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell: CustomUICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath as IndexPath) as! CustomUICollectionViewCell
        
        cell.setCell(indexPath: indexPath)
        
        return cell
    }
```

細かいところですが、必要でないのなら`Optional`は使わないようにしましょう。

```Swift
import UIKit

class CustomUICollectionViewCell : UICollectionViewCell{
    
    var textLabel = UILabel() // Optionalでなくてよい
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        // UILabelを生成.
        textLabel = UILabel(frame: CGRect(x:0, y:0, width:frame.width, height:frame.height))
        textLabel.text = "nil"
        textLabel.textAlignment = NSTextAlignment.center
        
        // Cellに追加.
        self.contentView.addSubview(textLabel)
    }

    func setCell(indexPath: IndexPath) {
        // Cellに応じてbackgroundColorを変更
        switch indexPath.row {
        case 0:
            self.backgroundColor = UIColor.blue
        case 1:
            self.backgroundColor = UIColor.orange
        case 2:
            self.backgroundColor = UIColor.yellow
        case 3:
            self.backgroundColor = UIColor.green
        case 4:
            self.backgroundColor = UIColor.red
        default:
            break
        }
        
        pageNumberLabel.text = "\(indexPath.row + 1)ページ目"
    }
    
}
```

強制キャスト `as!` もできるだけ使わないほうが安全です。

```Swift
    // Cellに値を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        guard let cell: CustomUICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath as IndexPath) as? CustomUICollectionViewCell else { return UICollectionViewCell() }
        
        cell.setCell(indexPath: indexPath)
        
        return cell
    }
```

### `CustomUICollectionViewCell` では役割が分かりづらい
`TutorialCollectionViewCell` に変更しましょう。

```Swift
import UIKit

class TutorialCollectionViewCell: UICollectionViewCell {
    
    ・・・
    
}
```

```Swift
    // Cellに値を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TutorialCollectionViewCell", for: indexPath as IndexPath) as? TutorialCollectionViewCell else { return UICollectionViewCell() }
        
        cell.setCell(indexPath: indexPath)
        
        return cell
    }
```

`identifier`がハードコーディングになっているので、定数にしましょう。

```Swift
import UIKit

class TutorialCollectionViewCell: UICollectionViewCell {
    
    public static let identifier = "TutorialCollectionViewCell"
    var textLabel = UILabel()
    
    ・・・
}
```

```Swift
class TutorialCollectionView: UICollectionView {
    
    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        setup()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setup() {
        // Cellに使われるクラスを登録
        tutorialCollectionView.register(TutorialCollectionViewCell.self, forCellWithReuseIdentifier: TutorialCollectionViewCell.identifier)
        
        // dataSourceを自身に設定
        self.dataSource = self
        
        // ページングさせる
        self.isPagingEnabled = true
        
        // ScrollIndicatorを非表示にする
        self.showsHorizontalScrollIndicator = false
        
        // BackgroundColorを白にする。
        self.backgroundColor = .white
    }
}
```

```Swift
    // Cellに値を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TutorialCollectionViewCell.identifier, for: indexPath as IndexPath) as? TutorialCollectionViewCell else { return UICollectionViewCell() }
        
        cell.setCell(indexPath: indexPath)
        
        return cell
    }
```


### `viewDidAppear` でリサイズしている
現在の`ViewController` を確認します。  
`viewDidLoad`で初期化したものを `viewDidAppear` でリサイズしていますね。  
この書き方は冗長に思えます。  

```Swift
import UIKit

class ViewController: UIViewController {
    
    var tutorialCollectionView: UICollectionView!
    var layout: UICollectionViewFlowLayout!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height

        let collectionViewFrame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight)

        // CollectionViewのレイアウトをセット
        layout = setLayout(width: viewWidth, height: viewHeight)

        // TutorialCollectionViewを生成
        tutorialCollectionView = TutorialCollectionView(frame: collectionViewFrame, collectionViewLayout: layout)

        // CollectionViewをViewに追加する
        self.view.addSubview(tutorialCollectionView)

    }

    override func viewDidAppear(_ animated: Bool) {
        // ViewDidLoadではSafeAreaが取得できないのでここでリサイズ
        let safeArea = self.view.safeAreaInsets
        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height
        let collectionViewFrame = CGRect (x: safeArea.left, y: safeArea.top, width: viewWidth - safeArea.left, height: viewHeight - safeArea.top - safeArea.bottom)
        
        layout.itemSize = CGSize(width: viewWidth - safeArea.left, height: viewHeight - safeArea.top - safeArea.bottom)
        
        tutorialCollectionView.frame = collectionViewFrame
        
    }

    func setLayout(width: CGFloat, height: CGFloat) -> UICollectionViewFlowLayout {
        // CollectionViewのレイアウトを生成
        let layout = UICollectionViewFlowLayout()
        
        // Cell一つ一つの大きさを設定
        layout.itemSize = CGSize(width: width, height: height)
        
        // Cellの行間隔を設定
        layout.minimumLineSpacing = 0
        
        // Cellの列間隔を設定
        layout.minimumInteritemSpacing = 0
        
        // CollectionViewのスクロールの方向を横にする
        layout.scrollDirection = .horizontal
        
        return layout
    }
    
}
```

`viewWillLayoutSubviews` で初期化すればリサイズをする必要場なくなります。

```Swift
import UIKit

class ViewController: UIViewController {

    var tutorialCollectionView: UICollectionView!
    var layout: UICollectionViewFlowLayout!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillLayoutSubviews() {

        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height

        let collectionViewFrame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight)

        // CollectionViewのレイアウトをセット
        layout = setLayout(width: viewWidth, height: viewHeight)

        // TutorialCollectionViewを生成
        tutorialCollectionView = TutorialCollectionView(frame: collectionViewFrame, collectionViewLayout: layout)

        // CollectionViewをViewに追加する
        self.view.addSubview(tutorialCollectionView)

    }

    func setLayout(width: CGFloat, height: CGFloat) -> UICollectionViewFlowLayout {
        // CollectionViewのレイアウトを生成
        let layout = UICollectionViewFlowLayout()
        
        // Cell一つ一つの大きさを設定
        layout.itemSize = CGSize(width: width, height: height)
        
        // Cellの行間隔を設定
        layout.minimumLineSpacing = 0
        
        // Cellの列間隔を設定
        layout.minimumInteritemSpacing = 0
        
        // CollectionViewのスクロールの方向を横にする
        layout.scrollDirection = .horizontal
        
        return layout
    }
    
}
```

暗黙的アンラップ型 `!` はあまり使わないようにしましょう。

```Swift
import UIKit

class ViewController: UIViewController {

    lazy var tutorialCollectionView = TutorialCollectionView()
    var layout = UICollectionViewFlowLayout()

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillLayoutSubviews() {

        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height

        let collectionViewFrame = CGRect (x: 0, y: 0, width: viewWidth, height: viewHeight)

        // CollectionViewのレイアウトをセット
        layout = setLayout(width: viewWidth, height: viewHeight)

        // TutorialCollectionViewを生成
        tutorialCollectionView = TutorialCollectionView(frame: collectionViewFrame, collectionViewLayout: layout)

        // CollectionViewをViewに追加する
        self.view.addSubview(tutorialCollectionView)

    }

    func setLayout(width: CGFloat, height: CGFloat) -> UICollectionViewFlowLayout {
        // CollectionViewのレイアウトを生成
        let layout = UICollectionViewFlowLayout()
        
        // Cell一つ一つの大きさを設定
        layout.itemSize = CGSize(width: width, height: height)
        
        // Cellの行間隔を設定
        layout.minimumLineSpacing = 0
        
        // Cellの列間隔を設定
        layout.minimumInteritemSpacing = 0
        
        // CollectionViewのスクロールの方向を横にする
        layout.scrollDirection = .horizontal
        
        return layout
    }
    
}
```

最後にアクセス修飾子を付けたら完成です。

---

# リファクタ後

## ViewController
```Swift

import UIKit

class ViewController: UIViewController {
    
    private lazy var tutorialCollectionView = TutorialCollectionView()
    private var layout = UICollectionViewFlowLayout()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewWillLayoutSubviews() {
        setTutorialCollectionView()
    }
    
    private func setTutorialCollectionView() {
        let safeArea = self.view.safeAreaInsets
        let viewWidth = self.view.frame.width
        let viewHeight = self.view.frame.height
        
        let collectionViewFrame = CGRect (x: safeArea.left,
                                          y: safeArea.top,
                                          width: viewWidth - safeArea.left - safeArea.right,
                                          height: viewHeight - safeArea.top - safeArea.bottom)
        
        // CollectionViewのレイアウトをセット
        layout = setLayout(width: viewWidth, height: viewHeight)
        
        // CollectionViewを生成
        tutorialCollectionView = TutorialCollectionView(frame: collectionViewFrame, collectionViewLayout: layout)
        
        // CollectionViewをViewに追加する
        self.view.addSubview(tutorialCollectionView)
    }
    
    private func setLayout(width: CGFloat, height: CGFloat) -> UICollectionViewFlowLayout {
        // CollectionViewのレイアウトを生成
        let layout = UICollectionViewFlowLayout()
        
        // Cell一つ一つの大きさを設定
        layout.itemSize = CGSize(width: width, height: height)
        
        // Cellの行間隔を設定
        layout.minimumLineSpacing = 0
        
        // Cellの列間隔を設定
        layout.minimumInteritemSpacing = 0
        
        // CollectionViewのスクロールの方向を横にする
        layout.scrollDirection = .horizontal
        
        return layout
    }
    
}

```

## TutorialCollectionView

```Swift

import UIKit

class TutorialCollectionView: UICollectionView {
    
    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        setup()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setup() {
        // Cellに使われるクラスを登録
        self.register(TutorialCollectionViewCell.self, forCellWithReuseIdentifier: TutorialCollectionViewCell.identifier)
        
        // dataSourceを自身に設定
        self.dataSource = self
        
        // ページングさせる
        self.isPagingEnabled = true
        
        // ScrollIndicatorを非表示にする
        self.showsHorizontalScrollIndicator = false
        
        // BackgroundColorを白にする。
        self.backgroundColor = .white
    }
}

extension TutorialCollectionView: UICollectionViewDataSource {
    // Cellの数を設定
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    
    // Cellに値を設定する
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // Cellを取得
        guard let cell: TutorialCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: TutorialCollectionViewCell.identifier, for: indexPath as IndexPath) as? TutorialCollectionViewCell else { return UICollectionViewCell() }
        // Cellに値を設定する
        cell.setCell(indexPath: indexPath)
        
        return cell
    }
    
}

```

## TutorialCollectionViewCell

```Swift

import UIKit

class TutorialCollectionViewCell: UICollectionViewCell {
    
    public static let identifier = "TutorialCollectionViewCell"
    
    private var pageNumberLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        addLabel()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func addLabel() {
        // UILabelを生成.
        pageNumberLabel = UILabel(frame: CGRect(x:0, y:0, width:frame.width, height:frame.height))
        pageNumberLabel.text = ""
        pageNumberLabel.textAlignment = NSTextAlignment.center
        
        // Cellに追加.
        self.contentView.addSubview(pageNumberLabel)
    }
    
    public func setCell(indexPath: IndexPath) {
        // Cellに応じてbackgroundColorを変更
        switch indexPath.row {
        case 0:
            self.backgroundColor = UIColor.blue
        case 1:
            self.backgroundColor = UIColor.orange
        case 2:
            self.backgroundColor = UIColor.yellow
        case 3:
            self.backgroundColor = UIColor.green
        case 4:
            self.backgroundColor = UIColor.red
        default:
            break
        }
        
        pageNumberLabel.text = "\(indexPath.row + 1)ページ目"
    }
    
}

```

ソースコードはGitHubにあげました
https://github.com/renchild8/TutorialCollectionView