Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

iOS14のUICollectionViewをCustomCellで実装する。

はじめに

前回、iOS14のUICollectionViewを実装する記事を書いたので、ついでにカスタムセルの扱いも変わったらしいしこれも記事に書こう!と思って書いているのですが今までとかなり扱いが変わっているので大変でした。
あってるか不安なのでここ間違ってるよ、こう書いた方がいいよ!とかあったらコメントください :persevere:

カスタムセル登場人物 一言紹介コーナー

UICollectionViewListCellクラス

ただのセルクラスです。
特に何もしません。

UICellConfigurationStateクラス

状態を保持してくれます。
isSelectedisFocusedがデフォルト用意されています。
カスタムして追加も可能です。
(今回は扱いません。)

UIViewクラス

カスタムビューを作ります。

UIContentConfigurationクラス

カスタムビューを初期化し、セルから表示するデータとUICellConfigurationStateを受け取り、カスタムビューに反映させ、セルにビューを返すクラスです。
一番の大切です。4番です。

実装イメージ

前回の記事と違いを出すために星を入れたのが絶妙にださい。。

全コード

全部で6ファイルです。
SimpleCustomCell.storyboard
SimpleCustomCellViewController.swift
SimpleCustomCell.swift
SimpleCustomCellConfiguration.swift
SimpleCustomCellView.xib
SimpleCustomCellView.swift

SimpleCustomCellViewController.swift

SimpleCustomCellViewController.swift
class SimpleCustomCellViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    enum Section: CaseIterable {
        case kanto
        case kyusyu
    }

    let data: [Section: [String]] = [
        .kanto: ["Kanto", "Tokyo", "Chiba"],
        .kyusyu: ["Kyusyu", "Fukuoka", "Miyazaki"]
    ]

    var collectionViewDataSource: UICollectionViewDiffableDataSource<Section, String>!

    override func viewDidLoad() {
        super.viewDidLoad()

        var layoutConfig = UICollectionLayoutListConfiguration(appearance: .grouped)
        layoutConfig.headerMode = UICollectionLayoutListConfiguration.HeaderMode.firstItemInSection

        let layout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
        collectionView.collectionViewLayout = layout

        collectionViewDataSource = createDataSource()
        reloadList()
    }

    func createDataSource() -> UICollectionViewDiffableDataSource<Section, String> {

        let normalCell = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, indexPath, text) in
            var content = cell.defaultContentConfiguration()
            content.text = text
            cell.contentConfiguration = content
        }

        let customCell = UICollectionView.CellRegistration<SimpleCustomCell, String> { (cell, indexPath, text) in
            cell.title = text
        }

        return UICollectionViewDiffableDataSource<Section, String>(collectionView: collectionView) {
            (collectionView, indexPath, text) -> UICollectionViewCell? in

            // セクションタイトルには、通常のセルを使っています。
            if indexPath.row == 0 {
                return collectionView.dequeueConfiguredReusableCell(using: normalCell, for: indexPath, item: text)
            } else {
                return collectionView.dequeueConfiguredReusableCell(using: customCell, for: indexPath, item: text)
            }
        }
    }

    func reloadList(){
        var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
        snapshot.appendSections(Section.allCases)
        snapshot.appendItems(data[.kanto]!, toSection: .kanto)
        snapshot.appendItems(data[.kyusyu]!, toSection: .kyusyu)
        collectionViewDataSource.apply(snapshot)
    }
}

かいせつ

特別なのはここぐらいです。
UICollectionViewListCellを設定するときは、defaultContentConfigurationメソッドを呼んでcontentを設定していましたが、その処理はSimpleCustomCellに移動しているためここではtitleを渡すだけになっています。

let customCell = UICollectionView.CellRegistration<SimpleCustomCell, String> { (cell, indexPath, text) in
    cell.title = text
}

SimpleCustomCell.swift

SimpleCustomCell.swift
class SimpleCustomCell: UICollectionViewListCell {

    var title: String!

    override func updateConfiguration(using state: UICellConfigurationState) {
        var newConfiguration = SimpleCustomCellConfiguration()
        newConfiguration.title = title
        contentConfiguration = newConfiguration
    }
}

かいせつ

updateConfigurationは、collectionViewにデータがapplyされたときとsetNeedsUpdateConfigurationメソッドが呼ばれレイアウトの更新が必要になった時に呼ばれるメソッドです。
ここでは、Configurationを初期化し、データ(title)をつめてセル自身のcontentConfigurationに設定しています。
(引数のstateは今回状態を扱わないので未使用です。)

SimpleCustomCellConfiguration.swift

SimpleCustomCellConfiguration.swift
struct SimpleCustomCellConfiguration: UIContentConfiguration, Hashable {

    var title: String?

    func makeContentView() -> UIView & UIContentView {
        return SimpleCustomCellView(configuration: self)
    }

    func updated(for state: UIConfigurationState) -> Self {
        return self
    }
}

かいせつ

makeContentViewメソッドは、セルに表示するビューを生成するメソッドです。
引数で自身をビューに渡しています。
updatedメソッドは状態が変わったら呼ばれるメソッドです。未使用です。

SimpleCustomCellView.xib(.swift)

SimpleCustomCellView.swift
class SimpleCustomCellView: UIView, UIContentView {

    @IBOutlet var nameLabel: UILabel!

    var configuration: UIContentConfiguration

    init(configuration: SimpleCustomCellConfiguration) {
        self.configuration = configuration
        super.init(frame: .zero)
        loadNib()
        setUpUI()
    }

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

    private func loadNib() {
        let nib = UINib(nibName: "\(SimpleCustomCellView.self)", bundle: nil)
        guard let view = nib.instantiate(withOwner: self, options: nil).first as? UIView else { return }
        view.frame = frame
        addSubview(view)
    }

    private func setUpUI() {
        guard let configuration = configuration as? SimpleCustomCellConfiguration else { return }
        nameLabel.text = configuration.title
    }
}

かいせつ

xibをloadし、渡されたconfigurationのデータをLabelに設定しています。
特に解説するところはないですん。

まとめ

Configurationを使用することによって軽量になり、再利用が簡単になるようです。
今までのDelegate、DataSourceを使った方法に比べたら、どこでどのメソッドが呼ばれるかわからずブラックボックス感が増えたなと感じてしまいました。
慣れたら気にならなくなるのでしょうか。。

次は今回使わなかったstateを使用して記事を書こうと思います。
はげみになるのでよかったらLGTMお願いします\(^o^)/

gitはこちら!
https://github.com/ymarui/iOS14_UICollectionView_sample

参考文献

https://qiita.com/shiz/items/4227accc7d13ae439d1d#推奨される使用方法
https://swiftsenpai.com/development/collectionview-expandable-list-part2/

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
4
Help us understand the problem. What are the problem?