LoginSignup
28
23

More than 3 years have passed since last update.

UICollectionView の使い方(列数を指定してレイアウトする方法)

Last updated at Posted at 2019-05-22

完成イメージ

スクリーンショット 2019-05-21 15.39.43.png

実現したいこと

  • 1行に表示するアイテムを2個(つまり2列)とする。
  • 1つのセルに複数のアイテムを入れる。
  • セクションの左右、セル間の余白は 2.0 とする。

(メモ)cellのクラスを作成する方法もありますが、今回はTagを使った方法で実装しています。

手順

1. StoryboardのView上に Library(Cmd + Shift + L)からCollectionVIew を持ってくる。

SafeAreaに対する制約をつけておく。
スクリーンショット 2019-05-21 10.10.28.png

2. Storyboard上の cellを選択して、Collection Reusable View の Identifier を "Cell" とする。

スクリーンショット 2019-05-21 10.13.38.png

3. cellにLibraryよりImageVIewを2つと、Labelを置き、それぞれに制約をつける。

スクリーンショット 2019-05-21 15.25.42.png

(参考)制約の付け方

「下部の小さいImageView」
スクリーンショット 2019-05-21 15.19.40.png

「ラベル」
スクリーンショット 2019-05-21 15.17.22.png

「下部の小さいImageView」と「ラベル」をStackViewに組み込む
スクリーンショット 2019-05-21 15.17.33.png

「上部の大きいImageView」
スクリーンショット 2019-05-21 15.17.42.png

4. 3.で作った2つのImageViewと、Label それぞれのtagに番号を振る。

大きいImageView ... tag 1
小さいImageView ... tag 2
Label ... tag 3

大きいImageView↓
スクリーンショット 2019-05-21 10.30.17.png

ImageView の ContentMode を AspectFill に変更する。
スクリーンショット 2019-05-21 11.35.58.png

5. ViewController.swift のclassに、UICollectionViewDelegate, UICollectionViewDataSource を追加する。

エラーが出るので、不足しているコードを追記する。

ViewController.swift
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

    private let photos = ["photo0", "photo1", "photo2", "photo3", "photo4", "photo5","photo0", "photo1", "photo2", "photo3", "photo4", "photo5"]
    private let userImages = ["userImage0", "userImage1", "userImage2", "userImage3", "userImage4", "userImage5","userImage0", "userImage1", "userImage2", "userImage3", "userImage4", "userImage5"]
    private let titles = ["パンケーキ", "ラーメン", "サンドウィッチ", "人参とポテト", "プレートランチ", "サーモングリル", "パンケーキ", "ラーメン", "サンドウィッチ", "人参とポテト", "プレートランチ", "サーモングリル"]

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


    // 1つのセクションの中に表示するセル(要素)の数。
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return photos.count
    }

   // セル(要素)に表示する内容
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // "Cell" の部分は Storyboard でつけた cell の identifier。
        let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)

        // Tag番号を使ってインスタンスをつくる
        let photoImageView = cell.contentView.viewWithTag(1)  as! UIImageView
        let photoImage = UIImage(named: photos[indexPath.row])
        photoImageView.image = photoImage

        let userImageView = cell.contentView.viewWithTag(2)  as! UIImageView
        let userImage = UIImage(named: userImages[indexPath.row])
        userImageView.image = userImage

        let titleLabel = cell.contentView.viewWithTag(3) as! UILabel
        titleLabel.text = titles[indexPath.row]

        return cell
    }

6. Storyboard に戻り、CollectionView と View Controller を紐づけする。

CollectionView を選択して cintrol を押しながら、ViewControllerにドラッグする。
dataSource、delegate
とそれぞれ結び紐付けします。

スクリーンショット 2019-05-21 11.07.30.png
スクリーンショット 2019-05-21 11.08.01.png

7. photo0...と、userImage0...用に、適当な画像をAssets.xcassets に保存しておく。

8. 実行

スクリーンショット 2019-05-21 15.33.08.png

とりあえず表示することころまではできたので、レイアウトを整えていく。

9. レイアウトを整える。

ViewController.swift のclassに、UICollectionViewDelegateFlowLayout を追加する。

以下をViewController.swift に追記する。

ViewController.swift

    // レイアウト設定 UIEdgeInsets については下記の参考図を参照。
    private let sectionInsets = UIEdgeInsets(top: 10.0, left: 2.0, bottom: 2.0, right: 2.0)
    // 1行あたりのアイテム数
    private let itemsPerRow: CGFloat = 2


ViewController.swift

    // Screenサイズに応じたセルサイズを返す
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
        let availableWidth = view.frame.width - paddingSpace
        let widthPerItem = availableWidth / itemsPerRow
        return CGSize(width: widthPerItem, height: widthPerItem + 42)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return sectionInsets
    }

    // セルの行間の設定
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 10.0
    }

スクリーンショット 2019-05-21 13.50.14.png
参考図:Apple公式リファレンス Collection View Programming Guide for iOSより

(メモ) 実行してみて、1列の表示なったら下記を確認
collectionView のMin Spacing に値が入っていたら 0 にする。

スクリーンショット 2019-05-21 14.19.45.png

10. セルがタップされたときの処理

ViewController.swift

    // セルが選択されたときの処理
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("\(titles[indexPath.row])がtapされたよ")
    }

11. 完成!

スクリーンショット 2019-05-21 15.39.43.png

おまけ...

3列表示にしてみるとこんな感じに。いろいろ応用できそうです。


    // 1行あたりのアイテム数を3にする
    private let itemsPerRow: CGFloat = 3

スクリーンショット 2019-05-21 15.41.44.png

コード全体

ViewController.swift
import UIKit

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    private let photos = ["photo0", "photo1", "photo2", "photo3", "photo4", "photo5","photo0", "photo1", "photo2", "photo3", "photo4", "photo5"]
    private let userImages = ["userImage0", "userImage1", "userImage2", "userImage3", "userImage4", "userImage5","userImage0", "userImage1", "userImage2", "userImage3", "userImage4", "userImage5"]
    private let titles = ["パンケーキ", "ラーメン", "サンドウィッチ", "人参とポテト", "プレートランチ", "サーモングリル", "パンケーキ", "ラーメン", "サンドウィッチ", "人参とポテト", "プレートランチ", "サーモングリル"]

    // レイアウト設定
    private let sectionInsets = UIEdgeInsets(top: 10.0, left: 2.0, bottom: 2.0, right: 2.0)

    // 1行あたりのアイテム数
    private let itemsPerRow: CGFloat = 2

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


    // 要素の数。
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return photos.count
    }
    // セル(要素)に表示する内容
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // "Cell" の部分は Storyboard でつけた cell の identifier。
        let cell: UICollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)

        // Tag番号を使ってImageViewののインスタンスをつくる
        let photoImageView = cell.contentView.viewWithTag(1)  as! UIImageView
        let photoImage = UIImage(named: photos[indexPath.row])
        photoImageView.image = photoImage


        let userImageView = cell.contentView.viewWithTag(2)  as! UIImageView
        let userImage = UIImage(named: userImages[indexPath.row])
        userImageView.image = userImage

        let titleLabel = cell.contentView.viewWithTag(3) as! UILabel
        titleLabel.text = titles[indexPath.row]

        return cell

    }

    // Screenサイズに応じたセルサイズを返す
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
        let availableWidth = view.frame.width - paddingSpace
        let widthPerItem = availableWidth / itemsPerRow
        return CGSize(width: widthPerItem , height: widthPerItem + 42)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return sectionInsets
    }
    // セルの行間の設定
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 10.0
    }

    // セルが選択されたときの処理
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("\(titles[indexPath.row])がtapされたよ")
    }

}

(参考)

28
23
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
28
23