はじめに
前回、iOS14のUICollectionViewを実装する記事を書いたので、ついでにカスタムセルの扱いも変わったらしいしこれも記事に書こう!と思って書いているのですが今までとかなり扱いが変わっているので大変でした。
あってるか不安なのでここ間違ってるよ、こう書いた方がいいよ!とかあったらコメントください
カスタムセル登場人物 一言紹介コーナー
UICollectionViewListCellクラス
ただのセルクラスです。
特に何もしません。
UICellConfigurationStateクラス
状態を保持してくれます。
isSelected
、isFocused
がデフォルト用意されています。
カスタムして追加も可能です。
(今回は扱いません。)
UIViewクラス
カスタムビューを作ります。
UIContentConfigurationクラス
カスタムビューを初期化し、セルから表示するデータとUICellConfigurationState
を受け取り、カスタムビューに反映させ、セルにビューを返すクラスです。
一番の大切です。4番です。
実装イメージ
前回の記事と違いを出すために星を入れたのが絶妙にださい。。
全コード
全部で6ファイルです。
SimpleCustomCell.storyboard
SimpleCustomCellViewController.swift
SimpleCustomCell.swift
SimpleCustomCellConfiguration.swift
SimpleCustomCellView.xib
SimpleCustomCellView.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
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
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)
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/