11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

iOS14のUICollectionViewをCustomCellで実装する。

Last updated at Posted at 2020-10-08

はじめに

前回、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/

11
7
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
11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?