Help us understand the problem. What is going on with this article?

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

まえがき

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

アドカレといえば去年のアドカレではこんな記事を書きました。
iOSアプリでよく見るチュートリアル画面を作成する

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

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

本編

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

ViewController.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

// 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に処理書きすぎ

    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 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 に準拠させている

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 のカスタムクラスを作りましょう。

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 を移動させます。

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
    }

}

UICollectionViewTutorialCollectionView に変更

    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
    }

cellForItemAtCell の設定をしている

    // 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
    }

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

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)ページ目"
    }

}
    // 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は使わないようにしましょう。

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! もできるだけ使わないほうが安全です。

    // 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 に変更しましょう。

import UIKit

class TutorialCollectionViewCell: UICollectionViewCell {

    ・・・

}
    // 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がハードコーディングになっているので、定数にしましょう。

import UIKit

class TutorialCollectionViewCell: UICollectionViewCell {

    public static let identifier = "TutorialCollectionViewCell"
    var textLabel = UILabel()

    ・・・
}
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
    }
}
    // 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 でリサイズしていますね。

この書き方は冗長に思えます。

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 で初期化すればリサイズをする必要場なくなります。

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
    }

}

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

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

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

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

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした